let x = compute_x();
f(x.clone());
g(x);
was confusing/surprising to me. Isn't the whole point of default-immutable that you can use x without owning a copy? Forgive me if this is a stupid question. let x = compute_x();
f(x.clone());
g(x);
was confusing/surprising to me. Isn't the whole point of default-immutable that you can use x without owning a copy? Forgive me if this is a stupid question. let x = compute_x();
f(&x);
g(&x);
Otherwise you have moves there. Now it depends on the API, it may want the value and not the reference (a borrow).For a non-trivial object (which apparently x is), you would always prefer to write &x (immutable reference to x) or at least &mut x (mutable reference, not applicable here because x is not defined as a mutable thing).
So apparently these functions intend to consume x. That's not common but there are a reasonable number of Rust APIs which consume larger things on purpose.
I guess in that case I don't really get the complaint. If there's a good reason for f or g to consume x, I would think I would always want the copy to be explicit. But I could still be missing some subtlety I guess.
to make it clear that the clone is "cheap", some Rust developer prefer using `Arc::Clone(&x)` over `x.clone()`.
1. Suppose x is a Teapot, some complicated object with lots of internal state - it has about two cups of Early Grey in it, and is a lovely blue and white pattern.
If we call f(x) then we're giving our Teapot, x, to this f() function. It apparently doesn't give anything back, so the Teapot is gone now! In Rust we say x was moved into f.
Maybe f empties the tea out of the teapot, then smashes it and uses the broken pieces to fill a pothole in the road. Weird.
So we use the clone() call to ask for an exact duplicate of the Teapot, now f(x.clone()) only gives the clone to this function f, our original Teapot remains unaltered in our possession.
Because we've still got x, we can call g() which apparently like f also wants the actual Teapot, maybe this function plants a cactus in it and puts it on a high shelf. Once again our Teapot, x, is gone because we gave it to g().
Now, most Rust functions on complicated objects like a Teapot (or more realistically, a Vec or a String or File) don't move parameters like this, they want either a mutable reference which you'd write &mut x or an immutable reference, which is written &x. If these functions f() and g() only needed immutable references, our Teapot x is fine. We can't use mutable references because x itself is not labelled as mutable, if it was, the Teapot might be altered, but it's still a Teapot, the functions don't own it, they were just given a reference and so they aren't allowed to destroy it for example.
OK 2. Suppose x is just a 32-bit unsigned integer, u32 in Rust
Now things are different, because the u32 type implements Copy, a trait which tells the Rust compiler, "My value is literally the value of the bytes in RAM, if you copy those bytes, that's all I am, that's a copy of me".
So now even though f(x) moves x into f, the compiler knows x is just this 4 byte value, it can give f the same 4 byte value and keep the one in x, and everybody is happy. f() can do whatever it wants with that value, x is unaffected. We can call x.clone() if we want, but it just makes another identical 4 byte value, kinda pointless.
Then g(x) does the same, even if it wants to "move" the parameter, the compiler just gives it a copy anyway, no harm done.
Does this help?