2

I wrote this code to see what happens when I pass two strings to a function and return them back again:

fn main() {
    let mut s3 = String::from("hello");
    let mut s4 = String::from("wolrd");

    (s3, s4) = take_n_giveback(s3, s4);

    println!("{0} and {1}", s3, s4);
}

fn take_n_giveback(x: String, y: String) -> (String, String) {
    (x, y)
}

I am getting an error which is not helpful:

error[E0070]: invalid left-hand side expression
 --> src/main.rs:5:5
  |
5 |     (s3, s4) = take_n_giveback(s3, s4);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ left-hand of expression not valid

This operation works fine when passing single string and returning back.

fn main() {
    let mut s3 = String::from("hello");
    s3 = take_n_giveback(s3);
    println!("{0} ", s3);
}

fn take_n_giveback(x: String) -> (String) {
    x
}

What's wrong here? What is the meaning of the error and in what situations can be encountered in code?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Akshay Naik
  • 669
  • 1
  • 6
  • 22
  • when you bind a value, you must use let, for example, `let (s3, s4) = ...` – Stargateur Mar 17 '19 at 07:38
  • As i mentioned it works for single variable without using let keyword. then how that happens. – Akshay Naik Mar 17 '19 at 07:43
  • 1
    I think you need to read https://stackoverflow.com/a/47649554/9204 and https://stackoverflow.com/questions/42311825/what-is-exactly-lvalue-context-in-rust. – Alexey Romanov Mar 17 '19 at 07:47
  • the example seems little complicated to me since I am beginner, can you please explain what is fundamental difference between first(not working) and second example(working) in updated question. – Akshay Naik Mar 17 '19 at 07:55
  • Ok, added an answer. – Alexey Romanov Mar 17 '19 at 09:15
  • 1
    Please take the time to go back and re-read [*The Rust Programming Language*](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html), specifically the [section on variables](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html). Many introductory topics are covered by the book, such as introductory syntax. – Shepmaster Mar 17 '19 at 13:31

3 Answers3

10
let ... = ... // or let mut

is a binding: it creates new variables.

... = ...

(without let) is an assignment: it changes values of already bound variables (or their parts). See What's the semantic of assignment in Rust? for more explanation of this difference.

The left hand of a binding must be a pattern; the left hand of an assignment must be an place expression (formerly called lvalue). As explained in https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions

A place expression is an expression that represents a memory location. These expressions are paths which refer to local variables, static variables, dereferences (*expr), array indexing expressions (expr[expr]), field references (expr.f) and parenthesized place expressions. All other expressions are value expressions.

(s3, s4) is a pattern, but it isn't a place expression, because it isn't one of the above; it doesn't represent a fixed memory location.

This operation works fine when passing single string and returning back.

A variable name, like s3, can be used both a pattern and as a place expression. But with different meanings! When it's used in an assignment

let mut s3 = String::from("hello");
s3 = take_n_giveback(s3);

it represents the location of the variable declared by let mut s3, so there is only a single variable; but in

let mut s3 = String::from("hello");
let s3 = take_n_giveback(s3);

it creates a new variable called s3, so there are two different variables which happen to have the same name. This is why it works even if the first one is not mut.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • although I have understood how it works, can you explain how it gets a fixed location when we write let (s3, s4) = take_n_giveback(s3, s4)? is it like when we create binding using "let" rust allocates continuous memory location s3 and s4 and then it becomes lvalue? – Akshay Naik Mar 18 '19 at 07:45
  • 1
    `let` uses a pattern, not an lvalue, so `(s3, s4)` here doesn't need a fixed location at all. It does allocate memory locations (on stack) for `s3` and `s4`, but I don't know if they are guaranteed to follow each other. – Alexey Romanov Mar 18 '19 at 09:27
  • Maybe it'll also help to think of something which is a lvalue, but not a pattern: indexing or field acccess. So you can write `s[0] = x;` (for suitable types), but not `let s[0] = x;`. – Alexey Romanov Mar 18 '19 at 09:31
  • 1
    I've checked the reference and lvalues are now called place expressions. So the answer has been slightly updated. – Alexey Romanov Mar 18 '19 at 09:40
1

You're missing the let keyword; it should be a variable or a let statement:

let (s3, s4) = take_n_giveback(s3, s4);
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alexander Ejbekov
  • 5,594
  • 1
  • 26
  • 26
-1
fn main() {
    let s3 = String::from("hello");
    let s4 = String::from("wolrd");

    let (s3, s4) = take_n_giveback(s3, s4);

    println!("{0} and {1}", s3, s4);
}

fn take_n_giveback(x: String, y: String) -> (String, String) {
    (x, y)
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
pexeer
  • 685
  • 5
  • 8