Hacker News new | ask | show | jobs
by saagarjha 2447 days ago
It depends: if add1 borrows the value then this code is fine. If add1 takes ownership of the parameter then no, this will not work.
2 comments

In Rust, you can tell whether something is borrowed from the call site, without having to look at the function being called. If a function takes a reference (`&T`), then the caller must pass a reference. There's no implicit by-referencing as in C++'s `&` types.

(I realize you, saagarjha, probably know this already -- this is more a note to other readers.)

What do you mean by "will not work"? Will it not compile? And how do we write that with add1 taking ownership vs. not?
You'll get a compile-time error explaining the situation, yes, including documentation links and possible fixes. The differences between the versions are minimal.

    fn add1(x: Thing) -> Thing {
      x + 1
    }

    fn add2(x: &Thing) -> Thing {
      x + 2
    }
This works on the assumption that whatever "+ 1" really means doesn't itself need ownership of x, e.g. because it mutates it. If it does, then you'll get an error. Or you could do this:

    fn add3(x: &mut Thing) {
      x.add(3)
    }
Which, presumably, modifies x instead of returning a copy. It's your job to make sure it makes sense to do that, but Rust has another trick in its pockets:

    let y = add1(x);  // Works; takes ownership.
    let z = add2(&y);  // Works, and doesn't take ownership.
    let zz = add2(&y);  // So you can do it twice. (Y tho?)
    let h = add3(&y);  // Compile-time error!
    let hh = add3(&mut y);  // Works. You need to specify that you're fine with y being mutated.
Consider the following code:

  fn add1(x: Vec<i32>) -> Vec<i32> { // Takes ownership
      return x.iter().map(|&x| x + 1).collect::<Vec<_>>();
  }
  
  fn add1_borrow(x: &Vec<i32>) -> Vec<i32> {
      return x.iter().map(|&x| x + 1).collect::<Vec<_>>();
  }
The first will take ownership of the thing you pass in, so you can't do something like this:

  let x = vec![1, 2, 3];
  let y = add1(x);
  let z = add1(x); // error[E0382]: use of moved value: `x`
but the follow code is OK, because the function just borrows the value instead of owning (and destroying) it:

  let x = vec![1, 2, 3];
  let y = add1_borrow(&x);
  let z = add1_borrow(&x);
This is legal too:

  let x = vec![1, 2, 3];
  let y = add1_borrow(&x);
  let z = add1(x);
but as before you can't use x after this.

(Note, I chose Vec here because Vec does not implement Copy: if I used i32 it would just copy the value in the non-borrowing case, which would make the code fine as no ownership would be transferred.)

It won't compile, because the compiler keeps track of where you moved the value and notices that x now has no value. If you want the function to borrow the argument instead, make it take a reference, &x.