Akki
Akki

Reputation: 95

Rust struct update syntax without taking ownership

#[derive(Debug)]
struct Car {
    company: String,
    model: String,
    cost: i32
}

fn main() {
    let car1 = Car {
        company: String::from("Ford"),
        model: String::from("123xyz"),
        cost: 50000
    };
    let car2 = Car {
        model: String::from("987pqr"),
        cost: 47000,
        ..car1
    };
    println!("{:#?}", car2);
    println!("{:#?}", car1.company);
}

Here compiler correctly tells me:

println!("{:#?}", car1.company);
                  ^^^^^^^^^^^^ value borrowed here after move
move occurs because `car1.company` has type `String`, which does not implement the `Copy` trait

I understand that String instance is on the heap and that is why it cannot be copied, ownership was moved to the car2.company. I can solve this use-case by something like this:

#[derive(Debug, Clone)]
struct Car {
    company: String,
    model: String,
    cost: i32
}

fn main() {
    let car1 = Car {
        company: String::from("Ford"),
        model: String::from("123xyz"),
        cost: 50000
    };
    let car2 = Car {
        model: String::from("987pqr"),
        cost: 47000,
        ..car1.clone()
    };
    println!("{:#?}", car2);
    println!("{:#?}", car1.company);
}

Question 1: Is there a better way to do this?


Now my problem with this is that if the struct had many fields clone would create a copy of the car1 instance of the struct just to use its values to be assigned to the car2 instance of struct which itself will store the values. If x is the memory required to store an instance of the struct Car,

3x memory required where technically only 2x memory should have been needed.

Question 2: After car2 is instantiated will car1.clone() go out of scope and hence relinquish the memory?

If yes: this method of clone would be fine as only temporary one extra x memory is needed.

If no: then is there a way to achieve this goal without needed the extra x memory or taking ownership of the binding itself.

Upvotes: 7

Views: 1030

Answers (1)

vallentin
vallentin

Reputation: 26245

You're essentially asking if ..car1.clone() performs a partial clone, and only clones the missing fields, i.e. company for car2.

Would that be a possible optimization? Possibly, but clone() could do anything, so it might not be as cut an dry to perform a "partial clone()". So the answer is no, car1.clone() is cloned entirely.

Question 1: Is there a better way to do this?

I'm assuming that you're using the update syntax (..), because you have significantly more fields. However, if not, then to avoid needlessly cloning model. Then you can explicitly clone and assign car1.company.

However, I think you already know this. But if you want to avoid the needless clone of model. Then this is the alternate solution.

let car2 = Car {
    company: car1.company.clone(),
    model: String::from("987pqr"),
    cost: 47000,
};

Question 2: After car2 is instantiated will car1.clone() go out of scope and hence relinquish the memory?

Yes. model from car1.clone() is dropped immediately after the assignment to car2. However, company is of course not dropped, as that is moved to car2.

Upvotes: 3

Related Questions