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
Related
Since Iter's "all" fn takes type FnMut is it possible to update the element while checking for condition and short circuiting? Though I understand it is not supposed to, but what prevents it from updating the value?
fn main() {
let a = ["as", "zz"];
let mut iter = a.iter();
iter.all(|& (mut x)| {x = "cc"; true});
for z in a.iter(){
println!("{z}");
}
}
Above prints
as
zz
In above case why setting "x = cc" not work?
Or Alternatively why does Iter "all" method takes F of type FnMut and not Fn when it is not supposed to mutate but only validate for condition
x = "cc" does not change the value referred by x, instead it changes the reference itself (i.e. it makes it refer to another value), as evidenced by this example:
fn main() {
let a = ["as", "zz"];
let mut iter = a.iter();
iter.all(|&(mut x)| {
println!("before: {:?}", x as *const _);
x = "cc";
println!("after: {:?}", x as *const _);
true});
for z in a.iter(){
println!("{z}");
}
}
Playground
Note that this has nothing to do with the fact that the closure is FnMut, which only means that it may change any captured values as in:
fn main() {
let a = ["as", "zz"];
let mut count = 0;
let mut iter = a.iter();
iter.all(|&_x| {
count += 1; // This is possible because the closure is `FnMut`
true});
println!("Count: {count}");
for z in a.iter(){
println!("{z}");
}
}
Playground
Do not be mistaken! A function implementing FnMut means that it captures a receiving context mutably. It does not mean that its items may be modified from its original source.
let mut k = 0;
assert_eq!((10..15)..all(|x| {
k += 1;
x > k
}), true);
Given
let mut iter = a.iter();
we have an iterator the items of which are references to elements in vector a. And these references are immutable. In order to have an iterator which allows you to mutate the items in a vector, use iter_mut.
let iter = a.mut_iter();
iter.all(|x| { *x = "cc"; true});
for z in a.iter(){
println!("{z}");
}
This would still not require FnMut in particular, since it does not capture any context other than the items iterated on. The adaptor all (and many other iterator adaptors) were designed to constrain to FnMut so that the closures passed as their first parameter are allowed to keep and manipulate some external state for whichever logic they intend to do. Since the closure is called sequentially in the same thread, this is always memory safe.
See also:
What's the difference between placing "mut" before a variable name and after the ":"?
When does a closure implement Fn, FnMut and FnOnce?
This program is fine:
const EMPTY: i32 = 0;
const SIZEX: usize = 4;
const SIZEY: usize = 4;
fn main() {
let mut test = vec![[EMPTY; SIZEY]; SIZEX];
test[2][3] = 4;
// test.what();
println!("Hello, world:{}", test[2][3]);
}
When I uncomment the test.what() line to see the type of test, the compiler emits the error:
error[E0599]: no method named `what` found for struct `Vec<[i32; 4]>` in the current scope
--> src/main.rs:8:10
|
8 | test.what();
| ^^^^ method not found in `Vec<[i32; 4]>`
I was expecting that the type of test would be something like Vec<[[i32; SIZEY]; SIZEX]>. What am I missing?
The vec![t; N] macro will make a Vec<T> (where T is the type of t) of length N. The length isn't part of the type since it is dynamic.
I know that a Rust reference is much like a C pointer, and I think of Rust references as C pointers all the time. After some experiments and searching, I'm confused.
I'm familiar with C and I've read What's the difference between placing “mut” before a variable name and after the “:”?, which gives the following table:
// Rust C/C++
a: &T == const T* const a; // can't mutate either
mut a: &T == const T* a; // can't mutate what is pointed to
a: &mut T == T* const a; // can't mutate pointer
mut a: &mut T == T* a; // can mutate both
The post is upvoted so I assume that it is correct.
I wrote the following Rust code
fn main() {
let mut x = 10;
let x1 = &mut x;
let x2 = &x1;
let x3 = &x2;
***x3 = 20;
}
in the hope that it is equivalent to the following C code
int main() {
int x = 10;
int *const x1 = &x;
int *const *const x2 = &x1;
int *const *const *const x3 = &x2;
***x3 = 20;
return 0;
}
The Rust code doesn't compile:
error[E0594]: cannot assign to `***x3` which is behind a `&` reference
--> src/main.rs:6:5
|
6 | ***x3 = 20;
| ^^^^^^^^^^ cannot assign
What's wrong here?
Strangely enough, the following code compiles!
fn main() {
let mut x = 10;
let mut x1 = &mut x;
let mut x2 = &mut x1;
let mut x3 = &mut x2;
***x3 = 20;
}
Why should let mut x1/2/3 be used instead of just let x1/2/3? I think of let x1 = &mut x as a constant pointer pointing to a mutable variable x, but it doesn't seem to be right in Rust. Is that Stack Overflow post inaccurate or I misunderstand it?
There are a few differences between Rust and C which do not show up in the table you have referenced in your question.
In Rust, mutability is a property of the binding rather than of the type.
Rust has strict aliasing rules, such that you cannot have more than one mutable reference to any variable at one time.
Your question (simplified) is: why can't I have a non mutable reference to a mutable variable, and mutate that variable through it. However, if you could do that you could also have two references which could be used to modify the variable, like this:
let mut x = 10;
let x1 = &mut x;
let x2 = &x1; // Non mutable reference to x1, ok
let x3 = &x1; // Another non mutable reference to x1, ok
**x2 = 20; // uhoh, now I can mutate 'x' via two references ... !
**x3 = 30;
Regarding your C equivalent to the given Rust code - you have not translated it according to the table. Consider this:
let x2 = &x1;
From the table in the answer you quoted:
a: &T == const T* const a; // Can't modify either
In this case, T would be const int*. So, it would be:
const int* const* const x2 = &x1;
Your whole program would be:
int main() {
// let mut x = 10;
int x = 10;
// let x1 = &mut x;
// a: &mut T == T* const a with T=int
int* const x1 = &x;
// let x2 = &x1;
// a: &T == const T* const a with T = int* const
const int* const* const x2 = (const int* const* const) &x1;
// let x3 = &x2;
// a: &T == const T* const a with T = const int* const* const
const const int* const* const* const x3 = &x2;
***x3 = 20;
return 0;
}
Note that a cast is needed to avoid warnings in the assignment of x2. This is an important clue: we are effectively adding const-ness to the pointed to object.
If you try to compile that you get:
t.c: In function ‘main’:
t.c:17:11: error: assignment of read-only location ‘***x3’
***x3 = 20;
^
In Rust it's a bit different. The & sign means reference to something and the * sign means dereference something. If my memory serves me correct, the C/C++ syntax makes use of the '->' symbol (Which is a way of dereferencing), which doesn't appear in Rust.
The hard part in Rust is keeping track of what's being borrowed by whom. Once you've understood how that works, and understanding what datatypes make use of what (Vec! makes use of the heap for instance): You should be pretty proficient in Rust!
I am trying to use associated constants as a condition in an if-expression to initialize another constant. I think that this should work, as I can use the associated constants directly to initialize some other constant, so it is applicable in a const context and the if expression does not depend on any other values.
trait C {
const c: i32;
}
trait StaticAssert<T1: C, T2: C> {
const canUseAssociatedConst: i32 = T1::c;
const canCompareAssociatedConst: bool = T1::c == T2::c;
const check: i32 = if T1::c == T2::c { 1 } else { 0 };
}
When I compile this, I get an error:
error[E0019]: constant contains unimplemented expression type
--> src/lib.rs:9:24
|
9 | const check: i32 = if T1::c == T2::c { 1 } else { 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I am not sure what the compiler wants to tell me. I've added i32 suffixes to enforce that the literals are actually i32 values to prevent any issues from different types in the branches, but this did not help either.
As far as I know, if and others are not (yet) supported in const contexts.
However, often you can acchieve a similar effect along the lines of the following:
trait C {
const c: i32;
}
trait StaticAssert<T1:C, T2:C> {
const canUseAssociatedConst: i32 = T1::c;
const canCompareAssociatedConst: bool = T1::c == T2::c;
const check: i32 = (T1::c == T2::c) as i32;
}
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.