November 19, 2021

Hi all,

I'm trying to figure out the most efficient way to create modified instances of immutable structs.

Currently, I'm doing the following:

immutable struct Node {
	string label;
	Node parentNode;
	NetworkPort port;

	auto withLabel(string newLabel)
	{
		return Node(newLabel, this.parentNode, this.port);
	}

	auto withParentNode(Node newParentNode)
	{
		return Node(this.label, newParentNode, this.port);
	}

	auto withNetworkPort(NetworkPort newPort)
	{
		return Node(this.label, this.parentNode, newPort);
	}
}

Coming from a scripting language the above makes the most sense.
In D, this an efficient way to do it? Are there any best practices around this?

Thanks in advance

Merlin

November 19, 2021
On Friday, 19 November 2021 at 17:38:53 UTC, Merlin Diavova wrote:
> I'm trying to figure out the most efficient way to create modified instances of immutable structs.

This might not be the best way but it is a kinda cool trick anyway: structs have a `tupleof` property you can slice. So you can do things like:

Node(old_node.tupleof[0 .. argIndex], replacement, old_node.tupleof[argIndex + 1 .. $]);

Where argIndex can be like 0 to replace the first item in the struct, or 1 to replace the second.

It is possible to look this up using reflection and kinda automate this if you wanted.

```
mixin template Replaceable() {
        // returns a replaced thing when you use it as `with_someField`
        typeof(this) opDispatch(string s, T)(T replacement) if(s.length > 5 && s[0 .. 5] == "with_")
        {
                static foreach(idx, member; typeof(this).tupleof) {
                        static if(__traits(identifier, member) == s[5 .. $])
                                enum argIndex = idx;
                }
                // if the arg is not found you will get an ugly error message here about undefined
                // argIndex.... meh.
                return typeof(this)(this.tupleof[0 .. argIndex], replacement, this.tupleof[argIndex + 1 .. $]);
        }
}

immutable struct Node {
        string label;
        Node* parentNode;
        int port;

        mixin Replaceable; // add the ugly generic code from the top
}

void main() {
        Node i = Node("one", null, 4);
        Node i2 = i.with_port(6); // and now use it like this
        assert(i2.label == "one");
        assert(i2.port == 6);
}
```


I did `with_` instead of camelCase to avoid having to mess with case comparisons on the name.