Changing the variables in Rust [duplicate] - rust

In Rust, in order to change the value of a mutable variable, what is the difference in let x = 12 or x = 12 in the following sample code?
fn main() {
let mut x: i32 = 8;
{
println!("{}", x);
let x = 12; // what if change to x = 12
println!("{}", x);
}
println!("{}", x);
let x = 42;
println!("{}", x);
}
The output is 8, 12, 8, 42. If I change let x = 12 to x = 12 ...
fn main() {
let mut x: i32 = 8;
{
println!("{}", x);
x = 12;
println!("{}", x);
}
println!("{}", x);
let x = 42;
println!("{}", x);
}
The output is 8, 12, 12, 42.
I understand that Rust uses let to do variable binding, so the let x = 12 is a variable rebinding and the binding is only valid inside a scope. But how to explain the functionality of x = 12 and the corresponding scope? Is that a type of variable binding?

The second let x introduces a second binding that shadows the first one for the rest of the block. That is, there are two variables named x, but you can only access the second one within the block statement after the let x = 12; statement. These two variables don't need to have the same type!
Then, after the block statement, the second x is out of scope, so you access the first x again.
However, if you write x = 12; instead, that's an assignment expression: the value in x is overwritten. This doesn't introduce a new variable, so the type of the value being assigned must be compatible with the variable's type.
This difference is important if you write a loop. For example, consider this function:
fn fibonacci(mut n: u32) -> u64 {
if n == 0 {
return 1;
}
let mut a = 1;
let mut b = 1;
loop {
if n == 1 {
return b;
}
let next = a + b;
a = b;
b = next;
n -= 1;
}
}
This function reassigns variables, so that each iteration of the loop can operate on the values assigned on the preceding iteration.
However, you might be tempted to write the loop like this:
loop {
if n == 1 {
return b;
}
let (a, b) = (b, a + b);
n -= 1;
}
This doesn't work, because the let statement introduces new variables, and these variables will go out of scope before the next iteration begins. On the next iteration, (b, a + b) will still use the original values.

Related

How do you alter a variable's value from within a for loop? [duplicate]

In Rust, in order to change the value of a mutable variable, what is the difference in let x = 12 or x = 12 in the following sample code?
fn main() {
let mut x: i32 = 8;
{
println!("{}", x);
let x = 12; // what if change to x = 12
println!("{}", x);
}
println!("{}", x);
let x = 42;
println!("{}", x);
}
The output is 8, 12, 8, 42. If I change let x = 12 to x = 12 ...
fn main() {
let mut x: i32 = 8;
{
println!("{}", x);
x = 12;
println!("{}", x);
}
println!("{}", x);
let x = 42;
println!("{}", x);
}
The output is 8, 12, 12, 42.
I understand that Rust uses let to do variable binding, so the let x = 12 is a variable rebinding and the binding is only valid inside a scope. But how to explain the functionality of x = 12 and the corresponding scope? Is that a type of variable binding?
The second let x introduces a second binding that shadows the first one for the rest of the block. That is, there are two variables named x, but you can only access the second one within the block statement after the let x = 12; statement. These two variables don't need to have the same type!
Then, after the block statement, the second x is out of scope, so you access the first x again.
However, if you write x = 12; instead, that's an assignment expression: the value in x is overwritten. This doesn't introduce a new variable, so the type of the value being assigned must be compatible with the variable's type.
This difference is important if you write a loop. For example, consider this function:
fn fibonacci(mut n: u32) -> u64 {
if n == 0 {
return 1;
}
let mut a = 1;
let mut b = 1;
loop {
if n == 1 {
return b;
}
let next = a + b;
a = b;
b = next;
n -= 1;
}
}
This function reassigns variables, so that each iteration of the loop can operate on the values assigned on the preceding iteration.
However, you might be tempted to write the loop like this:
loop {
if n == 1 {
return b;
}
let (a, b) = (b, a + b);
n -= 1;
}
This doesn't work, because the let statement introduces new variables, and these variables will go out of scope before the next iteration begins. On the next iteration, (b, a + b) will still use the original values.

What is the proper way to use the `cfg!` macro to choose between multiple implementations?

I have specified a few features inside Cargo.toml:
[features]
complex = []
simple = []
When I build my project I use cargo build --features="complex" or simple.
In some functions, I want to return a value based on which feature is used:
fn test() -> u32 {
let x: u32 = 3;
if cfg!(feature = "complex") {
let y: u32 = 2;
x + y
}
if cfg!(feature = "simple") {
let y: u32 = 1;
x + y
}
}
But this doesn't work as it tries to evaluate both expressions. What is the proper way to use the cfg! macro in my case?
The documentation for cfg! states:
Boolean evaluation of configuration flags.
That means that cfg!(...) is replaced with a Boolean (true / false). Your code would look something like this, after it's expanded:
fn test() -> u32 {
let x = 3;
if true {
let y = 2;
x + y
}
if true {
let y = 1;
x + y
}
}
The easiest solution is to add an else:
fn test() -> u32 {
let x = 3;
if cfg!(feature = "complex") {
let y = 2;
x + y
} else {
let y = 1;
x + y
}
}
You can also use the attribute form of cfg. In this case, the attribute can prevent the entire next expression from being compiled:
fn test() -> u32 {
let x: u32 = 3;
#[cfg(feature = "complex")]
{
let y: u32 = 2;
x + y
}
#[cfg(feature = "simple")]
{
let y: u32 = 1;
x + y
}
}
as it tries to evaluate both expressions.
No, it doesn't. Evaluation occurs at run-time, and this code cannot even be compiled.
See also:
Is it possible to conditionally compile a code block inside a function?
Example of how to use Conditional Compilation Macros in Rust
How many lines are covered by the Rust conditional compilation attribute?

Why does this Rust program ignore immutability

I have the following Rust program and I expect it to result in an compilation error since x is reassigned later. But it complies and gives output. Why?
fn main() {
let (x, y) = (1, 3);
println!("X is {} and Y is {}", x, y);
let x: i32 = 565;
println!("Now X is {}", x);
}
Rust actually lets you shadow other variables in a block, so let x: i32 = 565; is defining a new variable x that shadows the x defined earlier with let (x,y) = (1,3);. Note that you could even have redefined x to have a different type since the second x is a whole new variable!
fn main(){
let x = 1;
println!("Now X is {}",x);
let x = "hi";
println!("Now X is {}",x);
}
This reddit thread goes into more detail about why this is useful. The two things that are mentioned that seem interesting are:
For operations which take ownership of the variable, but return another variable of the same type, it sometimes "looks nice" to redefine the returned variable to have the same name. From here:
let iter = vec.into_iter();
let iter = modify(iter);
let iter = double(iter);
Or to make a variable immutable:
let mut x;
// Code where `x` is mutable
let x = x;
// Code where `x` is immutable

Is it possible to query the stack space of a closure?

fn test<T>(t: T) {
println!("size of: {}", std::mem::size_of::<T>());
}
fn main() {
let f = || {
let i1: i32 = 0;
let i2: i32 = 0;
let i3: i32 = 0;
i1 + i2 + i3
};
test(f) // prints "size of: 0"
}
A non move closure always seems to prints that the size is 0 probably because it is just inlined.
A move closure gets bigger with every variable that it captures but I wonder if it is possible to query the stack space that a closure requires?
Update:
I want to create something like a Coroutine<F>. I need to know the size of F for it to be executed. I currently allocate 1mb per coroutine which is way too much. So I was wondering if it would be possible to determine the actual size that I need to allocate.
I misunderstood the question, so the following text does not really answer OP's question!
Yes, you can measure the size of a closure. The sizes are just a bit confusing sometimes. Let's test all kinds of closures:
let constant = || 27;
let no_capture = |x: i32| 27 + x;
let a = vec![3];
let immut_capture = || a[0] + 27;
let immut_capture_arg = |x: i32| a[0] + x;
let mut b = vec![3];
let mut c = vec![3];
let mut_capture = || { b[0] += 27; b[0] };
let mut_capture_arg = |x: i32| { c[0] += x; c[0] };
let mut d = vec![3];
let mut e = vec![3];
let move_capture = move || { d[0] += 27; d.into_boxed_slice() };
let move_capture_arg = move |x: i32| { e[0] += x; e.into_boxed_slice() };
When I print their sizes with std::mem::size_of_val (which is roughly equivalent to your hand-written test() function), I get the following results:
constant -> 0
no_capture -> 0
immut_capture -> 8
immut_capture_arg -> 8
mut_capture -> 8
mut_capture_arg -> 8
move_capture -> 24
move_capture_arg -> 24
You can try it yourself with this code on playground.
So whats up with those results?
A closure is a type that saves its environment – either by reference or by value. Mutable and immutable reference to sized data have the same size, namely size_of::<usize>(), usually 8. This explains the size of the closures that capture the outer variable by reference.
The move closures on the other hand, capture their environment by value, which means that they have to save the environment inside themselves. So it's no surprise that their sizes are equal to size_of::<Vec<_>>().
What about the closures with size 0? Since they are not capturing any environment, they could be ordinary fn-functions, too. And I guess that Rust turns them into fn-functions. And in fact, if we try to print the size of an fn-item (not a function pointer!) like this:
fn foo(x: i32) -> i32 { x + 27 }
println!("{}", std::mem::size_of_val(&foo));
... we get 0!

rewriting a floating point sum from Python to Rust

I've been trying to rewrite the code below for summing floating point numbers while minimizing the rounding errors, but I found it pretty hard to do in Rust. Any suggestions would be greatly appreciated. I attach my non-working Rust attempt
def msum(iterable):
"Full precision summation using multiple floats for intermediate values"
# Rounded x+y stored in hi with the round-off stored in lo. Together
# hi+lo are exactly equal to x+y. The inner loop applies hi/lo summation
# to each partial so that the list of partial sums remains exact.
# Depends on IEEE-754 arithmetic guarantees. See proof of correctness at:
#www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps
partials = [] # sorted, non-overlapping partial sums
for x in iterable:
i = 0
for y in partials:
if abs(x) < abs(y):
x, y = y, x
hi = x + y
lo = y - (hi - x)
if lo:
partials[i] = lo
i += 1
x = hi
partials[i:] = [x]
return sum(partials, 0.0)
The code below is what I have in Rust so far, but it's not working yet
fn inexact_sum(v: &Vec<f64>) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut i: usize = 0;
let mut hi: f64;
let mut lo: f64;
for y in partials.clone().iter() {
hi = x + y;
lo = if x.abs() < y.abs() {
y - (hi - x)
} else {
x - (hi - y)
};
if lo != 0.0_f64 {
partials[i] = lo;
i += 1;
}
let x = hi;
println!("x = {}, y = {}", x, y);
}
partials.truncate(i);
partials.push(hi);
}
partials.iter().fold(0.0_f64, |a, b| a + b)
}
EDIT: Thinking about it a bit more, indeed, the error the compiler gave me error: use of possibly uninitialized variable: `hi` is indeed useful. I should have paid more attention to it. The point is that the first time the loop does not execute at all, so hi does not get initialized. So if I change partials.push(hi); to partials.push(*x); the code compiles and runs but it does not give the right answer.
Is this what you are looking for? I think you did not mean to clone the partials array but found that you needed to in order to satisfy the borrow checker; if you try to use the code:
for y in partials.iter() {
...
partials[i] = lo;
The borrow checker will complain:
<anon>:13:17: 13:25 error: cannot borrow `partials` as mutable because it is also borrowed as immutable [E0502]
I got around this by using an index into the array instead:
for j in 0..partials.len() {
let mut y = partials[j];
Then you are not cloning the whole array of partials each time around the outer loop! Since the partials array can be modified whilst iterating through it, taking a clone first means you will end up with the old value of y instead of the new one if it has been modified.
use std::mem;
fn msum(v: &[f64]) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut x = *x;
let mut i = 0;
for j in 0..partials.len() {
let mut y = partials[j];
if x.abs() < y.abs() { mem::swap(&mut x, &mut y) }
let hi = x + y;
let lo = y - (hi - x);
if lo != 0.0 {
partials[i] = lo;
i += 1;
}
x = hi;
}
partials.truncate(i);
partials.push(x);
}
partials.iter().fold(0., |a, b| a + b)
}
fn main() {
let v = vec![1.234, 1e16, 1.234, -1e16];
println!("{}",msum(&v));
}
Playpen

Resources