With an:
enum Foo {
Bar,
Doe,
}
I want to be able to create an instance of Foo via e.g. let doe: Foo = 1.into().
Now I tried implementing From<u8> but cannot seem to make match working.
The following naive code does not work:
match b {
Foo::Doe as u8 => Foo::Doe,
}
How is one supposed to easily match all enum variants?
If you want the "naive" way of matching, you have to realize that obviously not every u8 will map onto a variant of your enum. Hence you can't use From but instead should use TryFrom. Then you just do
match b {
1 => Ok(Bar),
2 => Ok(Doe),
_ => Err(whatever_error_type_you_use)
}
But if you don't want to do it in a pedestrian way, you can use a custom crate like this one https://crates.io/crates/int-enum
Related
Ultimately, I'd like to be able to wrap a function in a proc_macro like this:
native_function! {
fn sum(x:i32, y:i32) -> i32 {
x+y
}
}
I'm trying to find what I need to put in this function:
#[proc_macro]
pub fn native_function(item: TokenStream) -> TokenStream {
...
}
Inside the function above, I need to be able to capture the types of the parameters defined in the function; in this case - "i32"
I've tried all sorts of ways to do this, but currently I'm stumped. I'm trying to avoid using a derive macro since I don't want to have to complete and instantiate a struct. I don't even need a full solution, but if someone can point me to the function/object/library I need to be using to that would be great.
syn is the cannonical parsing library for proc macros.
Using it this is easy:
let f = syn::parse_macro_input!(item as syn::ItemFn);
let types: Vec<syn::Type> = f
.sig
.inputs
.into_iter()
.filter_map(|arg| match arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(syn::PatType { ty, .. }) => Some(*ty),
})
.collect();
By the way, your macro can be an attribute macro.
If you want to know whethere the type is a some known type, note first that you can never be sure; that is because macros operate without type information, and code like struct i32; is legal and will shadow the primitive i32 type.
If you've accepted this limitation, what you actually want is to compare it to a path type. Path types are represented by the Path variant of syn::Type. It has qself, which is the T in <T as Trait>::AssociatedType and None in a simple path (not fully-qualified, just A::B::C), and path, which is the Trait::AssociatedType or A::B::C. Paths are complex; they can contain generics, for example, but if all you want is to check if this is one-segment type of simple identifier like i32, syn has got you covered: just use Path::is_ident(). So:
let is_i32 = match ty {
syn::Type::Path(ty) => ty.path.is_ident("i32"),
_ => false,
}
If you want to compare against a more complex type, you will have to walk and match segment-by-segment.
I am trying to implement a "transform" function that takes a non-copy enum value and and modifies it based on a parameter, but some of the parameters do nothing. A simplified example is:
enum NonCopy {
A,
B,
C
}
fn transform(to_transfrom: &mut NonCopy, param: u32) -> () {
*to_transfrom = match param {
// transformations
1 => NonCopy::A,
2 => NonCopy::B,
// retains the value
_ => *to_transfrom
};
}
I know that since the NonCopy doesn't implement the Copy trait the value inside to_transform cannot be moved out, however if param is neither 1 or 2 the value of *to_transform is assigned to itself, so it stays the same and nothing should be moved, but the compiler doesn't recognize that.
How can I achieve such a pattern with an assignment to a match expression?
I know I could instead assign in the match expression, but the non-example version is bigger and I do not want to repeat so much code plus it is quite ugly.
A neat little trick in Rust is that when you break out of an expression (via return, break, etc.), you don't actually need to provide a value for that expression. In this case, you can return from the match arm without providing a value:
enum NonCopy {
A,
B,
C
}
fn transform(to_transfrom: &mut NonCopy, param: u32) -> () {
*to_transfrom = match param {
// transformations
1 => NonCopy::A,
2 => NonCopy::B,
// retains the value
_ => return,
};
}
I'm new to the Rust language and as I usually do when trying to pick up a language, I like to go through Euler Project questions. I want to get familiar with cargo and everything it offers, so I've created a cargo project called euler-project.
On startup, I want the program to ask the user which solution to run and have it run a function in the code that corresponds to the solution requested by the user.
I like to avoid huge chains of if / else if / else blocks, so I thought the match function would work well. Here's what I have.
use std::io;
fn main() {
// Solution selection. Every solution will be selectable here
let mut selection = String::new();
// Enum to hold every solved problem number for match control flow
enum Solutions {
P1, P2,
}
loop {
println!("Select the solution you would like to run. (Example: '32' would be Problem 32)");
io::stdin()
.read_line(&mut selection)
.expect("Input should be an integer.");
match selection {
Solutions::P1 => p1(),
Solutions::P2 => p2(),
}
}
}
fn p1() {
println!("p1")
}
fn p2() {
}
Currently this produces an error as follows:
...
21 | match selection {
| --------- this expression has type `std::string::String`
22 | Solutions::P1 => p1(),
| ^^^^^^^^^^^^^ expected struct `std::string::String`, found enum `main::Solutions`
What am I doing wrong here? If you know a better option for doing this type of control, please suggest it to me. I am also curious to know if there is anything similar to Python's interactive console mode that allows a user to run the code in the terminal, and to make function calls by simply typing function names and hitting enter.
You're comparing apples and oranges here. On the one hand, you have the string you have read from the user, and on the other hand you have values of type Solutions, but nowhere do you tell the compiler how to compare the two. The easiest way to do what you want is just to compare strings directly:
match selection.trim() {
"1" => p1(),
"2" => p2(),
_ => panic!("{} does not designate a valid problem!", selection),
}
Note the call to trim: the string returned by read_line includes the trailing newline, which you need to remove so that the comparisons will work.
Note also that you will need to call selection.clear() before read_line, otherwise the next iteration of the loop will append to the string instead of replacing it.
You can't match an enum with a String. You could try something like:
match selection.trim().parse::<i32>() {
Ok(x) if x == (Solutions::P1 as i32) => p1(),
Ok(x) if x == (Solutions::P2 as i32)=> p2(),
x => panic!{x},
}
I have data contained inside a Box, and would like to pattern match on it without accidentally copying the Box's contents from the heap to the stack; how do I do that?
Let's assume the following code:
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match *boxed_value {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
Does this copy the enum out of the box onto the stack and pattern match on that copy, or does it do the matching directly on the value pointed to by the box?
What about this variant?
use std::ops::Deref;
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match boxed_value.deref() {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
It seems that simply dereferencing a box does not automatically create a copy, otherwise one would not be able to create a reference to the contained value by using let x = &*boxed_value. This leads to a question about this syntax:
enum SomeEnum {
SomeEntry,
AnotherEntry,
}
fn main() {
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match &*boxed_value {
SomeEnum::SomeEntry => {}
SomeEnum::AnotherEntry => {}
}
}
First: in Rust, there are no implicit costly copies, unlike in, for example, C++. Whereas in C++, the default action is "deep copy" (via copy constructor or similar), the default action in Rust is moving. A move is a shallow copy which (a) is usually very small and cheap and (b) can be removed by the optimizer in most cases. To get deep clones in Rust you have manually use .clone(). If you don't do that, you usually don't really have to worry about this.
Second: matching on an enum only looks at the discriminant of that enum (unless you bind enum fields, see below). That's the "tag" or the "metadata" which specifies which variant of the enum is stored in a value. That tag is tiny: it fits in 8 bits in almost all cases (enums with more than 256 variants are rare). So you don't need to worry about that. And in your case, we have a C-like enum without any fields. So the enum only stores the tag and hence is tiny, too.
So what about enum fields that might be costly to copy? Like this:
enum SomeEnum {
SomeEntry(String),
AnotherEntry,
}
let boxed_value = Box::new(SomeEnum::AnotherEntry);
match *boxed_value {
SomeEnum::SomeEntry(s) => drop::<String>(s), // make sure we own the string
SomeEnum::AnotherEntry => {},
}
So in this case one variant stores a String. Since deep-copying a string is somewhat costly, Rust won't do it implicitly. In the match arm we try to drop s and assert it's a String. That means we (meaning: the body of the match arm) own the string. So, if the match arm owns it but we didn't get the owned value from cloning it, that means that the outer function doesn't own it anymore. And in fact, if you try to use boxed_value after the match, you will get move errors from the compiler. So again, either you get a compiler error or no bad things automatically happen.
Furthermore, you can write SomeEnum::SomeEntry(ref s) in the match. In that case, the string is bound by reference to s (so the drop() call won't work anymore). In that case, we never move from boxed_value. This is something I call "deferred moving", but I'm not sure if that's an official term for it. But it just means: when pattern matching, the input value is not moved at all until a binding in the pattern moves from it.
Lastly, please take a look at this code and the generated assembly. The assembly is optimal. So once again: while you might be worried about accidental clones when you come from the C++ world, this is not really something you need to worry about in Rust.
I'm trying simply to convert an inputted string into an integer. I know this can be done by applying unwrap to from_str but I figured I'd try to handle the error case properly and extract the value with a match instead. Instead of it working as expected I just get this error...
error: unable to infer enough type information about _; type annotations requiredwhich points to thexinSome(x)`
fn main () {
let ref num = os::args()[1];
match from_str(num.as_slice()) {
Some(x) => println!("{}", x),
None => println!("string cannot be converted to int"),
}
}
What am I missing?
Let’s look at the definition of from_str:
pub fn from_str<A: FromStr>(s: &str) -> Option<A>
The return type, you see, may be an Option of any type that implements FromStr, such as int, u8 and IpAddr. If you don’t say explicitly what A is, by changing from_str(…) to from_str::<int>(…) or similar, the compiler will try to figure out what it can be. In this case, it will look forward and see that x is of type A and based on the usage of x, it must implement std::fmt::Show as well. There is nothing else that it can infer.
Well then, what types implement both FromStr and Show? There are quite a few of them, and as soon as there’s more than one it is impossible for the compiler to decide what you meant, so it gives up.
The solution is to stipulate what type you are wishing to parse that argument as, by writing from_str::<int>(num.as_slice()) or similar.
The return type of from_str is generic. It can return any type that implements FromStr, wrapped in an Option. The compiler could not infer what type you want from the value's usage, because println! is also generic. Is it i32, f64, bool, something else?
You can specify the type you want on the from_str call:
fn main() {
let ref num = os::args()[1];
match from_str::<i32>(num.as_slice()) {
Some(x) => println!("{}", x),
None => println!("string cannot be converted to int"),
}
}