Thread overview
assigning to multiple fields in a struct
Jun 05
Lukanian
Jun 05
monkyyy
Jun 06
Basile B.
Jun 07
Lukanian
Jun 07
kinke
June 05

I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields unchanged. Standard situation looks like this.

struct MyStruct {
    int    field1;
    double field2;
    string field3;
    int    field4;
    string field5;
}

MyStruct myStruct = //...

// i need to change some values
myStruct.field1 = 10;
myStruct.field2 = 0.5;
myStruct.field5 = "hello";

// and it gets even uglier if struct is inside some complex structure i.e.:
myArray[0].someOtherStruct.myStruct.field1 = 10;
myArray[0].someOtherStruct.myStruct.field2 = 0.5;
myArray[0].someOtherStruct.myStruct.field5 = "hello";

I propose two solutions:

    1. automatically generate setter for every field which returns reference to the struct
    1. automatically generate an assign function with all fields as optional params, but assign only those which are given by user (no idea how to implement this)

My attempt to implement solution 1.

myStruct
  .setfield1(10)
  .setfield2(0.5)
  .setfield5("hello");

// or just make it in one line
myStruct.setfield1(10).setfield2(0.5).setfield5("hello");

I tried to implement it with mixin template:


mixin template GenerateSetters(T) {
    enum fieldNames = FieldNameTuple!T;
    alias fieldTypes = FieldTypeTuple!T;
	enum typeName = fullyQualifiedName!T;

    string generateFunctions() {
        string param;
		string fuctions;
        foreach (i, fieldName; fieldNames) {
            param = fieldTypes[i].stringof ~ " " ~ fieldName;

	    fuctions ~="ref "~typeName~" set"~fieldName~"(return ref "~typeName~" a, "
                ~ param ~ ") {\n"~
                "   a."~fieldName~"  = "~fieldName~";\n"~
                "   return a;\n"~
                "}\n";
        }

        return fuctions;
    }

	//pragma(msg, generateFunction());
    mixin(generateFunctions());
}

struct MyStructTest {
    int field1;
    double field2;
    string field3;
	string field4;
	string field5;
}

mixin GenerateSetters!(MyStructTest); // you need to call this after struct definition

It works, but if it was built into the language it could be more elegant than this.

The second solution could look like this, but i don't have an implementation:

myStruct.setFields(field1:10, field2:0.5, field5:"hello");

June 05

On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:

>

I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields unchanged. Standard situation looks like this.

struct MyStruct {
    int    field1;
    double field2;
    string field3;
    int    field4;
    string field5;
}

MyStruct myStruct = //...

// i need to change some values
myStruct.field1 = 10;
myStruct.field2 = 0.5;
myStruct.field5 = "hello";

// and it gets even uglier if struct is inside some complex structure i.e.:
myArray[0].someOtherStruct.myStruct.field1 = 10;
myArray[0].someOtherStruct.myStruct.field2 = 0.5;
myArray[0].someOtherStruct.myStruct.field5 = "hello";

I propose two solutions:

    1. automatically generate setter for every field which returns reference to the struct
    1. automatically generate an assign function with all fields as optional params, but assign only those which are given by user (no idea how to implement this)

My attempt to implement solution 1.

myStruct
  .setfield1(10)
  .setfield2(0.5)
  .setfield5("hello");

// or just make it in one line
myStruct.setfield1(10).setfield2(0.5).setfield5("hello");

I tried to implement it with mixin template:


mixin template GenerateSetters(T) {
    enum fieldNames = FieldNameTuple!T;
    alias fieldTypes = FieldTypeTuple!T;
	enum typeName = fullyQualifiedName!T;

    string generateFunctions() {
        string param;
		string fuctions;
        foreach (i, fieldName; fieldNames) {
            param = fieldTypes[i].stringof ~ " " ~ fieldName;

	    fuctions ~="ref "~typeName~" set"~fieldName~"(return ref "~typeName~" a, "
                ~ param ~ ") {\n"~
                "   a."~fieldName~"  = "~fieldName~";\n"~
                "   return a;\n"~
                "}\n";
        }

        return fuctions;
    }

	//pragma(msg, generateFunction());
    mixin(generateFunctions());
}

struct MyStructTest {
    int field1;
    double field2;
    string field3;
	string field4;
	string field5;
}

mixin GenerateSetters!(MyStructTest); // you need to call this after struct definition

It works, but if it was built into the language it could be more elegant than this.

struct MyStruct {
    int    field1;
    double field2;
    string field3;
    int    field4;
    string field5;
}
unittest{
    MyStruct myStruct;
    with(myStruct){
        field1=1;
        field2=3.14;
        field3="foo";
}}

you will likely be told to do this

>

The second solution could look like this, but i don't have an implementation:

myStruct.setFields(field1:10, field2:0.5, field5:"hello");

disagree that this is possible, templated name arguments doesnt have the tools yet

June 06

On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:

>

[...]
The second solution could look like this, but i don't have an implementation:

myStruct.setFields(field1:10, field2:0.5, field5:"hello");

Builtin tuples + syntax sugar + AST lowering could help here.

The sugar in the grammar is

DotTupleExp ::= Expression "." TupleExpression

for example

a.(member1, member2) = (0,1);

would be lowered to

auto __noSideEffect = a;
(__noSideEffect.member1, __noSideEffect.member2) = (0,1);
June 06

On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:

>

I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields

So you are not happy with:

MyStruct myStruct = //...
// i need to change some values
with (myStruct) {
  field1 = 10;
  field2 = 0.5;
  field5 = "hello";
}

Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...

June 07

On Friday, 6 June 2025 at 16:15:36 UTC, Dejan Lekic wrote:

>

On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:

>

I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields

So you are not happy with:

MyStruct myStruct = //...
// i need to change some values
with (myStruct) {
  field1 = 10;
  field2 = 0.5;
  field5 = "hello";
}

Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...

Thank you for reminding me about "with" I almost forgot about it. It may be handy sometimes, but I still think that my version can have some semantic advantages, mainly because it can be used as a rvalue:

// passing result to a funcion
myFunction(myStruct.setf1(1).setf3(3));
   // or
myFunction(myStruct.setFields(f1:1, f3:3));

// using it in a chain with oter calls
myStructmyStruct.setf1(1).setf3(3).calculateProduct().writeln();
myStruct.setFields(f1:1, f3:3).calculateProduct().writeln();
June 07

This is also an interesting generic approach: https://github.com/dlang/dmd/blob/8762d1eaee42119269b82a1b1b7063c89c1e6a69/compiler/src/build.d#L2195-L2205

Sketch:

struct Builder(T)
{
    T obj;

    ref Builder opDispatch(string name)(typeof(__traits(getMember, T, name)) arg)
    {
        __traits(getMember, obj, name) = arg;
        return this;
    }
}

struct S
{
    int x;
    double y;
    string z;
}

void main() {
    const s = Builder!S()
        .x(1)
        .y(2.0)
        .z("z")
        .obj;
    assert(s == S(1, 2.0, "z"));
}