While going through rust documentation I found below code
let k = 21;
let x : Result<_, &str> = Ok("foo");
assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);
What exactly is |e| & |v| in last line ? why it is required?
The .map_or_else functions take a closure as a parameter.
A closure is an anonymous function that can optionally capture the environment where they are defined. The syntax to define a closure is
let c = |x| println!(x + 2);
This takes 1 parameters which is mapped to the x variable.
Calling c(2) will print 4, calling c(4) will print 6.
Similarly |e| k * 2 and |v| v.len() is
also a closure. They also take 1 parameter, e and k which are the parameter names whose values will be filled by the .map_or_else function.
Related
Consider the following rust program:
fn main()
{
let mut x = 1;
let mut r = &x;
r;
let y = 1;
r = &y;
x = 2;
r;
}
It compiles without any errors and I agree with that behaviour.
The problem is that I am not able to reach the same conclusion when trying to reason about this formally:
The type of the variable r is &'a i32 for some lifetime 'a.
The type of &x is &'b i32 for some lifetime 'b.
The lifetime 'a includes x = 2;.
From let mut r = &x; we know that 'b: 'a.
Because of 3 and 4 we know that the lifetime 'b includes x = 2;.
Because of 2 and 5 we are doing x = 2; while borrowing x, so the program should be invalid.
What is wrong with the formal reasoning above, and how would the correct reasoning be?
The lifetime 'a includes x = 2;.
Because of 3 and 4 we know that the lifetime 'b includes x = 2;.
They don't. r is reassigned to on line 7, this ends the entire thing as r is thereafter a completely new value independent from the old one -- and yes rustc is smart enough to work at that granularity, that's why if you remove the final x; it will warn that
value assigned to r is never read
at line 7 (whereas you would not get that warning with Go for instance, the compiler doesn't work at that low a granularity).
Rustc can thus infer that the smallest necessary length for 'b stops somewhere between the end of line 5 and the start of line 7.
Since x doesn't need to be updated before line 8, there is no conflict.
If you remove the assignment, however, 'b now has to extend to the last last line of the enclosing function, triggering a conflict.
Your reasoning seems to be that of lexical lifetimes, rather than NLL. You may want to go through the RFC 2094, it is very detailed. But in essence it works in terms of liveness constraints of values, and solving those constraints. In fact the RFC introduces liveness with an example which is a somewhat more complicated version of your situation:
let mut foo: T = ...;
let mut bar: T = ...;
let mut p: &'p T = &foo;
// `p` is live here: its value may be used on the next line.
if condition {
// `p` is live here: its value will be used on the next line.
print(*p);
// `p` is DEAD here: its value will not be used.
p = &bar;
// `p` is live here: its value will be used later.
}
// `p` is live here: its value may be used on the next line.
print(*p);
// `p` is DEAD here: its value will not be used.
Also note this quote which very much applies to your misunderstanding:
The key point is that p becomes dead (not live) in the span before it is reassigned. This is true even though the variable p will be used again, because the value that is in p will not be used.
So you really need to reason off of values, not variables. Using SSA in your head probably helps there.
Applying this to your version:
let mut x = 1;
let mut r = &x;
// `r` is live here: its value will be used on the next line
r;
// `r` is DEAD here: its value will never be used
let y = 1;
r = &y;
// `r` is live here: its value will be used later
x = 2;
r;
// `r` is DEAD here: the scope ends
Life before NLL
Before discussing about non-lexical lifetimes (NLL), let's first discuss the "ordinary" lifetimes. In older Rust before NLL is introduced, the code below won't compile because r is still in scope while x is mutated in row 3.
let mut x = 1;
let mut r = &x;
x = 2; // Compile error
To fix this, we need to explicitly make r out of scope before x is mutated:
let mut x = 1;
{
let mut r = &x;
}
x = 2;
At this point you might think: If after the line of x = 2, r is not used anymore, the first snippet should be safe. Can the compiler be smarter so that we don't need to explicitly make r out of scope like we did in the second snippet?
The answer is yes, and that's when NLL comes in.
Life after NLL
After NLL is introduced in Rust, our life becomes easier. The code below will compile:
let mut x = 1;
let mut r = &x;
x = 2; // Compiles under NLL
But remember, it will compile as long as r is not used after the mutation of x. For example, this won't compile even under NLL:
let mut x = 1;
let mut r = &x;
x = 2; // Compile error: cannot assign to `x` because it is borrowed
r; // borrow later used here
Although the rules of NLL described in RFC 2094 are quite complex, they can be summarized roughly and approximately (in most cases) as:
A program is valid as long as every owned value is not mutated between the assignment of a variable referring to it and the usage of that variable.
The code below is valid because x is mutated before the assignment of r and before the usage of r:
let mut x = 1;
x = 2; // x is mutated
let mut r = &x; // r is assigned here
r; // r is used here
The code below is valid because x is mutated after the assignment of r and after the usage of r:
let mut x = 1;
let mut r = &x; // r is assigned here
r; // r is used here
x = 2; // x is mutated
The code below is NOT valid because x is mutated after the assignment of r and before the usage of r:
let mut x = 1;
let mut r = &x; // r is assigned here
x = 2; // x is mutated
r; // r is used here -> compile error
To your specific program, it's valid because when x is mutated (x = 2), there is no variable referring to x anymore—r is now referring to y because of the previous line (r = &y). Therefore, the rule is still adhered.
let mut x = 1;
let mut r = &x;
r;
let y = 1;
r = &y;
// This mutation of x is seemingly sandwiched between
// the assignment of r above and the usage of r below,
// but it's okay as r is now referring to y and not x
x = 2;
r;
I was reading the chapter on higher order functions of Rust by Example. Where they present the following canonical example:
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
fn main() {
let upper = 1000;
println!("imperative style: {}", acc);
let sum_of_squared_odd_numbers: u32 =
(0..).map(|n| n * n) // All natural numbers squared
.take_while(|&n_squared| n_squared < upper) // Below upper limit
.filter(|&n_squared| is_odd(n_squared)) // That are odd
.fold(0, |acc, n_squared| acc + n_squared); // Sum them
}
Simple enough. But I realized that I don't understand the type of parameter n_squared. Both take_while and filter accept a function that takes a parameter by reference. That makes sense to me, you want to borrow instead of consuming the values in the map.
However, if n_squared is a reference, why don't I have to dereference it before comparing its value to limit or equally surprising; why can I pass it directly to is_odd() without dereferencing?
I.e. why isn't it?
|&n_squared| *n_squared < upper
When I try that the compiler gives the following error:
error[E0614]: type `{integer}` cannot be dereferenced
--> src\higherorder.rs:13:34
|
13 | .take_while(|&n_squared| *n_squared <= upper)
|
Indicating that n_squared is an i32 and not &i32. Looks like some sort pattern matching/destructuring is happening here, but I was unable to find the relevant documentation.
You are using function parameter destructuring:
|&n_squared| n_squared < upper
is functionally equivalent to:
|n_squared| *n_squared < upper
To understand this better, imagine you're passing a tuple of type &(i32, i32) to a lambda:
|&(x, y) : &(i32, i32)| x + y
This code
let vec = vec![1, 3, 4, 5, 6];
for i in vec.iter().filter(|x| x % 2 == 0) {
println!("{}", i);
}
Produces the error
<anon>:4:36: 4:37 error: binary operation `%` cannot be applied to type `&&_` [E0369]
<anon>:4 for i in vec.iter().filter(|x| x % 2 == 0) {
^
I cannot understand the meaning of this error. Is this related to how the anonymous closure structure is created by the compiler?
The following code seems to be working.
for i in vec.iter().filter(|&x| x % 2 == 0) {
No, it is not related to how closure structure is created, it is a simple type error.
vec.iter(), when vec: Vec<T>, returns an iterator which yields references to its elements. filter() closure also accepts each element by reference (because otherwise elements would be consumed but this would defeat the whole purpose of filter()). Therefore, in vec.iter().filter(|x| ...) the closure argument x has type &&T, in your case, &&i32.
Rust numeric operators can't be applied to &&T, only to &T or T, so you need to dereference the closure argument somehow. There are two ways, first, as you noticed, you can use dereference pattern:
vec.iter().filter(|&x| x % 2 == 0)
// or even
vec.iter().filter(|&&x| x % 2 == 0)
Dereference pattern automatically dereferences the reference it is matching:
&x: &&i32 ==> x: &i32
&&x: &&i32 ==> x: i32
Alternatively, you can dereference the number directly:
vec.iter().filter(|x| *x % 2 == 0)
// or
vec.iter().filter(|x| **x % 2 == 0)
According to the rust book for iterators there are consumers like find and fold.
They are used as:
let greater_than_forty_two = (0..100).find(|x| *x > 42);
and
let sum = (1..4).fold(0, |sum, x| sum + x);
In both cases x is the current value. However in the first case x is a reference and in the second case it is the element itself.
As far as I understood from the for loop, it has to be a reference to avoid owner problems. But shouldn't there be the same owner problem in the fold case?
The issue is somewhat hard to explain with integers, since they are Copy, so compare:
let v = vec!["a".to_string(), "b".to_string()];
let result = v.into_iter().find(|s| s.starts_with("b"));
result == Some("b".to_string())
let v = vec!["a".to_string(), "b".to_string()];
let result = v.into_iter().fold("".to_string(), |a, s| s + &a);
result == "ba".to_string()
In the first case, the String needs to be returned to the caller, so we can't have the closure destroy it (which would happen if it was passed by value). In the second, the values are no longer used after the call to the closure, so they can be passed to the closure by value (which allows us to consume the String when we append to it).
I have some trouble understanding the differences in this example:
I thought that both range(..) and iter() are std::iter::Iterator
So why can I pass |x| in the first example to map and filter, but have to pass it as |&x| in the 2nd example?
use std::iter::AdditiveIterator;
fn main() {
let range1 = range(1,9);
let range2 = [1,2,3,4,5,6,7,8,9];
//indented for easier visualization
let sum1 = range1 .map(| x| x * x).filter(| x| x % 2 == 0).sum();
let sum2 = range2.iter().map(|&x| x * x).filter(|&x| x % 2 == 0).sum();
println!("{}", sum1);
println!("{}", sum2);
}
Just knowing they're both Iterator without knowing Iterators type parameter doesn't tell you anything about what they're iterating over, which is the important difference here. For this code:
range1 is an Iterator<int>
range2.iter() is an Iterator<&int>
That is, slice iterator returns references pointing directly into the slice (it has to do this or, for general T, it would be trying to steal ownership out of a borrowed slice &[T]: illegal), while range just returns the counter by-value since it constructs it internally, and is free to hand it out ("transferring ownership").
Hence, to get an int out of the slice iterator, you need to dereference the reference somehow, e.g. pattern matching through it via |&x| ..., or writing |x| *x * *x.