#Using the builder pattern how to move struct ownership?

19 messages · Page 1 of 1 (latest)

brazen rune
#

I keep getting the compiler error: "move occurs because self.sth has type S1, which does not implement the Copy trait":

struct S1 {field1: String, field2: i32}
struct S2 {sth: S1}
 
struct Builder {sth: S1}
impl Builder {
    fn new(sth: S1) -> Builder {Builder{sth}}
    fn build(&self) -> S2 {S2{sth: self.sth}}
}

fn main() {
    let s1 = S1{field1: "asd".to_owned(), field2: 32};
    let _ = Builder::new(s1).build();
}

Compiler error:

error[E0507]: cannot move out of `self.sth` which is behind a shared reference
 --> src/main.rs:7:36
  |
7 |     fn build(&self) -> S2 {S2{sth: self.sth}}
  |                                    ^^^^^^^^ move occurs because `self.sth` has type `S1`, which does not implement the `Copy` trait

What I actually want to do is just move S1 through the Builder::new to it's final destination - in S2 without copying it all the time (my understanding is that Copy trait does that).
I could use a reference in S2 but the field is long lived and I'd prefer to store there it's value. Currently the way with references would be to have all the arguments defined first before calling the builder and then consuming their references. So how do I consume a struct without copying it all the time?

warm anchor
#

If you pass an instance by value, and that instance doesn't implement Copy then it's consumed

You can have the builder just hold an S2 and construct it in the new function though?

Then build can return that directly

brazen rune
#

Thanks for the quick feedback!

"and that instance doesn't implement Copy then it's consumed"
Um... but S2doesn't have Copy trait implemented and the compiler complains it cannot move it...

"You can have the builder just hold an S2 and construct it in the new function though?"
The code is a min example. In my actual builder I do other things before producing the final struct.

warm anchor
#

compiler complains it cannot move it...

The compiler actually complains that you can't use it after it's moved, which is correct. It's saying that a move occurs because the type doesn't implement Copy, and since a move occurs then you can't use the old value afterwards

#

I think there's code that you're not showing where you use the S1 that you're passing to Builder::new afterwards right?

brazen rune
#

Aha, so the actual error is that it cannot move...

warm anchor
#

Ah okay, that's a slightly different error - it's just saying that you actually can't do a move because all you have is a borrow to the Builder

You can fix that by taking ownership of the Builder in the build method by changing the signature to:

fn build(self) -> S2

#

Note that with that you can only use the builder to produce one final value, so it's not reusable

#

The other option is to take it by reference but you would have to Clone the S2

#

Which would require S2 to implement Clone (not Copy)

brazen rune
#

Especially since I call in build self.sth and not &self.sth

warm anchor
#

It doesn't affect the return value but it allows you to move out of self - which you can't do if you only have a &self

When you type self.sth if that type isn't Copy then it's doing a move, and you're not allowed to move out of something if you only have a reference to it

brazen rune
#

Thanks for the explanation. So how do you decide when to use in methods &self or self?

#

Or could you always use send and then call &self.sth when you only need to borrow a field?

warm anchor
#

One way to think about it is that you're only allowed to move something if you're the owner of it. If your method is taking a &self then you don't own that data so it would be bad if you were allowed to move something out of it - other people could be watching (since you can have multiple &self at the same time)

By changing the method to take self you can only ever call that if you give that method ownership of itself, which guarantees there's nobody else watching (no references to it) - so you can do whatever

Decision of whether to use &self or self is just based on whether you need to own the data or not. It's generally better to use &self (or &mut self) in methods but it depends on the use-case

#

If you use &self then you're not forcing the owner to give up its ownership of that value - if you use self then you can't use that value after the method is called

brazen rune
#

I feel I owe you a beer. That was super helpful and makes a lot of sense! I think I'll start to actually like Rust.