I am not sure what is wrong:
I have an enum like the following:
#[derive(Debug,PartialEq)]
pub enum Tree {
Leaf,
Node(Box<Tree>,i32,Box<Tree>)
}
Now for some reason when I try to find the sum of all of the leaves in this tree:
pub fn tree_sum(t:&Tree) -> i32 {
match t {
Tree::Leaf => 0,
Tree::Node(Leaf, x, Leaf) => *x
}
}
The compiler decides to give me a very strange error:
error[E0416]: identifier `Leaf` is bound more than once in the same pattern
--> src/functions.rs:11:28
|
11 | Tree::Node(Leaf,x, Leaf) => *x,
| ^^^^ used in a pattern more than once
Can someone explain to me what went wrong and what it should be instead?
The Tree::Node variant contains types (Box<Tree>) which are not enums, meaning that you cannot match on the inner variants.
What's happening here is that, as the compiler tells you, you have created two different bindings with the same name.
If we assumed you're only interested in the contents of nodes, you'd do:
match t {
Tree::Leaf => 0,
Tree::Node(leaf1, x, leaf2) => ...
}
where both leaf1 and leaf2 being of the type &Box<Tree>.
Now it is a bit unclear what is the goal of this match, as if you were only interested in nodes containing two leaves, then this would make the match non-exhaustive, and you'd need to add default cases.
If you are not interested in the values of the inner trees, and only *x you can do the following:
match t {
Tree::Leaf => 0,
Tree::Node(_, x, _) => *x,
}
which is exhaustive.
If you want to match on nodes that contains two leaves, then you need to be more creative.
An example could be the following:
match t {
Tree::Leaf => 0,
Tree::Node(leaf1, x, leaf2)
if matches!(**leaf1, Tree::Leaf) && matches!(**leaf2, Tree::Leaf) =>
{
*x
}
Tree::Node(_, _, _) => todo!()
}
Note that leaf1 and leaf2 are dereferenced twice in order to be unboxed.
This is needed because t is originally a reference.
A default pattern is also needed.
See it on the playground.
Leaf is the name of a variable that is getting bound to part of that enum variant. You simply need to choose a different name for Leaf.
pub fn tree_sum(t:&Tree) -> i32 {
match t {
Tree::Leaf => 0,
Tree::Node(Leaf, x, Leaf2) => *x
// ^^^ different name
}
}
Related
I am trying to convert Rc<Vec<F>> into into Rc<Vec<T>> where T and F are numeric types like u8, f32, f64, etc. As the vectors may be quite large, I would like to avoid copying them if F and T are the same type. I do not manage to find out how to do that. Something like this -- it does not compile as the type comparison T == F is invalid:
fn convert_vec<F: num::NumCast + Copy, T: num::NumCast + Copy>(data: &[F], undef: T) -> Vec<T> {
data.iter()
.map(|v| match T::from(*v) {
Some(x) => x,
None => undef,
})
.collect()
}
fn convert_rc_vec<F: num::NumCast + Copy, T: num::NumCast + Copy>(
data: &Rc<Vec<F>>,
undef: T,
) -> anyhow::Result<Rc<Vec<T>>> {
if (T == F) { // invalid
Ok(data.clone()) // invalid
} else {
Ok(Rc::new(convert_vec(data, undef)))
}
}
The vector that I need to convert from is the response from a server which first sends the data type (something like "u8", "f32", "f64", ...) and then the actual data. At present, I store the vector with these data in enum like
pub enum Values {
UInt8(Rc<Vec<u8>>),
Float32(Rc<Vec<f32>>),
Float64(Rc<Vec<f64>>),
// ...
}
At compile time, I do not know in which format the server will send the data, i.e. I do not know F in advance. I do know T in every case I use it, but T might be a different type depending on the use case.
Using specialized functions like convert_rc_vec_to_f32 it is easy to handle the case where clone() is best. But that requires a separate function for each T with almost identical text. I am trying to find a more elegant solution than writing a macro or more or less repeating the code 9 times.
You should not try to prevent your function from being monomorphized with T and F being the same type, or even change its behavior in that case. Instead, you should not use it at all if it would be monomorphized in that case. This is possible because, if T and F were the same type, you would know it at compile time, so you could actually simply remove the function call at all.
It seems that you are actually storing all these vectors into an enum, which means you only know the actual type at run-time. But this doesn't mean my suggestion doesn't apply. Typically, if you wanted to get a vec of f32, you can do something like
match data {
Float32(v) => v,
Float64(v) => convert_rc_vec(v),
UInt8(v) => convert_rc_vec(v),
...
}
If T and F both have a 'static lifetime, then you can use TypeId to compare the two types "at runtime":
if TypeId::of::<T>() == TypeId::of::<F>() {
Ok(data.clone()) // invalid
} else {
/* ... */
}
However, since this comparison happens "at runtime", the type system still doesn't know that T == F inside of this branch. You can use unsafe code to force this "conversion":
if TypeId::of::<T>() == TypeId::of::<F>() {
Ok(unsafe {
// SAFETY: this is sound because `T == F`, so we're
// just helping the compiler along here, with no actual
// type conversions
Rc::<Vec<T>>::from_raw(
Rc::<Vec<F>>::into_raw(data.clone()) as *const _
)
})
} else {
/* ... */
}
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 {
'(' => { ... }
// ...
}
}
I wonder if there is a way to simplify the following pattern match arms when two or more different enum's types have the same data member or same function.
(if not it will be nice to explain why)
UPDATE:
as requested a more accurate example of what i want (forgive me for confusing data member access with function) (try it online):
struct Point<T> {
x: i32,
y: T,
}
enum Record {
V4(Point<i64>),
V6(Point<i32>),
}
fn get_record() -> Record {
Record::V4(Point{ x: 1, y: 1})
}
fn main() {
let x = match get_record() {
Record::V4(r) => r.x,
Record::V6(r) => r.x,
};
println!("{}", &x);
// this will not compile
// let rec = get_record();
// println!("{}", rec.x);
// this will not compile either
// note: if V4 Point was i32 it will compile & run
// let rec = get_record();
// let x = match get_record() {
// Record::V4(r) | Record::V6(r) => r.x,
// };
}
Original Post:
use std::net::IpAddr;
use std::str::FromStr;
fn main() {
let v4_or_v6 = IpAddr::from_str("1.2.3.4").unwrap();
// match expression, both arms only differ by 1 char
let s = match v4_or_v6 {
IpAddr::V4(ip) => ip.to_string(),
IpAddr::V6(ip) => ip.to_string(),
};
println!("{}", &s);
// not working:
// let s2 = match v4_or_v6 {
// IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
// };
// println!("{}", &s2);
}
I understand that the underlying call to to_string() has different implementation for Ipv4 than Ipv6 but i think the compiler can be smart enough to handle this (am i wrong?)
trying to compile with the commented out code results in compilation error (try it online):
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:16:37
|
16 | IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
| ^^ expected struct `std::net::Ipv4Addr`, found struct `std::net::Ipv6Addr`
|
= note: expected type `std::net::Ipv4Addr`
found type `std::net::Ipv6Addr`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.
The working code de-sugars to:
let s = match v4_or_v6 {
IpAddr::V4(ip) => <Ipv4Addr as ToString>::to_string(&ip),
IpAddr::V6(ip) => <Ipv6Addr as ToString>::to_string(&ip),
};
Even though the statements look the same, they are different functions and in each branch it is known statically which to_string is going to be used. To get this to work in a single match arm, you would have to somehow produce a trait object from the pattern match, so that each ip has the same type (i.e. &dyn ToString). Currently there isn't a way to do that and I haven't seen any proposal like it.
It's pretty common to see identical-looking match arms, where the same trait method is called on each, even in the rustc project. This is just how it is, for now.
If you have an enum where each variant holds types that implement the same traits, it might be convenient to implement the traits on the enum and delegate to the inner types. If you don't have a trait but your types have common structure (as in the x, y fields in the struct of your updated post), then you can provide an accessor on the enum:
impl Record {
fn x(&self) -> i32 {
match self {
Record::V4(Point { x, .. }) => *x,
Record::V6(Point { x, .. }) => *x,
}
}
}
While this is basically the same thing, it means you can write it once instead of everywhere that you need to access x:
let rec = get_record();
let x = get_record().x();
Note that IpAddr already does this so, in your original code, you could have avoided the match altogether with:
let s = v4_or_v6.to_string();
Is there a way to extract the tag of an enum to, e.g., use it as an index?
I'd like to have a vector of vectors classified by enum type, i.e., if I had:
enum Test {
Point {
x: i32,
y: i32,
},
Point2 {
a: i32,
b: i32,
},
Number(i32),
}
I may want a vector like this:
[[Test::Point{1, 2}, Test::Point{3, 4}], [Test::Number(1), Test::Number(2)]]
I'd like to dynamically add to this vector of vectors given new values. So if a function was passed in a Test::Number, it would go in the second array.
Certainly in the worst case I could explicitly pattern match every possible union value, but for large enums that would get rather verbose and repetitive since all the cases would just end up as
match e {
Test::Point { _, _ } => v[0].push(e),
Test::Point2 { _, _ } => v[1].push(e),
Test::Number(_) => v[2].push(e),
// imagine a lot of these
}
I've tried a lot of syntax permutations, but I haven't gotten anything that will compile. Is there a way to treat the enum struct tags like, well, an enumeration? It looks like there's a FromPrimitive derive, but it's unstable and doesn't work on struct enums.
(I suppose an alternative question if you can't is if you can write a macro to autowrite that match).
You can use .. to ignore all fields of an enum, no matter how many there are, and you can import variants from inside the enums namespace with use. E.g
enum Foo {
X { i: u8 },
Y(u8),
Z
}
fn bar(x: Foo) -> u32 {
use Foo::*;
match x {
X { .. } => 100,
Y(..) => 3,
Z => 12,
}
}
You can use the alternation syntax (via |) in the match arms and methods on the enum to reduce code duplication:
enum Test {
Point1,
Point2,
Point3,
Point4,
Number1,
Number2,
Number3,
Number4,
}
impl Test {
fn bucket(&self) -> u8 {
use Test::*;
match *self {
Point1 | Point2 | Point3 | Point4 => 0,
Number1 | Number2 | Number3 | Number4 => 1,
}
}
}
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));