www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Cannot call mutable method on final struct

reply Christopher Wright <dhasenan gmail.com> writes:
Hey everyone.

I get an error on the following code complaining about the use of 
Box.toString. The message is:
"Error: cannot call mutable method on final struct"

I don't know what that means. I believe it's complaining about const 
stuff and the struct being altered (that is all that makes sense, given 
the message and the fact that it's in a loop), but looking at the 
toString method in std.boxer.Box, it doesn't seem to alter the struct. 
As far as I can tell, the code should be valid.

The workaround is using 'ref Box b' (or 'ref b') in the foreach loop.

---
import std.boxer, std.string, std.stdio;

class C(U...) {
    string value;
    this (U u) {
       value = "";
       foreach (Box b; boxArray(u)) {
          value ~= b.toString;
       }
    }
}

void main () {
    auto c = new C!(int, string, char)(12, "foom", 't');
    writefln(c.value);
}
---

Can anyone here else say whether this is a bug in Box.toString or in 
determining whether a function is mutable?
Sep 09 2007
parent reply Carlos Santander <csantander619 gmail.com> writes:
Christopher Wright escribió:
 Hey everyone.
 
 I get an error on the following code complaining about the use of 
 Box.toString. The message is:
 "Error: cannot call mutable method on final struct"
 
 I don't know what that means. I believe it's complaining about const 
 stuff and the struct being altered (that is all that makes sense, given 
 the message and the fact that it's in a loop), but looking at the 
 toString method in std.boxer.Box, it doesn't seem to alter the struct. 
 As far as I can tell, the code should be valid.
 
 The workaround is using 'ref Box b' (or 'ref b') in the foreach loop.
 
 ---
 import std.boxer, std.string, std.stdio;
 
 class C(U...) {
    string value;
    this (U u) {
       value = "";
       foreach (Box b; boxArray(u)) {
          value ~= b.toString;
       }
    }
 }
 
 void main () {
    auto c = new C!(int, string, char)(12, "foom", 't');
    writefln(c.value);
 }
 ---
 
 Can anyone here else say whether this is a bug in Box.toString or in 
 determining whether a function is mutable?

I don't use D 2.0, but here it goes... string is const(char)[] or const char[] or something like that... It's const, anyway, so you aren't supposed to modify it. Replace "string value" with "char[] value". -- Carlos Santander Bernal
Sep 09 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
Carlos Santander wrote:
 I don't use D 2.0, but here it goes...
 
 string is const(char)[] or const char[] or something like that... It's 
 const, anyway, so you aren't supposed to modify it. Replace "string 
 value" with "char[] value".
 

If I use char[] rather than string, it fails with the error message I stated (and indeed, in the original, I used char[] accidentally). If I just call Box.toString and don't do anything with the returned string, it fails with the same error message. 'string ~= whatever' is valid because the string is not final. The compiler rewrites it as 'string = string ~ whatever'. New minimal test case: --- import std.boxer; void main () { foreach (b; boxArray(9, 'b')) { b.toString; } } ---
Sep 10 2007
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:fc3bdu$8tn$1 digitalmars.com...
 'string ~= whatever' is valid because the string is not final. The 
 compiler rewrites it as 'string = string ~ whatever'.

Iiii wouldn't be so sure about that. I don't know if the compiler rewrites "a ~= b" as "a = a ~ b" when dealing with const stuff, but as far as D1 goes, ~= and ~ are two different operations. ~ always creates a copy of the original data, while ~= attempts to resize the destination in place and append the new data in the newly-resized space.
Sep 10 2007
prev sibling next sibling parent Christian Kamm <kamm.incasoftware shift-at-left-and-remove-this.de> writes:
Christopher Wright wrote:

 New minimal test case:
 ---
 import std.boxer;
 void main () {
     foreach (b; boxArray(9, 'b')) {
        b.toString;
     }
 }
 ---

The problem is that the b defined in the foreach statement is final, thus D won't let you call non-const methods on it. Boxer.toString is not a const method. Same thing without boxer or toString: --- struct Foo { const void const_bar() {} void nonconst_bar() {} } void main() { Foo[] foo_arr; foo_arr.length = 1; foreach(foo; foo_arr) { foo.const_bar(); // OK foo.nonconst_bar(); // Error: cannot call mutable method on final struct } } ---
Sep 10 2007
prev sibling parent Carlos Santander <csantander619 gmail.com> writes:
Christopher Wright escribió:
 Carlos Santander wrote:
 I don't use D 2.0, but here it goes...

 string is const(char)[] or const char[] or something like that... It's 
 const, anyway, so you aren't supposed to modify it. Replace "string 
 value" with "char[] value".

If I use char[] rather than string, it fails with the error message I stated (and indeed, in the original, I used char[] accidentally). If I just call Box.toString and don't do anything with the returned string, it fails with the same error message. 'string ~= whatever' is valid because the string is not final. The compiler rewrites it as 'string = string ~ whatever'. New minimal test case: --- import std.boxer; void main () { foreach (b; boxArray(9, 'b')) { b.toString; } } ---

In that case, I guess the workaround (ref b) is not really a workaround but the way it should be done. -- Carlos Santander Bernal
Sep 10 2007