Search code examples
stringd

Allocation memory for string


In D string is alias on immutable char[]. So every operation on string processing with allocation of memory. I tied to check it, but after replacing symbol in string I see the same address.

string str = "big";
writeln(&str);
str.replace("i","a");
writeln(&str);

Output:

> app.exe
19FE10
19FE10

I tried use ptr:

string str = "big";
writeln(str.ptr);
str.replace(`i`,`a`);
writeln(str.ptr);

And got next output:

42E080
42E080

So it's showing the same address. Why?


Solution

  • You made a simple error in your code:

    str.replace("i","a");

    str.replace returns the new string with the replacement done, it doesn't actually replace the existing variable. So try str = str.replace("i", "a"); to see the change.

    But you also made a too broadly general statement about allocations:

    So every operation on string processing with allocation of memory.

    That's false, a great many operations do not require allocation of new memory. Anything that can slice the existing string will do so, avoiding needing new memory:

    import std.string;
    import std.stdio;
    
    void main() {
            string a = "  foo  ";
            string b = a.strip();
            assert(b == "foo"); // whitespace stripped off...
    
            writeln(a.ptr);
            writeln(b.ptr); // but notice how close those ptrs are
            assert(b.ptr == a.ptr + 2); //  yes, b is a slice of a
    }
    

    replace will also return the original string if no replacement was actually done:

    string a = "  foo  ";
    string b = a.replace("p", "a"); // there is no p to replace
    assert(a.ptr is b.ptr); // so same string returned
    

    Indexing and iteration require no new allocation (of course). Believe it or not, but even appending sometimes will not allocate because there may be memory left at the end of the slice that is not yet used (though it usually will).

    There's also various functions that return range objects that do the changes as you iterate through them, avoiding allocation. For example, instead of replace(a, "f", "");, you might do something like filter!(ch => ch != 'f')(a); and loop through, which doesn't allocate a new string unless you ask it to.

    So it is a lot more nuanced than you might think!