Type casting for Option type - rust

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, ...

Related

Mismatched types parameter error on rust generics

expected type parameter T, found type parameter A error display. I have written lifetime implementation code also but it stills doesn't solve the problem. What's wrong I am doing?
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd, A: PartialOrd>(x: T, y: A) -> T {
if x > y {
x
} else {
y
}
}
// fn main() {
// let x = 3;
// let y = 5.0;
// let max_value = max(&x, &y);
// println!("The maximum value is {}", max_value);
// }
// fn max<'a, T: PartialOrd + Copy, A: PartialOrd + Copy>(x: &'a T, y: &'a A) -> &'a T {
// if x > y {
// x
// } else {
// y
// }
// }
T and A do not have to be the same type, so you have two problems.
The first is that you constrain T and A to be PartialOrd, which is the same thing as PartialOrd<Self>. So your actual constraints are T: PartialOrd<T>, A: PartialOrd<A>. This means you can compare the order of T's to other T's and A's to other A's, but x > y compares a T to an A.
Instead, you need to constrain T: PartialOrd<A>. (This also fails, but because of the invocation in main() -- more on that later.)
Second, the function is declared to return T but the else block returns y, which is not a T. Rust is statically typed, so it expects the types to exactly match.
This could be fixed by requiring that A can be converted to T (that is, A: Into<T>) and then you can return y.into() from the else block.
So at this point, we have:
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd<A>, A: Into<T>>(x: T, y: A) -> T {
if x > y {
x
} else {
y.into()
}
}
But now you are left with more problems:
There are no types T and A satisfying T: PartialOrd<A> where T is an integer and A is a float, therefore you cannot call this function with 3 and 5.0 as you do in main().
Likewise, there's no implementation of Into<T> on A for an integer type T and a float type A.
x > y will move x and y, and then you cannot return them later. This is trivially fixed by constraining both T and A to be Copy.
The second issue could be fixed by having an enum that means "either T or A" and returning that instead. The either crate has such a type called Either, which we can use here as Either<T, A>:
use either::Either;
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: PartialOrd<A> + Copy, A: Copy>(x: T, y: A) -> Either<T, A> {
if x > y {
Either::Left(x)
} else {
Either::Right(y)
}
}
(The println! works because Either<T, A> implements Display when both T and A do.)
You are still left with the problem where there's no built-in ordering implementation between integers and floats.
A "hail mary" solution could be to require that T and A can both be converted to f64 and then convert x and y to f64 before comparing them:
use either::Either;
fn main() {
let x = 3;
let y = 5.0;
let max_value = max(x, y);
println!("The maximum value is {}", max_value);
}
fn max<T: Copy + Into<f64>, A: Copy + Into<f64>>(x: T, y: A) -> Either<T, A> {
if x.into() > y.into() {
Either::Left(x)
} else {
Either::Right(y)
}
}
This is the first bit of code we have that actually compiles, and this might be good enough for your purposes. There are still some issues that remain, however:
i64 and u64 cannot be losslessy converted to f64, therefore they do not implement Into<f64>, and so if you change let x = 3; to let x = 3u64; (or 3i64) compilation will again fail.
f64 does not implement Ord because it's possible for there to be two f64 values x and y that are not equal but neither is greater than the other -- if either value is NaN, for example. This won't cause your program to crash, but it may produce an unexpected or incorrect result.
I suspect that this is a learning exercise, so hopefully this answer helps you understand what is wrong with the original code. I would not recommend a function like this in a real-world program; instead, it would be far better to convert both arguments to be of the same Ord-implementing type ahead of time and then you can use the built-in std::cmp::max function (or Ord::max).

Proper bounds on associated types to allow default methods on traits [duplicate]

I wanted to implement a function computing the number of digits within any generic type of integer. Here is the code I came up with:
extern crate num;
use num::Integer;
fn int_length<T: Integer>(mut x: T) -> u8 {
if x == 0 {
return 1;
}
let mut length = 0u8;
if x < 0 {
length += 1;
x = -x;
}
while x > 0 {
x /= 10;
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
And here is the compiler output
error[E0308]: mismatched types
--> src/main.rs:5:13
|
5 | if x == 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0308]: mismatched types
--> src/main.rs:10:12
|
10 | if x < 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error: cannot apply unary operator `-` to type `T`
--> src/main.rs:12:13
|
12 | x = -x;
| ^^
error[E0308]: mismatched types
--> src/main.rs:15:15
|
15 | while x > 0 {
| ^ expected type parameter, found integral variable
|
= note: expected type `T`
found type `{integer}`
error[E0368]: binary assignment operation `/=` cannot be applied to type `T`
--> src/main.rs:16:9
|
16 | x /= 10;
| ^ cannot use `/=` on type `T`
I understand that the problem comes from my use of constants within the function, but I don't understand why the trait specification as Integer doesn't solve this.
The documentation for Integer says it implements the PartialOrd, etc. traits with Self (which I assume refers to Integer). By using integer constants which also implement the Integer trait, aren't the operations defined, and shouldn't the compiler compile without errors?
I tried suffixing my constants with i32, but the error message is the same, replacing _ with i32.
Many things are going wrong here:
As Shepmaster says, 0 and 1 cannot be converted to everything implementing Integer. Use Zero::zero and One::one instead.
10 can definitely not be converted to anything implementing Integer, you need to use NumCast for that
a /= b is not sugar for a = a / b but an separate trait that Integer does not require.
-x is an unary operation which is not part of Integer but requires the Neg trait (since it only makes sense for signed types).
Here's an implementation. Note that you need a bound on Neg, to make sure that it results in the same type as T
extern crate num;
use num::{Integer, NumCast};
use std::ops::Neg;
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + NumCast,
{
if x == T::zero() {
return 1;
}
let mut length = 0;
if x < T::zero() {
length += 1;
x = -x;
}
while x > T::zero() {
x = x / NumCast::from(10).unwrap();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
The problem is that the Integer trait can be implemented by anything. For example, you could choose to implement it on your own struct! There wouldn't be a way to convert the literal 0 or 1 to your struct. I'm too lazy to show an example of implementing it, because there's 10 or so methods. ^_^
num::Zero and num::One
This is why Zero::zero and One::one exist. You can (very annoyingly) create all the other constants from repeated calls to those.
use num::{One, Zero}; // 0.4.0
fn three<T>() -> T
where
T: Zero + One,
{
let mut three = Zero::zero();
for _ in 0..3 {
three = three + One::one();
}
three
}
From and Into
You can also use the From and Into traits to convert to your generic type:
use num::Integer; // 0.4.0
use std::ops::{DivAssign, Neg};
fn int_length<T>(mut x: T) -> u8
where
T: Integer + Neg<Output = T> + DivAssign,
u8: Into<T>,
{
let zero = 0.into();
if x == zero {
return 1;
}
let mut length = 0u8;
if x < zero {
length += 1;
x = -x;
}
while x > zero {
x /= 10.into();
length += 1;
}
length
}
fn main() {
println!("{}", int_length(45));
println!("{}", int_length(-45));
}
See also:
How do I use floating point number literals when using generic types?

Expected std::iter::Iterator, but std::iter::Iterator found

I am trying to express the following:
Given a matrix and two index increments, return all quadruplets of numbers from the matrix: quadruplets of numbers along the rows, or along the columns, or along some diagonal.
use std::iter::Iterator;
use std::iter::Peekable;
use std::ops::Range;
struct Quads<'a> {
mx: &'a Vec<Vec<u32>>,
xs: &'a mut Peekable<Range<i32>>,
ys: &'a mut Peekable<Range<i32>>,
dx: i32,
dy: i32,
}
impl<'a> Quads<'a> {
fn new(mx: &'a Vec<Vec<u32>>, dx: i32, dy: i32) -> Quads<'a> {
let ys = (if dy < 0 { -3 * dy } else { 0 })..(mx.len() as i32 - if dy > 0 { 4 * dy } else { 0 });
let xs = 0..0;
Quads{
mx: mx,
xs: &mut xs.peekable(),
ys: &mut ys.peekable(),
dx: dx,
dy: dy,
}
}
}
impl<'a> Iterator for Quads<'a> {
type Item = &'a mut dyn Iterator<Item = u32>;
fn next(&mut self) -> Option<Self::Item> {
while self.xs.peek() == None && self.ys.peek() != None {
self.xs = &mut ((if self.dx < 0 { -3 * self.dx } else { 0 })..
(self.mx[0].len() as i32 - if self.dx > 0 { 4 * self.dx } else { 0 }))
.peekable();
self.ys.next();
}
let y = self.ys.peek();
if y == None {
return None;
}
let y = *y.unwrap();
let x = self.xs.next().unwrap();
Some(&mut ((x..).step_by(self.dx as usize)
.zip((y..).step_by(self.dy as usize))
.take(4)
.map(|(x,y)| self.mx[y as usize][x as usize])))
}
}
This produces confusing error messages:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:52:27
|
52 | .map(|(x,y)| self.mx[y as usize][x as usize])))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 33:4...
--> src/main.rs:33:4
|
33 | / fn next(&mut self) -> Option<Self::Item> {
34 | | while self.xs.peek() == None && self.ys.peek() != None {
35 | | self.xs = &mut ((if self.dx < 0 { -3 * self.dx } else { 0 })..
36 | | (self.mx[0].len() as i32 - if self.dx > 0 { 4 * self.dx } else { 0 }))
... |
52 | | .map(|(x,y)| self.mx[y as usize][x as usize])))
53 | | }
| |____^
= note: ...so that the types are compatible:
expected &&mut Quads<'a>
found &&mut Quads<'a>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 30:6...
--> src/main.rs:30:6
|
30 | impl<'a> Iterator for Quads<'a> {
| ^^
= note: ...so that the types are compatible:
expected std::iter::Iterator
found std::iter::Iterator
It seems to indicate that it has found the same things it was looking for. So what's wrong?
Intended use
Look at https://projecteuler.net/problem=11
Of course, that problem can be solved in more straightforward ways, but I am learning how to express complex things in Rust. So here I am trying to express a Quad that is an Iterator that can extract quadruples of numbers from that Euler problem, where each quadruple is an Iterator itself.
Everything inside Quad represents the state of the Iterator. xs and ys represent the iterators of the coordinates of "the current cell" from which to start the next quadruple. next then tries to see if reached the end of the row, and advances to the next row by reinitialising xs to a new Iterator. When ys reached beyond the last row, we've extracted all the quadruples.
Then something like this:
for q in Quad::new(mx, 1, 0) { ... process all quadruples along the rows }
for q in Quad::new(mx, 0, 1) { ... process all quadruples along the columns }
for q in Quad::new(mx, 1, 1) { ... process all quadruples along one diagonal }
for q in Quad::new(mx, 1, -1) { ... process all quadruples along the other diagonal }
I think I've got the idea captured, but I don't know what the compiler does not like about it, and consequently how to move forward.
Ok, so I figured it out. It is really not helpful that rustc produced such a confusing error message - evidently, it found what it was looking for, but still unsatisfied.
There are several problems with the code as posted. My original assumption has been that by marking a reference as mutable I can tell the compiler that whoever receives the reference is responsible for it henceforth, including memory management. Whereas it may be true in some cases (I am not certain; that's still to figure out), it certainly does not work with struct fields (Quad's xs) and return values. In this case we can get rid of &mut in the declaration of the xs and ys:
struct Quads<'a> {
mx: &'a Vec<Vec<u32>>,
xs: Peekable<Range<i32>>,
ys: Peekable<Range<i32>>,
dx: i32,
dy: i32,
}
The other problem has been that if it is not a reference, how do you limit the lifetime of a value. (Eg in this case the Iterator returned by next is valid only for as long as the mx)
The other problem has been the expression problem: how do you make next return an Iterator (I don't want to leak what kind of Iterator) so that the compiler is happy. (Eg just dyn Iterator won't do - "size is not known at compile time"). These two are solved by using Box, which can be annotated with a lifetime, too:
impl<'a> Iterator for Quads<'a> {
type Item = Box<dyn Iterator<Item = u32> + 'a>;
fn next(&mut self) -> Option<Self::Item> {
...
}
}
The other problem has been that even though the use of mx is read-only, the closure |(x, y)| self.mx[y][x] captures self, which is a mutable reference. That's the easy one: get a local variable, then move:
let mx = self.mx;
Some(Box::new(...
.map(move |(x, y)| mx[y as usize][x as usize])))
Nearly forgot. And the really strange one, which looked fishy even as I typed it originally: step_by takes usize, which is unsigned, and isn't really constructing a Range that enumerates values by adding a given increment; rather it constructs an Iterator that skips a given number of elements (pretty much). So, a tuple iterator is needed:
struct Tup<T> {
x: (T, T),
d: (T, T),
}
...
impl<T: AddAssign + Copy> Iterator for Tup<T> {
type Item = (T, T);
fn next(&mut self) -> Option<(T, T)> {
self.x.0 += self.d.0;
self.x.1 += self.d.1;
Some(self.x)
}
}
So instead of zipping two Iterators with step_by, you get a Tup initialised with known initial values and deltas:
Some(Box::new(Tup::new((x, y), (self.dx, self.dy))
.take(4)
.map(move |(x, y)| mx[y as usize][x as usize])))
Another solution would be not to export iterators, but pass an operation to run inside.

How to add the proper type annotations when converting an integer to a float?

What's the proper type annotation in the following code to convert an integer to a float?
use conv::{ValueInto, errors::PosOverflow};
fn run_me() -> Result <f32, PosOverflow<usize>> {
let m: f32 = 21.3;
let n: usize = 23;
let p: f32 = n.value_into()?;
let bar = m * p;
let baz = m * (n.value_into()?); // Compiler error here
Ok(baz)
}
fn main() {
run_me();
}
Which fails with:
error[E0277]: cannot multiply `()` to `f32`
--> src/main.rs:7:17
|
7 | let baz = m * (n.value_into()?);
| ^ no implementation for `f32 * ()`
|
= help: the trait `std::ops::Mul<()>` is not implemented for `f32`
error[E0277]: the trait bound `(): conv::ValueFrom<usize>` is not satisfied
--> src/main.rs:7:22
|
7 | let baz = m * (n.value_into()?);
| ^^^^^^^^^^ the trait `conv::ValueFrom<usize>` is not implemented for `()`
|
= note: required because of the requirements on the impl of `conv::ValueInto<()>` for `usize`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.
error: Could not compile `tmp3`.
To learn more, run the command again with --verbose.
Basically, a conversion from usize to f32 could fail, so we have the conv package to handle the details. In the following code, the bar computes just fine, but baz does not. I'd like a one liner like baz to work directly because more complicated examples will need to chain together multiple conversions.
Edit 1
Ok, so I can force what I want by adding a trait to usize. Is there a problem with this approach or can the original call to value_into be fixed?
use conv::{ValueFrom, ValueInto, errors::PosOverflow};
trait ToF32 {
fn to_f32(self) -> Result<f32, PosOverflow<usize>>;
}
impl ToF32 for usize {
fn to_f32(self) -> Result<f32, PosOverflow<usize>> {
f32::value_from(self)
}
}
fn run_me() -> Result <f32, PosOverflow<usize>> {
let m: f32 = 21.3;
let n: usize = 23;
let p: f32 = n.value_into()?;
let bar = m * p;
let baz = m * n.to_f32()?;
// let buz = m * n.value_into()?;
Ok(baz)
}
fn main() {
run_me();
}
Some functions in Rust like value_into and try_into have to be called when assigning to directly to a typed variable.
To answer your question directly, the proper way to add type annotations is by assigning it into a temporary variable (like you did with bar and p) before using the value.

`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);
}

Resources