3 ms·
Are copy semantics always used in place of move semantics for a Copy type? I didn't know that.
by hgomersall 4y ago
Are copy semantics always used in place of move semantics for a Copy type? I didn't know that.
- deleted 4y ago[deleted]
- tialaramex 4y agoLiterally all that Copy does is it says after assignment the moved-from variable can still be used. So in this sense, sure, these semantics are "always used". But if you don't use the variable after assigning from it, you could also say the semantics aren't used in this case. Does that help? Copy does a lot less than many people think it does. If you're a low level person it's apparent this is because Copy types are just some bits and their meaning is literally in those bits, Copy the bits and you've copied the meaning. Thus, this "it still works after assignment" Copy behaviour is just how things would work naturally for such types. But Rust doesn't require programmers (and especially beginners) to grok that. It's possible to explain Copy semantics first in a way that's easier to grasp for people coming from, say, Java, but that's only half the picture because your students will soon need Move semantics which are different. Thus I recommend instead explaining Move semantics from the outset (which will be harder) and only introducing Copy as an optimisation. I think this might even be better for students coming from C++, because C++ move semantics are a horrible mess, so underscoring that Move is the default in Rust and it's fine to think of every assignment as Move in Rust will avoid them getting the idea that there must be secret magic somewhere, there isn't, C++ hacked these semantics in to a finished language which didn't previously have Move and that's why it's a mess. I'm less sure for people coming from low-level C. I can imagine if you're going to work with no_std on bare metal you might actually do just fine working almost entirely with Copy types and you probably need actual bona fide pointers (not just references) and so you end up needing to know what's "really" going on anyway. If you're no_std you don't have a String type anyway, nor do you have Box, and thus you can't write Box<str> either, although &str still works fine if you've burned some strings into your firmware or whatever.
- afdbcreid 4y agoThis isn't really something you usually encounter, but I have to bring this cute example: pub fn foo() -> impl FnOnce() { let non_copy: String = String::new(); let copy: i32 = 123; || { drop(non_copy); // Works drop(copy); // error[E0373] } } https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8407d92696525bf915875e9a8d09bff0 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- nyanpasu64 4y agoThis is another reason that I find (optional but first-class) explicit lambda captures (adding syntax for C++'s approach) is better, because it prevents this kind of implicit and surprising behavior.
- nynx 4y agoThere’s no implicit behavior here. It won’t compile if you don’t explicitly move the string into the closure.
- nyanpasu64 4y agoThe implicit behavior is that using a string moves it into the closure, and does compile even without `move || {...non_copy}` (which surprised even me), whereas using an integer takes a reference, and you have to "slap on `move` to make it work".
- afdbcreid 4y agoThe surprising part is that it does not compile with `Copy`, not that it compiles with `Copy`. The rules are that if a move a necessary then it is performed. The tricky part is that move is not necessary for `Copy` types, because you can capture a reference and still get an owned value.