Confusion About The Book Statement Of Ownership

It is starting with the ownership chapter on the book.

"What if we want to let a function use a value but not take ownership?"

This mean i can use a value from var without transfer an ownership.

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1); // move ownership of s1 > s2?

    println!("The length of '{}' is {}.", s2, len);

    println!("{s1}");
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() returns the length of a String

    (s, length)
}

If this code only let a func to use value and not transfer the ownership, I should can declare the s1 in the bottom of s2. Why i got an error?

   |
2  |     let s1 = String::from("hello");
   |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3  |
4  |     let (s2, len) = calculate_length(s1);
   |                                      -- value moved here
...
8  |     println!("{s1}");
   |               ^^^^ value borrowed here after move
   |
note: consider changing this parameter type in function `calculate_length` to borrow instead if owning the value isn't necessary
  --> src/main.rs:11:24
   |
11 | fn calculate_length(s: String) -> (String, usize) {
   |    ----------------    ^^^^^^ this parameter takes ownership of the value
   |    |
   |    in this function
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
   |
4  |     let (s2, len) = calculate_length(s1.clone());
   |                                        ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to previous error

It is unclear what you are asking. You are moving s1, so naturally you can't use it after the call.

Please consult someone fluent in English to help phrase your question, because your post is not meaningful as it is currently standing.

2 Likes

In this example, ownership of s1 is transfered to calculate_length (which calls it s), then calculate_length transfers s back in the return value (and it's stored in s2). You can't use s1 after the call to calculate_length because you when you move it by transferring ownership to calculate_length, it becomes uninitialized (since String is not Copy).

The version of the example where ownership is not transferred is on the next page.

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);
    // IMPORTANT               ^

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    // IMPORTANT       ^
    s.len()
}

Here s1 is still usable as it was never moved from / ownership was not given away.

2 Likes

My question is the book said that the code is only take the value from var without move the ownership. As you said, the code is moving the ownership too.

While this works, taking ownership and then returning ownership with every function is a bit tedious. What if we want to let a function use a value but not take ownership? It’s quite annoying that anything we pass in also needs to be passed back if we want to use it again, in addition to any data resulting from the body of the function that we might want to return as well. Rust does let us return multiple values using a tuple, as shown in Listing 4-5.

It means that the code in question is not on the page I'm reading, thank you. I think example 4-2 which is a way to use value without transfer ownership. I read it again, it is about returning multiple value with tuples.

The book shows a trick/pattern for transferring ownership twice: once from the caller the callee, and then back from the callee to the caller. That effectively gives back ownership of the value to the caller again, but in a different variable.

However, the concrete variable s1 is still moved. You get back the value in s2, not in s1, so of course you can't use s1.

It's not enough to read the book as prose. It's not a novel or a short story. Don't take the text literally, especially not analogies. You have to understand that higher-level constructs and techniques are not something the compiler understands (nor should it).

The book shows you this trick to get a feeling for how tedious this constant transfer of ownership would be without borrowing, in order to motivate borrowing. It shows that with moving only, it's still possible to effectively avoid permanently transferring ownership. But the compiler doesn't go "ha, is this guy trying to do the move-in-and-out trick?" – it only analyzes concrete code according to the concrete rules of the language. And those rules say that once you move from a variable, you can't use it anymore.

3 Likes

Got it thanks.

No, it is not. The tuple is only a device to help return the moved value as well as the actually useful value the function calculated (the length). The essence of that example is the ownership structure.

1 Like

Very clear, I miss the essence of this example.
Again, i really appreciate your answer.