How to Copy a Struct and Modify One of Its Properties at the Same Time

How to copy a struct and modify one of its properties at the same time?

The answers here are ridiculous, especially in case struct's members change.

Let's understand how Swift works.

When a struct is set from one variable to another, the struct is automatically cloned in the new variable, i.e. the same structs are not related to each other.

struct A {
let x: Int
var y: Int
}

let a = A(x: 5, y: 10)
var a1 = a
a1.y = 69
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"

Keep in mind though that members in struct must be marked as var, not let, if you plan to change them.

More info here: https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

That's good, but if you still want to copy and modify in one line, add this function to your struct:

func changing<T>(path: WritableKeyPath<A, T>, to value: T) -> A {
var clone = self
clone[keyPath: path] = value
return clone
}

Now the example from before changes to this:

let a = A(x: 5, y: 10)
let a1 = a.changing(path: \.y, to: 69)
print("a.y = \(a.y); a1.y = \(a1.y)") // prints "a.y = 10; a1.y = 69"

I see that adding 'changing' to a lot of struct would be painful, but an extension would be great:

protocol Changeable {}

extension Changeable {
func changing<T>(path: WritableKeyPath<Self, T>, to value: T) -> Self {
var clone = self
clone[keyPath: path] = value
return clone
}
}

Extend your struct with 'Changeable' and you will have your 'changing' function.

With the 'changing' function approach, too, any property that you specify in the 'changing' function's call sites (i.e. of type WritableKeyPath) should be marked in the original struct as var, not let.

How can I easily copy an immutable struct in swift, varying only some fields [Like kotlin dataclass copy method]?

Think, you can extend the struct with a copy(...) method taking nil values as default and replacing them with instance ones while using non-nil otherwise. E.g. something like this:

extension LogicalState {
func copy(a: String? = nil, b: Bool? = nil, c: Int? = nil) -> LogicalState {
return LogicalState(a: a ?? self.a, b: b ?? self.b, c: c ?? self.c)
}
}

So you can use it to copy instance while varying the needed params:

let state = LogicalState(a: "A", b: false, c: 10)
let stateCopy1 = state.copy(c: 30)
let stateCopy2 = state.copy(a: "copy 2")

Modification of a field in copied structure change original structure field

Although temporaryLevels is actually a copy of lastLevels, the properties RightLevels and RightLevelsArray still refer to the same data, as arrays and lists are class types.

In the assignment

       Levels temporaryLevels = lastLevels;

you create a copy of the references to the list and the array, but not of the objects themselves.

I don't know of any built-in way to do this, but you can define a copying constructor in Levels:

public Levels(Levels source)
{
NumberOfLevels = source.NumberOfLevels;
RightLevelsArray = new SingleLevel[NumberOfLevels + 1];
source.RightLevels.CopyTo(RightLevelsArray);
RightLevels = new List<SingleLevel>();
RightLevels.AddRange(source.RightLevels);
}

And, instead of the assignment, you call the new constructor like this:

        Levels temporaryLevels = new Levels(lastLevels);

Now temporaryLevels is a deep copy of last Levels, and modifying the list or array of one struct won't change the other.

Copying one structure to another

Copying by plain assignment is best, since it's shorter, easier to read, and has a higher level of abstraction. Instead of saying (to the human reader of the code) "copy these bits from here to there", and requiring the reader to think about the size argument to the copy, you're just doing a plain assignment ("copy this value from here to here"). There can be no hesitation about whether or not the size is correct.

Also, if the structure is heavily padded, assignment might make the compiler emit something more efficient, since it doesn't have to copy the padding (and it knows where it is), but mempcy() doesn't so it will always copy the exact number of bytes you tell it to copy.

If your string is an actual array, i.e.:

struct {
char string[32];
size_t len;
} a, b;

strcpy(a.string, "hello");
a.len = strlen(a.string);

Then you can still use plain assignment:

b = a;

To get a complete copy. For variable-length data modelled like this though, this is not the most efficient way to do the copy since the entire array will always be copied.

Beware though, that copying structs that contain pointers to heap-allocated memory can be a bit dangerous, since by doing so you're aliasing the pointer, and typically making it ambiguous who owns the pointer after the copying operation.

For these situations a "deep copy" is really the only choice, and that needs to go in a function.

How do I copy a struct in Golang?

In go, primitive types, and structs containing only primitive types, are copied by value, so you can copy them by simply assigning to a new variable (or returning from a function). For example:

type Person struct{
Name string
Age int
}

alice1 := Person{"Alice", 30}
alice2 := alice1
fmt.Println(alice1 == alice2) // => true, they have the same field values
fmt.Println(&alice1 == &alice2) // => false, they have different addresses

alice2.Age += 10
fmt.Println(alice1 == alice2) // => false, now they have different field values

Note that, as mentioned by commenters, the confusion in your example is likely due to the semantics of the test library you are using.

If your struct happens to include arrays, slices, or pointers, then you'll need to perform a deep copy of the referenced objects unless you want to retain references between copies. Golang provides no builtin deep copy functionality so you'll have to implement your own or use one of the many freely available libraries that provide it.

Change the value of a property of a struct in C#

Structs can be either mutable or immutable, but they should be immutable according to many people.

Your example is a mutable struct.

Example of use:

var t = new Test();
// t.str is null, and t.int1 is 0

t.str = "changed!"; // OK

var t2 = t;
t2.int1 = 42;
// t.int1 is still 0

var li = new List<Test> { t, t2, };
t.int1 = 666; // OK, but copy in li is unaffected

li[0].int1 = 911; // compile-time error, not a variable

var t3 = t2;
bool checkA = (t3 == t2); // compile-time error, you did not overload operator ==
bool checkB = t3.Equals(t2); // OK, true, ValueType class overrides Equals for you
bool checkC = t2.Equals(t); // OK, false
bool checkD = object.ReferenceEquals(t, t); // false, two distinct boxes
// same as (object)t==(object)t

By request, here is one way to make that struct immutable:

public struct Test 
{
public string str { get; private set; }
public int int1 { get; private set; }

public Test(string str, int int1) : this()
{
this.str = str;
this.int1 = int1;
}
}
// if you introduce methods (other than constructors) that use the private setters,
// the struct will no longer be immutable

and here is another one:

public struct Test 
{
readonly string m_str;
readonly int m_int1;

public string str { get { return m_str; } }
public int int1 { get { return m_int1; } }

public Test(string str, int int1)
{
m_str = str;
m_int1 = int1;
}
}

C# Automatic deep copy of struct

The runtime performs a fast memory copy of structs and as far as I know, it's not possible to introduce or force your own copying procedure for them. You could introduce your own Clone method or even a copy-constructor, but you could not enforce that they use them.

Your best bet, if possible, to make your struct immutable (or an immutable class) or redesign in general to avoid this issue. If you are the sole consumer of the API, then perhaps you can just remain extra vigilant.

Jon Skeet (and others) have described this issue and although there can be exceptions, generally speaking: mutable structs are evil.
Can structs contain fields of reference types

Update the fields of one struct to another struct

For trivially copying every field, you can simply do something like *personA = personB. If you need to "not copy" just one specific field (same field every time), you could probably just save that field's value in a separate variable, copy everything with *personA = personB, and then copy the value back. But this is only useful for very specific situations. It wouldn't allow for example to have a dynamic set of fields not to copy.

If you want to do it with more flexibility, you can do it using reflection, sample code below.

Notice that there may be a few limitations. Notably, you can only set exported fields. Also, if you don't test for these limitations and accidentally try to set a field that's not settable, or with a value of type that's not assignable to that field, etc, the reflect package will happily panic. So it's wise to add a lot of checks before you actually .Set(...) the field.

import (
"fmt"
"reflect"
)

type Person struct {
FirstName string
LastName int
Age int
HairColor string
EyeColor string
Height string
}

func updateFields(personA *Person, personB Person) {
// .Elem() called to dereference the pointer
aVal := reflect.ValueOf(personA).Elem()
aTyp := aVal.Type()

// no .Elem() called here because it's not a pointer
bVal := reflect.ValueOf(personB)

for i := 0; i < aVal.NumField(); i++ {
// skip the "Age" field:
if aTyp.Field(i).Name == "Age" {
continue
}
// you might want to add some checks here,
// eg stuff like .CanSet(), to avoid panics
aVal.Field(i).Set(bVal.Field(i))
}
}

func main() {
b := Person{
FirstName: "Bruno",
LastName: 1,
Age: 2,
HairColor: "hello",
EyeColor: "world",
Height: "tall",
}
a := Person{}
fmt.Println(a)
updateFields(&a, b)
fmt.Println(a)
}

Class vs. Struct in Swift (copying)

Why in the class declaration does it change the firstMessage object. Are they the same objects?

The example you gave is a really nice one because it succinctly illustrates the difference between class and struct, and you came about this close -> <- to answering your own question, even if you didn't realize it. As the other answers have explained, class creates a reference type, which means that when you assign an instance of a class to a variable, that variable gets a reference to the object, not a copy of it. You said so yourself:

//if I assign, its a reference to the original instance
var secondMessage = firstMessage

In your example, firstMessage and secondMessage are really references to the one object that you created. This kind of thing is done all the time in object oriented languages because it's often important to know that you're dealing with a specific object and not a copy, especially if you might want to make changes to that object. But that also brings danger: if your code can get a reference to an object and change it, so can some other code in the program. Shared objects that can be changed create all kinds of headaches when you start writing multithreaded code. When you added text to secondMessage, firstMessage also changed because both variables refer to the same object.

Changing the declaration of Message to struct makes it a value type, where assignment (for example) creates a new copy of the object in question instead of a new reference to the same object. When you added text to secondMessage after changing Message to a struct, the assignment secondMessage = firstMessage created a copy of firstMessage, and you only changed that copy.

Is this a rule that if I assign a new object from the old object?

Whether your assignment creates a copy of the object or a reference to it depends, as you've shown, on whether the thing being assigned has reference semantics (class) or value semantics (struct). So you need to be aware of the difference, but most of the time you don't need to think too hard about it. If you're dealing with an object where you don't care about the object's identity and are mainly concerned with its contents (like a number, string, or array), expect that to be a struct. If you care about which object you're dealing with, like the front window or the current document, that'll be a class.

Then I would have to declare secondMessage = Message() to make it a new instance.

Right -- if Message is a class, assigning one to a new variable or passing it into a method won't create a new one. So again, are you more likely to care about which message you're dealing with, or what is in the message?



Related Topics



Leave a reply



Submit