View mode: basic / threaded / horizontal-split · Log in · Help
November 15, 2012
Calls to struct methods and immutable
The following code refuses to compile:

///////////////////////////////////////////////////////
import std.math;

struct Foo
{
      int a;
      int b;

      this(int a, int b)
      {
            this.a = a;
            this.b = b;
      }

      void check()
      {
            real c = (a ^^ 2 + b ^^ 2) ^^ 0.5;
            assert(c < 10);
      }
}


void main()
{
      auto foo = cast(immutable) Foo(3, 4);
      foo.check();
}
///////////////////////////////////////////////////////

... producing an error message: immustruct.d(25): Error: function 
immustruct.Foo.check () is not callable using argument types () immutable

The reason seems pretty evident -- making the instance immutable means that the 
temporary internal variable c in check() can't be (over)written.  At the same 
time, this feels a bit daft -- you're talking about a transient value that is 
never seen outside the function scope.

Is there any way of getting round this constraint so such temporary, transient 
variables are still permitted within methods of an immutable instance?

As a workaround, if I write a function external to Foo, e.g.

void check2(Foo foo)
{
      real c = (foo.a ^^ 2 + foo.b ^^ 2) ^^ 0.5;
      assert(c < 10);
}

... then calling foo.check2() runs without problem.  I'm just curious as to 
whether it can be done within a struct method too.
November 15, 2012
Re: Calls to struct methods and immutable
On Thursday, 15 November 2012 at 13:54:10 UTC, Joseph Rushton 
Wakeling wrote:

Make 'void check()' be 'void check() const'
Thanks
Dan
> The following code refuses to compile:
>
> ///////////////////////////////////////////////////////
> import std.math;
>
> struct Foo
> {
>       int a;
>       int b;
>
>       this(int a, int b)
>       {
>             this.a = a;
>             this.b = b;
>       }
>
>       void check()
>       {
>             real c = (a ^^ 2 + b ^^ 2) ^^ 0.5;
>             assert(c < 10);
>       }
> }
>
>
> void main()
> {
>       auto foo = cast(immutable) Foo(3, 4);
>       foo.check();
> }
> ///////////////////////////////////////////////////////
>
> ... producing an error message: immustruct.d(25): Error: 
> function immustruct.Foo.check () is not callable using argument 
> types () immutable
>
> The reason seems pretty evident -- making the instance 
> immutable means that the temporary internal variable c in 
> check() can't be (over)written.  At the same time, this feels a 
> bit daft -- you're talking about a transient value that is 
> never seen outside the function scope.
>
> Is there any way of getting round this constraint so such 
> temporary, transient variables are still permitted within 
> methods of an immutable instance?
>
> As a workaround, if I write a function external to Foo, e.g.
>
> void check2(Foo foo)
> {
>       real c = (foo.a ^^ 2 + foo.b ^^ 2) ^^ 0.5;
>       assert(c < 10);
> }
>
> ... then calling foo.check2() runs without problem.  I'm just 
> curious as to whether it can be done within a struct method too.
November 15, 2012
Re: Calls to struct methods and immutable
Joseph Rushton Wakeling:

>       auto foo = cast(immutable) Foo(3, 4);

Strive to write D code as much cast-free as possible, because
they are dangerous.


> The reason seems pretty evident -- making the instance 
> immutable means that the temporary internal variable c in 
> check() can't be (over)written.

You are wrong. A version of your code:


import std.math;

struct Foo {
     int a, b;

/*
     this(int a, int b) {
         this.a = a;
         this.b = b;
     }
*/

     void check() const pure nothrow {
         immutable real p = a ^^ 2 + b ^^ 2;
         assert(sqrt(p) < 10);
     }
}

void main() {
     auto foo = immutable(Foo)(3, 4);
     // immutable foo = Foo(3, 4); // simpler alternative
     foo.check();
}


Bye,
bearophile
November 15, 2012
Re: Calls to struct methods and immutable
> /*
>      this(int a, int b) {
>          this.a = a;
>          this.b = b;
>      }
> */
>
>      void check() const pure nothrow {
>          immutable real p = a ^^ 2 + b ^^ 2;
>          assert(sqrt(p) < 10);
>      }
> }

If you keep the constructor, then it's probably better to replace 
check() with an invariant().

Bye,
bearophile
November 15, 2012
Re: Calls to struct methods and immutable
On 11/15/2012 03:06 PM, bearophile wrote:
>       void check() const pure nothrow {
>           immutable real p = a ^^ 2 + b ^^ 2;
>           assert(sqrt(p) < 10);
>       }

Is it appropriate to have 'nothrow' given that the assert could fail?

> Strive to write D code as much cast-free as possible, because
> they are dangerous.

The practical situation I'm dealing with is that the a struct gets built inside 
a function, based on data read from files.  Once the data has been written to 
the struct, it should never be changed again.

i.e.

struct Foo
{
     // contains a few different arrays of data
     void add( /* adds new data points to collection */) { ... }
}

auto makeFoo()
{
    Foo foo;
    foreach( /* lots of data */ )
        foo.add( /* new data point */ );
    return foo;
}

So, in practice, it seems like that function should cast it to immutable as it 
returns -- and this would be safe, no?

By the way, I should add -- I recognize I'm deluging the list with a bunch of 
questions in these last days, and I'm very grateful for the amount of advice you 
and others have been giving.  I hope it's not becoming too overwhelming or annoying!
November 15, 2012
Re: Calls to struct methods and immutable
Joseph Rushton Wakeling:

> Is it appropriate to have 'nothrow' given that the assert could 
> fail?

Failing asserts produce errors, not exceptions. "nothrow" only 
deals with exceptions. And thankfully in this case your D code 
doesn't need to be "appropriate", because D refuses nothrow if 
the function is able to throw.


> So, in practice, it seems like that function should cast it to 
> immutable as it returns -- and this would be safe, no?

Try to avoid casts as much as possible in D, they are a great 
source for bugs. Take a look at assumeUnique, or better make  
your makeFoo pure so its output is assignable to immutable.


> By the way, I should add -- I recognize I'm deluging the list 
> with a bunch of questions in these last days, and I'm very 
> grateful for the amount of advice you and others have been 
> giving.  I hope it's not becoming too overwhelming or annoying!

For me it's not a problem: when I don't have time to answer I 
don't answer :-)

Bye,
bearophile
November 15, 2012
Re: Calls to struct methods and immutable
On 11/15/2012 03:36 PM, bearophile wrote:
> Try to avoid casts as much as possible in D, they are a great source for bugs.
> Take a look at assumeUnique, or better make your makeFoo pure so its output is
> assignable to immutable.

Thanks for the pointer to assumeUnique -- it will be useful, although AFAICS it 
applies only to arrays, not to structs or other entities, no?

Alas, pure isn't an option, because the makeFoo involves some random procedures :-(
November 15, 2012
Re: Calls to struct methods and immutable
Joseph Rushton Wakeling:

> Alas, pure isn't an option, because the makeFoo involves some 
> random procedures :-(

http://d.puremagic.com/issues/show_bug.cgi?id=5249
:-)

(But I have to remove its assignment to Andrei, I think I did 
that by mistake, I didn't know how such things work).

Bye,
bearophile
November 15, 2012
Re: Calls to struct methods and immutable
On 11/15/2012 06:24 AM, Joseph Rushton Wakeling wrote:

> The practical situation I'm dealing with is that the a struct gets built
> inside a function, based on data read from files. Once the data has been
> written to the struct, it should never be changed again.

If you don't want the callers be able to change, then return 
immutable(Foo). Otherwise, if the caller wants to use the object as 
immutable, then the user should declare immutable:

a) Don't allow users to modify:

immutable(Foo) makeFoo()
{
    Foo foo;
    foreach (i; 0..10)
        foo.add( /* new data point */ );
    return foo;
}

b) The user wants to play safe:

auto makeFoo()
{
    Foo foo;
    foreach (i; 0..10)
        foo.add( /* new data point */ );
    return foo;
}

void main()
{
    immutable foo = makeFoo();
}

Both of those compile with dmd 2.060.

> I'm deluging the list with a
> bunch of questions

Please continue to do so. That is what this forum is for. Your questions 
help us all. :)

Ali
November 15, 2012
Re: Calls to struct methods and immutable
On 11/15/2012 06:40 PM, Ali Çehreli wrote:
> b) The user wants to play safe:
>
> auto makeFoo()
> {
>      Foo foo;
>      foreach (i; 0..10)
>          foo.add( /* new data point */ );
>      return foo;
> }
>
> void main()
> {
>      immutable foo = makeFoo();
> }
>
> Both of those compile with dmd 2.060.

Really?  I'm using from-GitHub dmd, and with the above example I get a "cannot 
implicitly convert expression to immutable" error, e.g.:

    Error: cannot implicitly convert expression (testSets(nfRaw,0.1L)) of type 
TestData!(ulong,ulong) to immutable(TestData!(ulong,ulong))
« First   ‹ Prev
1 2 3
Top | Discussion index | About this forum | D home