This question already has answers here:
Can I convert a string to enum without macros in Rust?
(3 answers)
Closed 12 months ago.
I know how to do it in Java.
It seems I need to implement TryFrom or something like that.
enum ROMAN {
I = 1,
V = 5,
X = 10,
L = 50,
C = 100,
D = 500,
M = 1000
}
I want to get value using by enums name.
println!("{:?}", ROMAN::valueOf("M")); // It should be `1000`
Either implement FromStr or TryFrom manually, or use something like enum_derive which provides these features.
Or just add a bespoke value_of method on your enum without bothering with traits.
Or do all of it, though that seems a bit much
impl Roman {
pub fn value_of(s: &str) -> Option<Self> {
Some(match s {
"I" => Self::I,
"V" => Self::V,
"X" => Self::X,
"L" => Self::L,
"C" => Self::C,
"D" => Self::D,
"M" => Self::M,
_ => return None,
})
}
}
impl FromStr for Roman {
type Err = (); // should probably provide something more useful
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::value_of(s).ok_or(())
}
}
impl TryFrom<&str> for Roman {
type Error = ();
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::value_of(s).ok_or(())
}
}
println!("{:?}", ROMAN::valueOf("M")); // It should be `1000`
It's never going to be 1000 because that's not how Rust works. You'd need to handle the error then convert the success value to its discriminant.
Related
I have an enum with over 100 variants. and I have to get each of its variants from a string. Something like this.
enum VeryLongEnum {
A,
B,
C,
D,
E,
}
impl From<&'static str > for VeryLongEnum {
fn from(s: &'static str) -> VeryLongEnum {
match s {
"A" => VeryLongEnum::A,
"B" => VeryLongEnum::B,
"C" => VeryLongEnum::C,
"D" => VeryLongEnum::D,
"E" => VeryLongEnum::E,
}
}
}
But I don't want to write all the variants one by one, it's crazy.
I'm trying to create a mini macro to simplify this, I did something like this.
macro_rules! from_str {
( $s:expr ) => {{
let t: VeryLongEnum = VeryLongEnum::$s;
t
}};
}
to use like this.
let variant = "A";
let en = from_str!(variant);
But I'm having an error which says expected a identifier.
I understand that identifiers and expresions are different types of token trees, but the question is how can I "force" this to generate the enum variant with the literal?
let variant = "A";
let en = from_str!(variant);
variant is a string that exists at runtime, you cannot pass it to a macro like that.
An alternative is to define a macro that defines the enum as well as the string conversion. This macro can use the stringify! macro in Rust to convert at identifier to a static string that can be passed to pattern match. Since the conversion is fallible, you should define a TryFrom instead of From (or FromStr which allows you to call "A".parse()).
macro_rules! go {
($($ident:ident)+) => {
#[derive(Debug)]
enum VeryLongEnum {
$($ident,)+
}
impl TryFrom<&'static str> for VeryLongEnum {
type Error = &'static str;
fn try_from(s: &'static str) -> Result<VeryLongEnum, &'static str> {
match s {
$(stringify!($ident) => Ok(VeryLongEnum::$ident),)+
_ => Err("Invalid String")
}
}
}
}
}
go! { A B C D E }
fn main() {
let _ = dbg!(VeryLongEnum::try_from("A"));
let _ = dbg!(VeryLongEnum::try_from("E"));
let _ = dbg!(VeryLongEnum::try_from("F"));
}
Output:
[src/main.rs:24] VeryLongEnum::try_from("A") = Ok(
A,
)
[src/main.rs:25] VeryLongEnum::try_from("E") = Ok(
E,
)
[src/main.rs:26] VeryLongEnum::try_from("F") = Err(
"Invalid String",
)
Playground
Is there a succinct way to deserialize a variant of a fieldless enum from either its name or discriminant value? e.g. given this enum—
enum Foo {
A = 1,
B = 2,
C = 3,
}
—I’d like any of these strings or numbers to represent it:
{
"example-a": "a", // Foo::A
"example-b": "b", // Foo::B
"example-c": "c", // Foo::C
"example-1": 1, // Foo::A
"example-2": 2, // Foo::B
"example-3": 3, // Foo::C
}
I’ve seen that deriving Deserialize accommodates the former group and Deserialize_repr the latter, but I’m not sure how to accommodate both simultaneously.
I expected that a shorthand like #[serde(alias = …)] might exist to cover this scenario.
There is not a built-in shortcut that supports this directly. You will have to implement Deserialize manually. It's neither trivial nor super complicated:
impl<'de> serde::Deserialize<'de> for Foo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>
{
struct FooVisitor;
impl<'de> serde::de::Visitor<'de> for FooVisitor {
type Value = Foo;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "an integer or string representing a Foo")
}
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Foo, E> {
Ok(match s {
"a" => Foo::A,
"b" => Foo::B,
"c" => Foo::C,
_ => return Err(E::invalid_value(serde::de::Unexpected::Str(s), &self)),
})
}
fn visit_u64<E: serde::de::Error>(self, n: u64) -> Result<Foo, E> {
Ok(match n {
1 => Foo::A,
2 => Foo::B,
3 => Foo::C,
_ => return Err(E::invalid_value(serde::de::Unexpected::Unsigned(n), &self)),
})
}
}
deserializer.deserialize_any(FooVisitor)
}
}
Note that using deserialize_any means we are relying on the data format being self-describing; i.e., that the deserializer knows whether the data is stringy or integerish and will call the correct visit_ method accordingly. Serde also supports non-self-describing formats; however, you won't be able to use them with this Deserialize implementation.
This question already has answers here:
How do you access enum values in Rust?
(6 answers)
How do I get an enum as a string?
(2 answers)
Closed 4 years ago.
I'm implementing FizzBuzz in Rust. I went from the simplest version to using an enum and I can't resolve it. I've read enums are very potent so I tried to use them to their fullest.
Here is my implementation:
use std::fmt;
fn main() {
for i in 1..100 {
println!("{}", fizzbuzz(i))
}
}
fn fizzbuzz(value: i32) -> Answer {
use crate::Answer::*;
return match (value % 3, value % 5) {
(0, 0) => FizzBuzz,
(0, _) => Fizz,
(_, 0) => Buzz,
(_, _) => Nothing(value),
};
}
enum Answer {
FizzBuzz,
Fizz,
Buzz,
Nothing(i32),
}
impl fmt::Display for Answer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::Answer::*;
match self {
FizzBuzz => write!(f, "FizzBuzz"),
Fizz => write!(f, "Fizz"),
Buzz => write!(f, "Buzz"),
Nothing() => write!(f, "number passed to NoBuzz"),
}
}
}
I have 2 problems:
how to use the name of actual enum in match self block?
In Java, I could use just FizzBuzz.name() and it would print
"FizzBuzz" - is it possible in Rust?
is it possible to print that value I passed to Nothing in fizzbuzz
function?
You can derive a printable representation for debugging purposes with #[derive(Debug)]. This would let you print it out using println!("{:?}", self)
Example:
#[derive(Debug)]
enum Answer {
FizzBuzz,
Fizz,
Buzz,
Nothing(i32),
}
match self {
Nothing(_) -> /* ... */,
other -> println!("{:?}", other),
}
However, it's much more appropriate to write a Display instance yourself. You'll have to do the translation (Fizz -> "Fizz", etc) yourself, but then it will be in a centralized location and you can pass it to formatters like you've been doing.
To get the value out of the Nothing, you simply need to pattern match on it and give it a name. Instead of
Nothing(_) => // Do something generic
consider
Nothing(n) => // Do something that involves the number n
I am attempting to implement a new trait for a String that has a function that capitalizes the first letter of each String and un-capitalizes the rest. I am basing the function's interface on to_uppercase() and to_lowercase() in the Rust Standard Library.
use std::io;
trait ToCapitalized {
fn to_capitalized(&self) -> String;
}
impl ToCapitalized for String {
fn to_capitalized(&self) -> String {
self.chars().enumerate().map(|(i, c)| {
match i {
0 => c.to_uppercase(),
_ => c.to_lowercase(),
}
}).collect()
}
}
fn main() {
let mut buffer = String::new();
io::stdin().read_line(&mut buffer).ok().expect("Unable to read from stdin.");
println!("{}", buffer.to_capitalized());
}
This code is based on a suggestion given here, but the code is outdated and causes multiple compilation errors. The only issue I am having with my implementation now is the following error:
src/main.rs:10:13: 13:14 error: match arms have incompatible types [E0308]
src/main.rs:10 match i {
^
src/main.rs:10:13: 13:14 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:10:13: 13:14 note: expected type `std::char::ToUppercase`
src/main.rs:10:13: 13:14 note: found type `std::char::ToLowercase`
src/main.rs:12:22: 12:38 note: match arm with an incompatible type
src/main.rs:12 _ => c.to_lowercase(),
So in short, the return values of fn to_uppercase(&self) -> ToUppercase and fn to_lowercase(&self) -> ToLowercase can't be collected together because the map now has multiple return types.
I've attempted trying to cast them to another common Iterator type such as Bytes and Chars, but these iterator types can't be collected to form a String. Any suggestions?
Casting is rarely a good approach to solving type issues in Rust. The correct solution here would be to write (or find a crate that defines) a type that unifies disparate iterator types. But that would require effort, so it's simpler to just throw collect out the window:
trait ToCapitalized {
fn to_capitalized(&self) -> String;
}
impl ToCapitalized for String {
fn to_capitalized(&self) -> String {
let mut r = String::with_capacity(self.len());
for (i, c) in self.chars().enumerate() {
match i {
0 => r.extend(c.to_uppercase()),
_ => r.extend(c.to_lowercase()),
}
}
r
}
}
fn main() {
let buffer = String::from("canberra");
println!("{}", buffer.to_capitalized());
}
This is, more or less, what collect would do anyway if you had some type to represent "either ToUppercase or ToLowercase". In the vast majority of cases, this will also only perform a single allocation.
Here's how I would do it:
trait ToCapitalized {
fn to_capitalized(&self) -> String;
}
impl ToCapitalized for String {
fn to_capitalized(&self) -> String {
match self.chars().next() {
Some(c) => {
c.to_uppercase()
.chain(self.chars().skip(1).flat_map(|c| c.to_lowercase()))
.collect()
}
None => String::new(),
}
}
}
fn main() {
println!("{}", "fOoBaR".to_string().to_capitalized());
}
This will be a little slower than the ideal solution, as it decodes the first char twice, but it's quite readable IMO.
Output:
Foobar
After looking at the implementation for pub fn to_uppercase(&self) -> String here, I devised a solution that is a bit of a hybrid between Dogbert and DK.'s solutions and the implementation given in the standard library. It even works with Unicode!
fn to_capitalized(&self) -> String {
match self.len() {
0 => String::new(),
_ => {
let mut s = String::with_capacity(self.len());
s.extend(self.chars().next().unwrap().to_uppercase());
s.extend(self.chars().skip(1).flat_map(|c| c.to_lowercase()));
return s;
}
}
}
Working Rust Playground Example
Edit: For greater visibility, Shepmaster's simplified and optimized solution:
fn to_capitalized(&self) -> String {
let mut s = String::with_capacity(self.len());
let mut chars = self.chars();
s.extend(chars.by_ref().take(1).flat_map(|c| c.to_uppercase()));
s.extend(chars.flat_map(|c| c.to_lowercase()));
s
}
This question already has answers here:
How do I stop iteration and return an error when Iterator::map returns a Result::Err?
(4 answers)
Closed 3 years ago.
I have code like this:
let things = vec![/* ...*/]; // e.g. Vec<String>
things
.map(|thing| {
let a = try!(do_stuff(thing));
Ok(other_stuff(a))
})
.filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
})
.map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
})
.collect::<Result<Vec<_>, _>>()
In terms of semantics, I want to stop processing after the first error.
The above code works, but it feels quite cumbersome. Is there a better way? I've looked through the docs for something like filter_if_ok, but I haven't found anything.
I am aware of collect::<Result<Vec<_>, _>>, and it works great. I'm specifically trying to eliminate the following boilerplate:
In the filter's closure, I have to use match on thing_result. I feel like this should just be a one-liner, e.g. .filter_if_ok(|thing| check(a)).
Every time I use map, I have to include an extra statement let a = try!(thing_result); in order to deal with the possibility of an Err. Again, I feel like this could be abstracted away into .map_if_ok(|thing| ...).
Is there another approach I can use to get this level of conciseness, or do I just need to tough it out?
There are lots of ways you could mean this.
If you just want to panic, use .map(|x| x.unwrap()).
If you want all results or a single error, collect into a Result<X<T>>:
let results: Result<Vec<i32>, _> = result_i32_iter.collect();
If you want everything except the errors, use .filter_map(|x| x.ok()) or .flat_map(|x| x).
If you want everything up to the first error, use .scan((), |_, x| x.ok()).
let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok());
Note that these operations can be combined with earlier operations in many cases.
Since Rust 1.27, Iterator::try_for_each could be of interest:
An iterator method that applies a fallible function to each item in the iterator, stopping at the first error and returning that error.
This can also be thought of as the fallible form of for_each() or as the stateless version of try_fold().
You can implement these iterators yourself. See how filter and map are implemented in the standard library.
map_ok implementation:
#[derive(Clone)]
pub struct MapOkIterator<I, F> {
iter: I,
f: F,
}
impl<A, B, E, I, F> Iterator for MapOkIterator<I, F>
where
F: FnMut(A) -> B,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<B, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|x| x.map(&mut self.f))
}
}
pub trait MapOkTrait {
fn map_ok<F, A, B, E>(self, func: F) -> MapOkIterator<Self, F>
where
Self: Sized + Iterator<Item = Result<A, E>>,
F: FnMut(A) -> B,
{
MapOkIterator {
iter: self,
f: func,
}
}
}
impl<I, T, E> MapOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
filter_ok is almost the same:
#[derive(Clone)]
pub struct FilterOkIterator<I, P> {
iter: I,
predicate: P,
}
impl<I, P, A, E> Iterator for FilterOkIterator<I, P>
where
P: FnMut(&A) -> bool,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<A, E>;
#[inline]
fn next(&mut self) -> Option<Result<A, E>> {
for x in self.iter.by_ref() {
match x {
Ok(xx) => if (self.predicate)(&xx) {
return Some(Ok(xx));
},
Err(_) => return Some(x),
}
}
None
}
}
pub trait FilterOkTrait {
fn filter_ok<P, A, E>(self, predicate: P) -> FilterOkIterator<Self, P>
where
Self: Sized + Iterator<Item = Result<A, E>>,
P: FnMut(&A) -> bool,
{
FilterOkIterator {
iter: self,
predicate: predicate,
}
}
}
impl<I, T, E> FilterOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
Your code may look like this:
["1", "2", "3", "4"]
.iter()
.map(|x| x.parse::<u16>().map(|a| a + 10))
.filter_ok(|x| x % 2 == 0)
.map_ok(|x| x + 100)
.collect::<Result<Vec<_>, std::num::ParseIntError>>()
playground
filter_map can be used to reduce simple cases of mapping then filtering. In your example there is some logic to the filter so I don't think it simplifies things. I don't see any useful functions in the documentation for Result either unfortunately. I think your example is as idiomatic as it could get, but here are some small improvements:
let things = vec![...]; // e.g. Vec<String>
things.iter().map(|thing| {
// The ? operator can be used in place of try! in the nightly version of Rust
let a = do_stuff(thing)?;
Ok(other_stuff(a))
// The closure braces can be removed if the code is a single expression
}).filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
}
).map(|thing_result| {
let a = thing_result?;
// do stuff
b
})
The ? operator can be less readable in some cases, so you might not want to use it.
If you are able to change the check function to return Some(x) instead of true, and None instead of false, you can use filter_map:
let bar = things.iter().filter_map(|thing| {
match do_stuff(thing) {
Err(e) => Some(Err(e)),
Ok(a) => {
let x = other_stuff(a);
if check_2(x) {
Some(Ok(x))
} else {
None
}
}
}
}).map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
}).collect::<Result<Vec<_>, _>>();
You can get rid of the let a = try!(thing); by using a match in some cases as well. However, using filter_map here doesn't seem to help.