Let's consider a couple of scenarios since we don't know x's type here presumably on purpose
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.
It all makes sense, sure. It's not that I don't understand why rust would distinguish these things. The question is really, do people writing rust code not mostly default to passing the immutable reference when that's an option (i.e. when the function doesn't need to modify the input). And why not? (Edit: just saw your other comment on the other thread, will respond there)
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?