News for hackers. Stuff that matters.
Re:Rust for Professionals by coderenegade on Monday January 02, 2023 @10:23PM (#34227181)
I found the copy/move situation in Rust to be far less intuitive than in C++. In C++, move semantics are obvious because they rely on std::move and the && operator, whereas in Rust, similar behavior seemed to depend on the object type. Even more confusingly, Rust has its own move operator as well, despite destructive move being the default behavior for assignment.

I found it frustrating enough that I put the language down and just went back to using C++.

Re:Rust for Professionals by scotty79 on Monday January 02, 2023 @10:46PM (#34227356)
> In C++, move semantics are obvious because ...

In Rust it's also obvious because every = is a move. Confusion comes from tutorials pretending for too long that it's not.

> whereas in Rust, similar behavior seemed to depend on the object type.

It's best to think of that as an exception to the rule created specifically for numbers and other similar small, cheaply copied things.

If you need to move `a` but `a` has a trait Copy then you copy instead.

> Rust has its own move operator as well, despite destructive move being the default behavior for assignment.

I don't think that's true? Rust has a `move` keyword but it's a part of closure definition that makes it take all the variables from its environment by move, even if it doesn't need it. Unless you are talking about something else...

Re:Rust for Professionals by legerdemain on Monday January 02, 2023 @11:31PM (#34227692)

  > In Rust it's also obvious because every = is a move.
False, depending on the presence of an explicit type annotation on the left-hand side, an = triggers either a move or a reborrow.
Re:Rust for Professionals by scotty79 on Tuesday January 03, 2023 @12:02AM (#34227892)
How does it look like? Do you mean this? https://haibane-tenshi.github.io/rust-reborrowing/

If you assume = always moves you can just never use automatic reborrowing (do &mut * instead) and you'll loose nothing.

I don't think that's a very common pattern, fully optional and it can be just treated as another exception to the rule, just like Copy types.

So that's total of two exceptions. Way easier to be tackled individually when the time comes than assuming = means something else, or something complex from the start.

Re:Rust for Professionals by legerdemain on Tuesday January 03, 2023 @01:50AM (#34228602)
I've posted similar minimal examples before. The first one triggers a move (and then fails to compile), while the second one triggers a reborrow. Sure, you can explicitly write out every instance of reborrowing with `&mut *`. That would require you to understand every instance that triggers it, and would also be unbelievably noisy, since automatic dereferencing and automatic reborrowing are actually incredibly common.

  > pub fn main() {
  >     let mut x = String::from("moo");
  >     let y = &mut x;
  >     let z = y;
  >     println!("{:?}", y)
  > }
  > 
  > pub fn main() {
  >     let mut x = String::from("moo");
  >     let y = &mut x;
  >     let z: &mut _ = y;
  >     println!("{:?}", y)
  > }
Re:Rust for Professionals by scotty79 on Tuesday January 03, 2023 @09:47AM (#34231987)
Ok. So that's just triggering auto-insertion of &mut *

It's completely optional fearure. You don't have to rely on it. Like auto-insertion of semicolons in JS maybe it's better if you don't. Although likelihood of this biting you is way lower than in case of relying on semicolons autoinsertion.

Re:Rust for Professionals by legerdemain on Tuesday January 03, 2023 @04:47PM (#34238563)
It's "completely optional" in the sense that in several years of reading other people's Rust code, I have never seen anyone explicitly use it throughout a code base. It is utterly ubiquitous, rarely explained, and poorly (if at all) understood by a large majority of Rust users I've mentioned it to.

My claim is that for most Rust users, understanding the borrow checker is limited to a few heuristics that seem to work for them 80% of the time, and throwing up their hands and declaring an approach unworkable the remaining 20% of the time. To a first approximation, no one can mentally model the borrow checker beyond extremely simple examples of 4-5 lines of code.

Re:Rust for Professionals by puffoflogic on Tuesday January 03, 2023 @01:53AM (#34228620)
There's a way of thinking of `Copy` which makes it not an exception: `Copy` variables/places are simply not rendered invalid/uninitialized when they are the source of a move operation, unlike non-`Copy` sources. They're still moved from bitwise like all other rust values!
Re:Rust for Professionals by scotty79 on Tuesday January 03, 2023 @09:49AM (#34232017)
I like it!
Re:Rust for Professionals by saghm on Tuesday January 03, 2023 @08:35AM (#34231091)
I'd be pretty surprised if the C++ move semantics were considered more intuitive by someone not familiar with either and then learning both for the same time. Rust's semantics do depend on the type in that anything that implements Copy will be implicitly copied rather than moved, but I'm sure I understand why that's unintuitive. I'm also not sure what you mean by Rust having it's own move operator; the only thing I can think of is the `move` keyword used for indicating that closures should capture by move rather than by reference, but it's not used outside of closures as far as I'm aware, so I suspect that the confusion here is more due to expecting things to behave like C++ rather than the Rust semantics being "unintuitive" in a vacuum.

At a high level, I think the most unintuitive part of moving in C++ compared to Rust is that it can silently degrade into a copy without anything indicating it. In Rust, trying to use a value after it's been moved will give you a compiler error, at which point you can reconsider whether you do in fact want to explicitly copy or if you made a mistake or need to refactor. In C++, the only way I'm aware of to verify whether a value is actually moved or not is to use a debugger. The benefit for requiring explicit copies is similar to having bindings be immutable by default and requiring an explicit `mut` annotation; if you start out enforcing the constraint that things should be moved or immutable, fixing it later if you find out it won't work that way only requires adding `.clone()` or `mut` in one place. On the other hand, if you start out with implicit copies or mutability by default and then want to change it later, it can be a lot more work to refactor the places where the variable is used to not violate this, and it may not even be possible in some cases.

Re:Rust for Professionals by bear24rw on Wednesday January 04, 2023 @01:10PM (#34249178)
> In C++, the only way I'm aware of to verify whether a value is actually moved or not is to use a debugger.

Not as simple as if there was a proper borrow checker but its sort of possible:

https://awesomekling.github.io/Catching-use-after-move-bugs-...