Why is referencing/borrowing needed in this case? - rust

I'm new to rust and just played around with some code:
fn array_leaders(arr: &[i32]) -> Vec<i32> {
let mut res = Vec::new();
for (i, left) in arr.iter().enumerate() {
let right = arr.iter().skip(i+1).sum();
if left > right { res.push(*left) };
}
res
}
which gives the following error:
error[E0277]: the trait bound `&i32: Sum<&i32>` is not satisfied
--> src/lib.rs:5:42
|
5 | let right = arr.iter().skip(i+1).sum();
| ^^^ the trait `Sum<&i32>` is not implemented for `&i32`
Now, I've figured out that i could just add an & to the right variable in the if statement:
if left > &right { res.push(*left) };
which solves the problem. I would like to understand why though

It's because left has type &i32 (generated via .iter()) but right as type i32 (generated by sum()).
We could compare *left against right (two i32).
Rust also enables the comparison through references: left against &right (two &i32).
The same level of indirection must be present on both sides of the comparison.
The short example below tries to illustrate this.
Moreover, as stated in a comment, Rust tries to deduce the types that are not strictly imposed (when some alternatives exist).
Here, the type of left is imposed by .iter() but the return type of sum() could be deduced.
Then Rust tries to find an implementation of sum() that matches &i32 because this result will be compared against &i32 (left). This cannot be found here, thus the error.
fn main() {
let a = 5;
for b in 3..=7 {
let r_a = &a;
let r_b = &b;
let cmp1 = a < b;
let cmp2 = r_a < r_b;
let cmp3 = *r_a < *r_b;
println!("{} < {} --> {} {} {}", a, b, cmp1, cmp2, cmp3);
}
}
/*
5 < 3 --> false false false
5 < 4 --> false false false
5 < 5 --> false false false
5 < 6 --> true true true
5 < 7 --> true true true
*/

Related

Type casting for Option type

I'm newbie in Rust from Python. I believe it's a basic question, but I am too new to find the answer by keywords like Type Casting Option.
In Python, to make the type checker know return type is not Optional[int] + int, we can address assert logic to enforce the type checker know x will never be None after line assert.
from typing import Optional
def add_one(x: Optional[int] = None) -> int:
if x is None:
x = 0
assert x is not None
return x + 1
if __name__ == '__main__':
add_one(0) # 1
add_one() # 1
add_one(999) # 1000
In Rust, assuming the interface is same, how do achieve the same thing? Namely, how to make compiler know the type of x is not Option anymore?
fn add_one(mut x: Option<i32>) -> i32 {
if x == None {
x = Some(0);
}
return x + 1;
}
fn main() {
add_one(Some(0));
add_one(None);
add_one(Some(999));
}
Here's the error message:
error[E0369]: binary operation `+` cannot be applied to type `std::option::Option<i32>`
--> tmp.rs:5:14
|
5 | return x + 1;
| - ^ - {integer}
| |
| std::option::Option<i32>
|
= note: an implementation of `std::ops::Add` might be missing for `std::option::Option<i32>`
Note that I've tried things like adding another variable with type i32 (let y: i32 = x;), but it didn't work either with following message.
error[E0308]: mismatched types
--> tmp.rs:5:22
|
5 | let y: i32 = x;
| ^ expected i32, found enum `std::option::Option`
|
= note: expected type `i32`
found type `std::option::Option<i32>`
Use unwrap_or:
fn add_one(x: Option<i32>) -> i32 {
x.unwrap_or(0) + 1
}
fn main() {
assert_eq!(1, add_one(Some(0)));
assert_eq!(1, add_one(None));
assert_eq!(1000, add_one(Some(999)));
}
Namely, how to make compiler know the type of x is not Option anymore?
Rust's type checker doesn't have to be compatible with somewhat dodgy usage patterns, so the way to do that is to redefine x as not an option anymore e.g.:
fn add_one(mut x: Option<i32>) -> i32 {
let x = if let Some(v) = x {
v
} else {
0
};
return x + 1;
}
or the uglier and less efficient (but maybe closer to Python):
fn add_one(mut x: Option<i32>) -> i32 {
let x = if x == None {
0
} else {
x.unwrap()
};
return x + 1;
}
Note that you don't have to shadow x, so you could just as well let y instead. It would probably be cleaner here.
But as Boiethios pointed out, Rust provides utilities for this sort of use-cases e.g. unwrap_or, map_or, ...

Writing a function to take iterables of reference and value type

I'd like to have a function that takes an iterable and returns its smallest and largest elements. This is part of an exercise in learning Rust, but I'm struggling in being able to handle reference types and value types at the same time.
This is what I have:
fn min_max<'a, I, T>(mut iter: I) -> Option<(&'a T, &'a T)>
where
I: Iterator<Item = &'a T>,
T: PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min;
for el in iter {
if el < min {
min = el;
}
if el >= max {
max = el;
}
}
Some((min, max))
}
Then, I give this an iterator over some integers.
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
This works, and prints 3 10. But then I want to do some operations on the numbers before I compute the minimum and maximum, like a map and/or a filter.
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
This gives a compiler error:
error[E0271]: type mismatch resolving `<[closure#src/main.rs:31:35: 31:44] as std::ops::FnOnce<(&u32,)>>::Output == &_`
--> src/main.rs:32:31
|
32 | if let Some((min, max)) = min_max(doubled) {
| ^^^^^^^ expected u32, found reference
|
= note: expected type `u32`
found type `&_`
= note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::slice::Iter<'_, u32>, [closure#src/main.rs:31:35: 31:44]>`
= note: required by `min_max`
This confused me, because if nums.iter() works as an argument, why shouldn't nums.iter().map(...)?
I understand the error message in principle: my array is of u32, not &u32, whereas my function requires Iterator::Item to be of type &'a T. But then I don't get why it errors only on the second sample (using .iter().map()) and not on the first (just .iter()).
I've made a playground with this example and a commented out example where I construct an iterable of integers from a string. This fails in exactly the same way as the second example above (and is closer to my actual use case).
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
I'd like to have a function that takes an iterable and returns its smallest and largest elements.
Use Itertools::minmax.
handle reference types and value types at the same time.
You don't need to — references to numbers can also be compared:
fn foo(a: &i32, b: &i32) -> bool {
a < b
}
In your case, remember that a value and a reference to that value are different types. That means you can accept an iterator of any type so long as the yielded values are comparable, and this includes both references and values, as requested:
fn min_max<I>(mut iter: I) -> Option<(I::Item, I::Item)>
where
I: Iterator,
I::Item: Clone + PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min.clone();
for el in iter {
if el < min {
min = el;
} else if el >= max {
max = el;
}
}
Some((min, max))
}
I chose to add the Clone bound although to be more true to your original I could have used the Copy bound. Itertools returns an enum to avoid placing any restrictions on being able to duplicate the value.
This works with all three of your examples:
fn main() {
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
}
3 10
6 20
3 10
my array is of u32, not &u32, whereas my function requires Iterator::Item to be of type &'a T. But then I don't get why it errors only on the second sample (using .iter().map()) and not on the first (just .iter()).
Because iterating over an array returns references. By using map, you are changing the type of the iterator's item from &i32 to i32. You could have also chosen to adapt the first call to return values.
You have a type mismatch problem because the .iter() call produces a "slice" iterator (Iterator with Item = &T), but the .map(|x| 2 * x) is a iterator adaptor, the call of which produces a new "value" iterator (Iterator with Item = T). These values must necessarily be stored in memory before we can get them "slice", because we can only get a reference to the value that is already stored somewhere in the memory. Therefore, we need to collect the result of the map function before we can get an iterator with references to the values it returns:
let doubled: Vec<_> = nums.iter().map(|x| 2 * x).collect();
if let Some((min, max)) = min_max(doubled.iter()) {
println!("{} {}", min, max);
}
For more details, see chapter 13.2 Iterators of The Rust Programming Language book.

Why do I get a type inference error for only one side of a range bound?

I am solving the SPOJ PRIME1 - Prime Generator problem:
use std::io::stdin;
fn is_prime(n: u64) -> bool {
let bound = (n as f64).sqrt() as u64;
for i in 2..bound + 1 {
if n % i == 0 {
return false;
}
}
true
}
fn main() {
let mut s = String::new();
stdin().read_line(&mut s).expect("Error");
let n: u32 = s.trim().parse().unwrap();
for i in 0..n {
let mut s = String::new();
stdin().read_line(&mut s).expect("Error");
let t: Vec<&str> = s.trim().split(" ").collect();
let mut left = t[0].parse().unwrap();
let right = t[1].parse().unwrap();
if left == 1 {
left += 1;
}
for j in left..right + 1 {
if is_prime(j) {
println!("{}", j);
}
}
}
}
The compilation fails due to this error:
error[E0282]: type annotations needed
--> src/main.rs:23:13
|
23 | let right = t[1].parse().unwrap();
| ^^^^^
| |
| consider giving `right` a type
| cannot infer type for `_`
Why is the error message about the right variable? Why is nothing said about the left variable? They appear in almost the same context. Am I missing something?
When debugging a problem, it's very useful to create a MCVE. This is a technique that will greatly aid you as you continue to learn about programming. Here's an example for this case:
fn main() {
let left = "0".parse().unwrap();
let right = "1".parse().unwrap();
for j in left..right + 1 {
let j2: u64 = j;
}
}
They do take practice, however. See the revision history for a reduction that was a little too minimal.
They appear in almost the same context.
Yes, and how they are different should be a clue. What is different about left and right in the above code? There are two things I see:
right has a value added to it. In Rust, Add can be overloaded on different types for the two things being added. This means that the pair of input types determines the output type. However, you cannot "work backwards" from a result type to input types because there can be more than one pair of input types that results in a u64. The four permutations of (u64, &u64) is one example.
left occurs first. If you reduce all the way to this code, you still only get one error, whichever occurs first:
fn main() {
let left = "0".parse().unwrap();
let right = "1".parse().unwrap();
}
As for why, I'm not fully sure. However, cascading errors is a common hard thing for compilers to report. In this case, I'd believe that type inference fails once, so any subsequent inference is likely to be wrong, so the compiler stops there and reports the error.

`expected reference, found integral variable` in accessing a value from BTreeMap

I'm trying to retrieve a key from a BTreeMap and manipulate it in subsequent code.
use std::collections::BTreeMap;
fn main() {
let mut map: BTreeMap<u64, u64> = BTreeMap::new();
map.entry(0).or_insert(0);
// It seems like this should work:
let y = map[0] + 1; // expected reference, found integral variable
// Other things I've tried:
let value_at_0 = map[0]; // expected reference, found integral variable
let ref value_at_0 = map[0]; // expected reference, found integral variable
let y = value_at_0 + 1;
let y = (&map[0]) + 1; // expected reference, found integral variable
let y = (&mut map[0]) + 1; // binary operation `+` cannot be applied to type `&mut u64`
let y = (*map[0]) + 1; // type `u64` cannot be dereferenced
println!("{}", y);
}
The error is particularly confusing, since I would think an integral variable would be precisely the kind of thing you could add 1 to.
To show what I would like this code to do, here is how this would be implemented in Python:
>>> map = {}
>>> map.setdefault(0, 0)
0
>>> y = map[0] + 1
>>> print(y)
1
For SEO purposes, since my Googling failed, the originating error in somewhat more complex code is expected reference, found u64
For reference, the full compilation error is:
error[E0308]: mismatched types
--> ./map_weirdness.rs:8:15
|
8 | let y = map[0] + 1; // expected reference, found integral variable
| ^^^^^^ expected reference, found integral variable
|
= note: expected type `&u64`
= note: found type `{integer}`
The bug was in what was being passed to [], though the error highlighted the whole map[0], suggesting that the error was in the type of the value of map[0] when it was actually in computing the value. The correct implementation needs to pass a reference to [] as follows:
use std::collections::BTreeMap;
fn main() {
let mut map: BTreeMap<u64, u64> = BTreeMap::new();
map.entry(0).or_insert(0);
let y = map[&0] + 1;
println!("{}", y);
}

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!

Resources