Can &Some(_) in vec.iter() { .. } compile? - rust

I tried to run the following code snippet:
let a = &[Some(1), Some(2), Some(3), None, Some(4)];
let mut sum = 0;
for &Some(x) in a.iter() {
sum += x;
}
assert_eq!(sum, 1+2+3+4);
The compiler replied with:
about_loops.rs:39:9: 43:18 error: non-exhaustive patterns: None not covered
about_loops.rs:39 for &Some(x) in a.iter() {
about_loops.rs:40 sum += x;
about_loops.rs:41 }
about_loops.rs:42
about_loops.rs:43 assert_eq!(sum, 1+2+3+4);
error: aborting due to previous error
make: *** [all] Error 101
Can I make such a construct compile for a for loop without using a match expression as suggested by luke and hobbs? Or is this error message misleading?
It does not seem so given the grammar definition of for.
for_expr : "for" pat "in" expr '{' block '}' ;
I'm on:
rustc 0.11.0-pre-nightly (6291955 2014-05-19 23:41:20 -0700)
host: x86_64-apple-darwin
To clarify: How expressive is the 'pat' portion of for_expr? This is not specified under http://doc.rust-lang.org/rust.html#for-expressions in contrast to the definition under http://doc.rust-lang.org/rust.html#match-expressions.

The pattern of a for loop essentially has the same restrictions as a let: it has to be irrefutable, that is, it can't ever fail to match.
Examples of irrefutable patterns are &, tuples, structs and single-variant enums. Other patterns (like multivariant enums or literals) aren't guaranteed to always match, since the type allows for values that aren't covered by the pattern.
The for construct is essentially a macro that desugars as follows (it desugars in the same pass as macros are expanded, you can see it manually running rustc with --pretty expanded):
for <pattern> in <iter_expression> {
<code>
}
// becomes
match &mut <iter_expression> { // match to guarantee data lives long enough
it => {
loop {
match it.next() {
None => break,
Some(<pattern>) => { <code> }
}
}
}
}
That is a normal match, i.e. the arms have to be exhaustive (cover every possibility), and so if <pattern> is just &Some(_), then the Some(&None) possibility isn't covered.
(The Some arm is essentially equivalent to Some(value) => { let <pattern> = value; .... Thinking about it now, this might actually be a desugaring that gives better error messages: I filed #14390.)

The Some is a type in an enum. The Option enum has two types, Some(T) and None. Your code assumes that a.iter() always is Some(T), and never checks for None. To add in the check, you can use an match. Something like this:
let a = &[Some(1), Some(2), Some(3), None, Some(4)];
let mut sum = 0;
for &j in a.iter() {
match j {
Some(x) => sum += x,
None => ()
}
}
assert_eq!(sum, 1+2+3+4);
Hope that helps!

for is binding each element in a to the pattern &Some(x) — so when the first element of a is &Some(1), x becomes 1. But None doesn't match the pattern &Some(x) so the binding can't succeed. Rust infers from the literal values that the type of a is actually Option (the type that encompasses either Some(_) or None) and that your pattern doesn't cover all of the possibilities. Instead of waiting for runtime to tell you it doesn't know what to do, it throws an error at compile-time instead.
From what little Rust I know (mostly having read the tutorial) I think you need to do something like:
for &thing in a.iter() {
match thing {
Some(x) => sum += x
None => /* do nothing */
}
}

The following works as well:
use std::iter::AdditiveIterator;
fn main() {
let a = &[Some(1), Some(2), Some(3), None, Some(4)];
let sum = a.iter().filter_map(|x| *x).sum();
assert_eq!(sum, 1+2+3+4);
}

This also works:
let sum = a.iter().fold(0, |s, e| s + e.unwrap_or(0));

Related

Shouldn't the `return` statement return `!` (never type)?

Example code snippet:
fn foo() -> i32 {
let a = return 2;
a + 1
}
fn main() {
println!("{}", foo());
}
I would expect that since a will never actually get assigned anything, its type should be !. However the compiler tells me that its type is in-fact () (the unit type). This struck as weird to me. What could be the reason behind this?
The type of return 42 is !:
break, continue and return expressions also have type !. For example we are allowed to write:
#![feature(never_type)]
let x: ! = {
return 123
};
From https://doc.rust-lang.org/std/primitive.never.html.
But one of the characteristic of ! is that it can be coerce to a value of any type:
fn foo() -> i32 {
let a: String = return 2;
42
}
fn main() {
println!("{}", foo());
}
This is what enables things like
let num: u32 = match get_a_number() {
Some(num) => num,
None => break,
};
(from the same page).
Both branches must have the same type. num is clearly u32, and break is !. Both can then have the same type u32 by coercing break to u32.
This is perfectly fine because a value of type ! can never exist, so the compiler can "convert" it to any value of any other type.
Where the confusion arises in your example is that the compiler will claim "error[E0277]: cannot add i32 to ()". This is probably for historical reasons. ! didn't use to exist as such in the Rust 1.0 days. Over time it became more of a first class citizen, but some special cases were required for backwards compatibility where ! will be treated as () over any other type.

Matching an i32 value with character constants

I'm writing a parser in Rust, which needs at various points to match the current token against candidate values. Some of the candidate values are characters, others are integer constants, so the token is declared as i32, which would be plenty to accommodate both. (All the characters to be matched against are ASCII.)
The problem is that when I supply a character constant like '(' to be matched against, the compiler complains that it expected i32 and is getting char.
I tried writing e.g. '(' as i32 but an as expression is not allowed as a match candidate.
Obviously I could look up the ASCII values and provide them as numbers, but it seems there should be a more readable solution. Declaring the token as char doesn't really seem correct, as it sometimes needs to hold integers that are not actually characters.
What's the recommended way to solve this problem?
It’s a bit verbose, but your match arms could be of the form c if c == i32::from(b'(').
Another alternative would be to match on u8::try_from(some_i32) (branch arms Some(b'(') and then either None if some_i32 == … or None => { match some_i32 { … } }).
Yet another would be to change the type from i32 to your own enum, which is probably the cleanest option but might require some convincing of the Rust compiler to get an i32-like representation if you need that for some reason.
Finally, you could define const PAREN_OPEN: i32 = b'(' as i32; and use PAREN_OPEN as the pattern.
Since as expressions are allowed in constants, and matching is allowed against constants, you can use a constant:
const LPAREN: i32 = '(' as i32;
match v {
LPAREN => { ... }
// ...
}
If you can use nightly, you can use the inline_const_pat feature to reduce the boilerplate:
#![feature(inline_const_pat)]
match v {
const { '(' as i32 } => { ... }
// ...
}
Another way: here's a small proc macro that will replace the characters with their numerical value (it does not work with nested char patterns):
use proc_macro::TokenStream;
use quote::ToTokens;
#[proc_macro]
pub fn i32_match(input: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(input as syn::ExprMatch);
for arm in &mut input.arms {
if let syn::Pat::Lit(lit) = &mut arm.pat {
if let syn::Expr::Lit(syn::ExprLit { lit, .. }) = &mut *lit.expr {
if let syn::Lit::Char(ch) = lit {
*lit = syn::Lit::Int(syn::LitInt::new(
&(ch.value() as i32).to_string(),
ch.span(),
));
}
}
}
}
input.into_token_stream().into()
}
i32_match! {
match v {
'(' => { ... }
// ...
}
}

Rust stacked optional in while let

I am really new in rust, and while going through the rustlings exercises I found something I do not fully understand regarding stacked Options.
Given the following code:
let vector = vec![Some(24), None, Some(42)];
let mut iter = vector.iter();
while let Some(Some(number)) = iter.next() {
println!("Number: {}", number);
}
I would expect to see the following output:
Number: 24
Number: 42
But I guess as soon as rust encounters the None, the while loop exits, only printing the 24
What would be the most idiomatic rust code to iterate and unwrap optional values?
The closest that I got would look something like this:
let mut iter = vector.iter();
while let Some(iterNext) = iter.next() {
if let Some(num) = iterNext {
println!("Number: {}", num);
}
}
Or it could also be done by checking the existence in a for loop:
for opt in &vector {
if opt.is_some() {
println!("Number: {}", opt.unwrap());
}
}
Another nice way to write this code is
for num in vector.iter().flatten() {
println!("{}", num);
}
The flatten() method on an iterator treats each item of the iterator as an iterator, and returns an iterator over all these iterators chained together. Option is an iterator that yields one element if it is Some, or none for None, so flatten() is exactly what we want here.
Of course you could also write this using for_each(), but for code with side effects I generally prefer for loops.
I would expect to see the following output: [...]
A while loop that encounters a false condition exits - but that's not specific to Rust, it's just how while loops work.
An idiomatic solution would probably combine your last two snippets:
for opt in &vector {
if let Some(num) = opt {
println!("Number: {}", num);
}
}
Just a simple for loop containing an if let, no unwrapping required.
Another idiomatic variant is to use iterator adapters:
vector
.iter()
.filter_map(|opt| opt.as_ref())
.for_each(|num| println!("{}", num));
Note that here we could use filter(Option::is_some), but then we would be left with options and would have to use unwrap() to get to the values. This is where
filter_map() comes in useful: it filters the Some values (after applying the map function), and at the same time extracts the values inside. opt.as_ref() serves to trivially convert &Option<T>, obtained from iterating a vector of options by reference, to Option<&T> which filter_map expects returned.
Using and_then to filter out the None's and only delivering Some's to the programs working part:
let vector = vec![Some(24), None, Some(42)];
for num in vector.iter() {
num.and_then::<i32, fn(i32) -> Option<i32>>(|n| {
println!("{}", n); // …working part
None
});
}

Why can I directly match an array of Options but not a variable containing an array of Options?

The following code compiles:
fn consume(_: Box<u64>) {}
let tuple = (Some(Box::new(1)), Some(Box::new(2)));
match tuple {
(Some(x), Some(y)) => {
consume(x);
consume(y);
}
_ => (),
}
The following code compiles:
fn consume(_: Box<u64>) {}
match [Some(Box::new(1)), Some(Box::new(2))] {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}
But this code does not compile:
fn consume(_: Box<u64>) {}
let array = [Some(Box::new(1)), Some(Box::new(2))];
match array {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}
The compiler says:
error[E0382]: use of moved value: `(array[..] as std::prelude::v1::Some).0`
--> src/main.rs:5:24
|
5 | [Some(x), Some(y)] => {
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because the value has type `std::boxed::Box<u64>`, which does not implement the `Copy` trait
Why do the first and second version compile, but not the third version?
Here's a reduced version of your code:
struct NonCopy;
fn main() {
// OK
let tuple = (Some(NonCopy), Some(NonCopy));
if let (Some(_x), Some(_y)) = tuple {}
// OK
if let [Some(_x), Some(_y)] = [Some(NonCopy), Some(NonCopy)] {}
// Fails
let array = [Some(NonCopy), Some(NonCopy)];
if let [Some(_x), Some(_y)] = array {}
}
The good news
This code works as-is when non-lexical lifetimes are enabled.
The bad news
Non-lexical lifetimes aren't stable yet.
The workaround
Explicitly transfer ownership of the array to the match or if let head expression:
let array = [Some(NonCopy), Some(NonCopy)];
if let [Some(_x), Some(_y)] = { array } {}
The explanation
The current implementation of the borrow checker is AST-based while a future implementation will be MIR-based. At a high level, you can think of this as "working on the code as I typed it" (AST) and "working on the logical flow of data in my code" (MIR).
Certain "hacks" have been added to the AST borrow checker, which is why you can successfully use an array literal but not the variable. With the MIR borrow checker, bigger hacks like these will disappear and the borrow checker will also become more precise, allowing more code to compile.
It is unexpected that the third version does not compile. The same issue occurs when matching boxed values as described here.
Citing the explanation for the error from the linked answer:
My only guess is that the ownership of the Box is moved to the first
param, the param is extracted, then the compiler tries to move it
again to the next parameter.
Replace "Box" with "array" and you get an explanation for what is going on when matching an array. One of the solutions presented in the linked answer also works for matching arrays - the use of curly braces to force a complete move of the Box/array into the match expression:
fn consume(_: Box<u64>) {}
let array = [Some(Box::new(1)), Some(Box::new(2))];
match {array} {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}

Can the `let Some(var) = option;` syntax be used in a situation where it's known that it's not `None`?

When an option variable is known to be non-None, its normally fine to write:
let var = option_var.unwrap();
In one case I ran into, this caused an error about moving out of borrowed context.
if let Some(var) = option_var { ... }
(Handy since it allows Some(ref mut var) = option_var too).
This works, however in this case I don't want this to be an if statement.
Writing let Some(var) = option_var; fails with the error "pattern None not covered".
To be clear this question isn't about borrowed context.
Can the let Some(var) = option; syntax be used in a situation where it's known that it's not None? Resolving the "Pattern None not covered" warning? Or is this simply not supported outside an if statement?
Given the following case:
fn main() {
let foo = Some(1);
let Some(bar) = foo;
}
error[E0005]: refutable pattern in local binding: None not covered
let Some(x) = y introduces a pattern; let and match statements and function arguments are pattern-matching contexts, but since in this case the pattern doesn't cover the other possible case, it is not a valid pattern matching context.
The only contexts which the let Some(x) = y statement is applicable to are the if let expressions and while let loops.
If you are sure (e.g. with an earlier assert!() or if it is introduced "by hand") that a None is not possible, you can just use unwrap().
I've just encountered a similar problem with enum like:
let SomeEnum::First(first) = item;
I know item is SomeEnum::First. But Rust won't let me compile.
One way to bypass this problem is to use a macro like this:
macro_rules! unwrap {
($enum:path, $expr:expr) => {{
if let $enum(item) = $expr {
item
} else {
panic!()
}
}};
}
And you can invoke unwrap! like this:
let first = unwrap!(SomeEnum::First, item);
PS. I'm using this in my tests. It helps shorten the tests.

Resources