error[E0507]: cannot move out of borrowed content
--> src/main.rs:52:9
|
52 | self.thing
| ^^^^^^^^^^ cannot move out of borrowed content
error: aborting due to previous error
I understand what it is saying but don't know the best solution. What I'd like to do is move self.thing into a temporary thing, then replace self.thing with a Thing::new(), and then return the temporary thing. Unfortunately that doesn't work. Would making self.thing an Option allow the build() method return a plain Thing? I'm trying to move the Thing once it is built rather than copy it because the real thing that this code is serving as a model for will be much heavier.
Can anyone advise?
One other question on this: If Thing's fields are private and Thing is in a different module from ThingBuilder am I right in assuming that my choices are to make the fields public or provide public setters because there's no equivalent of C++ "friend" classes in rust?
I would suggest making build taking self as value, not as mutable reference so it will be consumed (which will makes sense, because its the builder (?)).
Accident more than design! But the question you raise makes me think that I should put the builder inside the Plan module & then try and keep the fields private.
[edit] I've now been able to make the Plan fields private by making the builder part of the Plan module.
This is exaclty how I make my builders. There is also possibility to take self instead of mut self for intermediate methods, and reconstructing objects like this:
But I personally don't like this attitude. In general I strongly advice to avoid returning &'a mut Self - it allows chaining, but the problem is, that chained values are borrows, and you cannot do something like:
let builder = PlanBuilder::new().path("path");
do_additional_buildiing(builder);
Instead of this, you need to do additional boiler plate:
let builder = PlanBuilder::new();
builder.path("path");
do_additional_building(builder);
Stdlib does many strange things in many places. I guess that some of them, maybe all of them are pretty well justified, but it doesn't mean, that is always the best way. This exact case is very specific, because building doesn't need to consume builder, and also all building options are (almost) always known in place where file is created, which is not typical builder use case.
The way you describe works and is fine for the builder pattern. But if you create a builder and then separately (e.g., in if statements) want to change it then things get less appealing.
let t1 = Builder::new().one(1).two(2).build(); // fine
let b = Builder::new();
b = b.one(1);
b = if extra { b.two(2) } else { b };
let t2 = b.build();
Whereas right now using the &mut Self stuff I can do this:
let t1 = Builder::new().one(1).two(2).build(); // fine
let mut b = Builder::new();
b.one(1);
if extra { b.two(2) }
let t2 = b.build();
It is actually true, I didn't point it out because I don't fall in problem very often, but yes, this may be considered as disadvantage of this attitude.