I installed Rust 1.13 and tried:
fn main() {
let x: u32;
x = 10; // no error?
}
When I compiled this file there's some warnings, but there's no error. As I'm not declaring x as mut, shouldn't x = 10; cause an error?
What you have written is identical to:
let x: u32 = 10;
The compiler will not permit you to mutate it thereafter:
let x: u32;
x = 10;
x = 0; // Error: re-assignment of immutable variable `x`
Note that it is a compiler error if you try to use an uninitialized variable:
let x: u32;
println!("{}", x); // Error: use of possibly uninitialized variable: `x`
This feature can be pretty useful if you want to initialize the variable differently based on runtime conditions. A naive example:
let x: u32;
if condition {
x = 1;
} else if other_condition {
x = 10;
} else {
x = 100;
}
But still it will still be an error if there is a possibility that it isn't initialized:
let x: u32;
if condition {
x = 1;
} else if other_condition {
x = 10;
} // no else
println!("{:?}", x); // Error: use of possibly uninitialized variable: `x`
As mentioned, this is not mutation, but deferred initialization:
mutation is about changing the value of an existing variable,
deferred initialization is about declaring a variable at one point, and initializing it later.
The Rust compiler tracks whether a variable has a value at compile-time, so unlike C there is no risk of accidentally using an uninitialized variable (or unlike C++, a variable that was moved from).
The most important reason for using deferred initialization is scope.
fn main() {
let x;
let mut v = vec!();
{
x = 2;
v.push(&x);
}
println!("{:?}", v);
}
In Rust, the borrow-checker will validate that a reference cannot outlive the value it refers to, preventing dangling references.
This means that v.push(&x) requires that x lives longer than v, and therefore be declared before v.
The need for it does not crop up often, but when it does other solutions would require run-time checks.
Related
This question already has answers here:
What is the difference between immutable and const variables in Rust?
(7 answers)
Closed 5 days ago.
what's different let and const in rust ?
for example if use let without mut, is not possible change value
fn main(){
let MYINT = 1;
MYINT = 2; // error
const MYINT_ = 1;
MYINT_ = 1; //error
}
const indicates a compile-time constant which means it can only be set to a value that can be computed at compile time, e.g. a literal or the result of calling a const fn.
let (without mut) indicates a run-time constant which means it can be initialized from the result of any valid expression.
As to the difference in behavior of the two, you can use const variables in contexts that require compile-time evaluation, such as the length of arrays. You cannot use let variables in these contexts. For example:
fn main() {
const x: usize = 5;
let x_arr = [0i32; x];
}
This compiles successfully, and x_arr has type [i32; 5].
fn main() {
let x: usize = 5;
let x_arr = [0; x]; // E0435
}
This fails to compile; x cannot be used as an array length as the value must be known at compile time, and x is not a compile-time constant.
error[E0435]: attempt to use a non-constant value in a constant
I ran across a weird case where Rust was telling me that I didn't need mut on a variable, even though I was clearly mutating it! After some fiddling around I managed to get the following code to compile which just looks broken.
fn main() {
let x: u32;
loop {
match 1 {
1 => {}
_ => {
x = 5;
break;
}
}
}
dbg!(x);
}
Running it (playground) results in some kind of crash. What's going on here? Is this a compiler bug? I tried it on nightly and it still times out.
This also compiles:
let x: u32;
if false {
x = 5;
}
As well as:
let x: u32;
x = 5;
As #trentcl mentions, this is deferred initialization, which is why it is accepted.
Consider the following (contrived) way to increment x by 9.
fn main() {
let mut x = 0;
let mut f = || {
x += 4;
};
let _g = || {
f();
x += 5;
};
}
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> x.rs:6:12
|
3 | let mut f = || {
| -- first mutable borrow occurs here
4 | x += 4;
| - first borrow occurs due to use of `x` in closure
5 | };
6 | let _g = || {
| ^^ second mutable borrow occurs here
7 | f();
| - first borrow later captured here by closure
8 | x += 5;
| - second borrow occurs due to use of `x` in closure
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
So, it does not work. How to make an algorithm like the above that would modify a variable in a closure and call another closure from it that also modifies the variable?
In traditional languages it's easy. What to do in Rust?
The accepted answer is the most idiomatic approach, but there is also an alternative that is useful in situations when the additional argument doesn't work, for example when you need to pass the closure to third-party code that will call it without arguments. In that case you can use a Cell, a form of interior mutability:
use std::cell::Cell;
fn main() {
let x = Cell::new(0);
let f = || {
x.set(x.get() + 4);
};
let g = || {
f();
x.set(x.get() + 5);
};
f();
g();
assert_eq!(x.get(), 13);
}
By design, closures have to enclose external objects that are used in it upon creation, in our case closures have to borrow the external x object. So as compiler explained to you, upon creation of the closure f it mutably borrows x, and you can't borrow it once again when you create closure g.
In order to compile it you can't enclose any external object that you want to change. Instead you can directly pass the objects as a closure's argument (arguably it's even more readable). This way you describe that a closure accepts some object of some type, but you don't yet use/pass any actual object. This object will be borrowed only when you call the closure.
fn main() {
let mut x = 0;
let f = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
*local_x += 4;
};
let _g = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
f(local_x);
*local_x += 5;
};
_g(&mut x); // finally we borrow `x`, and this borrow will later move to `f`,
// so no simultaneous borrowing.
println!("{}", x); // 9
}
To expand on Alex Larionov's answer: you should think of a closure as a callable structure, anything you capture is set as a field of the structure, then implicitly dereferenced inside the function body. The way those fields are used is also what determines whether the closure is Fn, FnMut or FnOnce, basically whether the method would take &self, &mut self or self if it were written longhand.
Here
fn main() {
let mut x = 0;
let mut f = || {
x += 4;
};
// ...
}
essentially translates to:
struct F<'a> { x: &'a mut u32 }
impl F<'_> {
fn call(&mut self) {
*self.x += 4
}
}
fn main() {
let mut x = 0;
let mut f = F { x: &mut x };
// ...
}
from this, you can see that as soon as f is created, x is mutably borrowed, with all that implies.
And with this partial desugaring, we can see essentially the same error: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e129f19f25dc61de8a6f42cdca1f67b5
If we use the relevant unstable features on nightly we can get just a bit closer. That is pretty much what rustc does for us under the hood.
I'm still new with Rust but I have doubts about how mutability works for fields in structs. Specifically how we can modify fields that originally were immutable. For example:
struct Point {
x: isize,
y: isize,
}
impl Point {
fn new(x: isize, y: isize) -> Self {
Self { x, y }
}
fn set_x(&mut self, new_x: isize) {
self.x = new_x;
}
}
struct Line {
p: Point,
q: Point,
}
impl Line {
fn new(p: Point, q: Point) -> Self {
Self { p, q }
}
fn set_x_in_p(&mut self, new_x: isize) {
self.p.set_x(new_x);
}
}
fn main() {
// Both x and y are immutable
let p = Point::new(0, 0);
let q = Point::new(1, 1);
// Line IS mutable
let mut line = Line::new(p, q);
// Modifying point p (originally immutable) with a new x
line.set_x_in_p(999);
}
Instead with references we cannot
let x = 3;
let y = &mut x; // does not work because x originally is immutable
So, how does it work? Thanks.
In your example, p and q are indeed immutable, but then you move them into a Line instance with the constructor, since they're passed by value and don't implement Copy to enable implicit copying. This means that the original bindings (p and q) are no longer valid (the compiler will prevent you from using them) and the values are instead accessible only via the mutable line binding, which allows mutating its members. Essentially, it's not the values that are mutable, but rather their bindings. For example, the following code can be used to re-bind a value to change its mutability:
let x = String::from("hello world"); // using String as an example of a non-Copy type
let mut x = x; // this shadows the previous binding with a mutable one of the same name
x.make_ascii_uppercase(); // we can now mutate the string
let x = x; // shadow the mutable binding with an immutable one
println!("Result: {}", x);
This example works because we have direct control over the value, and can move/bind it as desired. Introducing references would limit what could be done - for example, the following examples wouldn't work:
let x = String::from("hello world");
let x_ref = &x; // create an immutable reference to x
let mut x_mut = x; // error - we can't move x while it's borrowed
let x_mut_ref = &mut x; // error - we can't create a mutable reference while any other references exist
I'd recommend reading the ownership and moves page of Rust by example, which explains it pretty well.
When you are declaring x, you are specifying it as immutable with the let instead of let mut. When you then declare y and initialize it as &mut x you are attempting to borrow x. In Rust, the one thing you can never do is simultaneously have shared ownership AND mutability.
Check out what Niko Matsakis has to say about ownership.
I can't figure out what does this error in Rust mean:
error: lifetime of non-lvalue is too short to guarantee its contents can be safely reborrowed
What's a non-lvalue? (I suspect is not a right value).
I want to understand what the errror means and to be able to modify "objects" from a vector of mutable references.
This is a minimum test case to produce the error. I insert in a vector a mutable reference to a struct, and then I try to modify the pointed struct.
struct Point {
x: uint,
y: uint
}
fn main() {
let mut p = Point { x: 0, y: 0};
p.x += 1; // OK, p owns the point
let mut v: Vec<&mut Point> = Vec::new();
v.push(&mut p);
// p.x += 1 // FAIL (expected), v has borrowed the point
let p1:&mut Point = *v.get_mut(0); // ERROR, lifetime of non-lvalue...
// never reached this line
// p1.x += 1;
}
Let's go over what you're trying to do here:
let p1:&mut Point = *v.get_mut(0);
*v.get_mut(0) returns a mutable reference to first the mutable reference in the vector, then dereferences it. If this compiled, you would end up with two mutable references to the same object: one in the vector, the other in the p1 variable. Rust is rejecting this because it's not safe.
By far the best solution is to make the vector the owner of your Point objects. ie. use a Vec<Point> instead of a Vec<&mut Point.
If you need something more complicated, you can use a RefCell for dynamically checked borrowing:
use std::cell::RefCell;
struct Point {
x: uint,
y: uint
}
fn main() {
let p = RefCell::new(Point { x: 0, y: 0});
p.borrow_mut().x += 1;
let mut v: Vec<&RefCell<Point>> = Vec::new();
v.push(&p);
let p1 = v.get(0);
p1.borrow_mut().x += 1;
p.borrow_mut().x += 1;
}