//rant follows, so feel free to ignore it, I'm sure it won't make any difference - but I just have to express it or else I don't think I can overcome it.
So, I'm having some serious trouble at the subconscious level, accepting this copy thing I know, it's ridiculous, but I'm just being honest here.(maybeee, unacceptably honest? or just rude)
I was thinking about this:
The way I would "solve" it is: consider "xx" being an expression, or the top/root expression(not contained in any other (sub)expression) and thus any amount of x occurrences inside this expression(or any subexpressions within it) is to be considered as x being moved into the top expression(the xx expression, not the first x or second x (sub)expressions), so it doesn't have to move into it twice for x*x, but only once.
But in a case like this call some_func(x*x, x);
it would fail for the third x, but not for the "x*x". Because the 2nd arg(aka 3rd x) is another expression, and x already moved into the first expression(arg 1).
Now, granted you still need to copy x twice for "x*x" to work, but this copy isn't what's bothering me, it's the fact that it's still accessible after the copy that is. Just as steveklabnik mentioned: [quote="steveklabnik, post:2, topic:6736"]
Moves are copies. The only difference is if you can use the value afterward or not; otherwise, they are identical.
[/quote]
Of course, this is obvious! That's my problem. That's what I don't find acceptable, that with the same syntax, you can have either of the two happening:
fn main() {
let s = String::from("hello"); // s goes into scope.
takes_ownership(s); // s moves into the function...
// ... and so is no longer valid here.
let x = 5; // x goes into scope.
makes_copy(x); // x would move into the function,
// but i32 is Copy, so it’s okay to still
// use x afterward.
} // Here, x goes out of scope, then s. But since s was moved, nothing special
// happens.
fn takes_ownership(some_string: String) { // some_string comes into scope.
println!("{}", some_string);
} // Here, some_string goes out of scope and `drop()` is called. The backing
// memory is freed.
fn makes_copy(some_integer: i32) { // some_integer comes into scope.
println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.
I'm saying x
, should move too! Always! And if you want copy, you could do x.copy()
or x.clone()
(I forget the difference, but it's irrelevant for this context)
Why wouldn't the copy be explicit? especially since it allows you to keep using the binding afterwards.
So, maybe I'm conflating two things:
- that a value can be copied
- that a value can move
It seems that 2. can always happen. But if 1. can happen then you choose to not do 2.
In other words, all values can move, but not all values can be copied(because I don't know, deep structure with heap and stuff? and maybe needs the user to have a block of code implemented that does the copying - the Copy trait?).
So, a value can be copy or not - but either way, it could always move, right? I mean, the move and copy seem to be two different things. (and I'm not talking about memcopy and memmove kind of copy and move, but obviously, Rust's understanding of Copy trait and the binding moving into another place and thus being inaccessible in the current context - so in this sense, moves are not copies)
So, the fact that takes_ownership(s);
moves and makes_copy(x);
copies, with those functions having the same implementation (just different types used) is just so unacceptable to me considering all other Rust safety stuff. It just boggles my mind, at the subconscious level - which is why I may not be able to full explain it why.
(By moves
I mean, s
is inaccessible afterwards and by copies
I mean x
is accessible afterwards.)
I mean, wtf, two different behaviors just based on type being different? Why do you make the programmer keep track of which behavior happens here, instead of the compiler? Why can't the behavior just be the same regardless of type : aka be move! always! And if you want copy, then do x.copy() or something, so now it's obvious that x didn't move(and yeah x could be a struct with Copy trait when it's not as obvious from reading the code(as the above code is))
I'm guessing this kind of move doesn't really do any copy, or it does a shallow one anyway. But copy likely does a deep copy(or else I could think of some bugs if the passed copy gets some of it's on-heap structures modified via interior mutability or something). So in reading some Rust code, where you see assignments or binding being passed to functions you'll be wondering: geee, I wonder if it's a copy or move happening here... oh yeah because that's so f'in explicit.
Bottom line: implicit copy OR move which is what's currently happening is evil and seems against (what I percieve to be) Rust's principles.
If anything is wrong(in my mind, right) with Rust so far, this (has)gotta be it.
I'm sorry, for the above, my subconscious is evil(it needs reprogramming, right?) - I had to let it spew its discontent but you'll forgive me, right? I mean, why wouldn't you? you're a good person!!(reader) Also, I'll forgive you for this implicit (copy OR move)
in Rust xDD ok, that's not helping. What I mean to say is, thank you for all that is Rust so far - I'm sure I'll find a way to workaround this while at the same time not hating it too much xD! even tho I feel so strongly about it, at the subconscious level.