How to do in-place assignments in Rust?

2019-09-20 11:33发布

问题:

In C++, I can do in-place assignments that increment the variable var while printing it out to the screen:

int var = 5;
std::cout << ( var += 1 ) << std::endl;

I am doing this because Rust does not have the in/decrement operators ++ & --. I tried it in Rust:

let mut counter = 0;
println!("{}", (counter += 1));

It is saying:

error[E0277]: the trait bound `(): std::fmt::Display` is not satisfied
 --> src/main.rs:3:20
  |
3 |     println!("{}", (counter += 1));
  |                    ^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `()`
  |
  = note: `()` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
  = note: required by `std::fmt::Display::fmt`

回答1:

The assignment operators in Rust don't evaluate to the assigned value; they evaluate to (). Therefore, you need to read the variable after the assignment to obtain the new value. You can do this with a block, since blocks are also expressions.

let mut counter = 0;
println!("{}", { counter += 1; counter });

If you're going to do this often, consider defining a macro:

macro_rules! inc {
    ($e:expr) => {
        { $e += 1; $e }
    }
}

let mut counter = 0;
println!("{}", inc!(counter));


回答2:

The shortest solution is to return the value:

let mut counter = 0;
println!("{}", {counter += 1; counter});

But honestly, I would say that this isn't idiomatic Rust, and you might as well just write it on another line:

let mut counter = 0;
counter += 1;
println!("{}", counter);

The longer answer is that you cannot get the value of the assignment. The trait for AddAssign is defined as

pub trait AddAssign<Rhs = Self> {
    fn add_assign(&mut self, Rhs);
}

Notably, there is no value returned from the method. Rust does not have a useful value for the result of an assignment because there isn't one, in general. Ownership semantics make it complicated to figure out what the "return value" of something like let x = String::new(); should be. If it were a String, then what would have happened to the ownership held by x?

Since all expressions return some type, assignments return the empty tuple (()), also known as the unit type.

In this case, the value is an integer, which implements the Copy trait. That means that you can trivially return a copy from the block.

The same solution would "work" for a String, until you tried to use it afterwards:

let mut string = String::new();
println!("{}", {string += "a"; string});
println!("{}", string); // use of moved value: `string`

Which could be fixed by returning a reference:

println!("{}", {string += "a"; &string});

But at this point, there's not really any "saving" to be done.



标签: rust