How to implement iter method using static/dynamic dispatch? - rust

I need to implement the method iter, which returns something which implements the trait Iterator<Item = char>. But the return value will be different implementations, depending on the enum variant.
Something like this:
pub enum Class {
SingleChar(char),
Range(Range),
And(Vec<Class>),
Or(Vec<Class>),
}
impl Class {
pub fn iter(&self) -> Iterator<Item = char> {
match *self {
Class::SingleChar(c) => vec![c],
Class::Range(ref range) => range.iter(),
Class::And(ref classes) => {
let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
match iter {
None => Some(class.iter()),
Some(iter) => Some(iter.merge(class.iter())),
}
});
Box::new(iter.unwrap())
},
Class::Or(ref classes) => {
let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
match iter {
None => Some(class.iter()),
Some(iter) => Some(iter.interleave(class.iter())),
}
});
Box::new(iter.unwrap())
},
}
}
}
range.iter() returns a struct that implements Iterator<Item=char>.
merge and interleave are itertools methods, which return MergeAscend and Interleave respectively (both of them implement Iterator<Item=char>)
How to implement such a scheme using static dispatch?
If static dispatch is not possible, how to implement such a scheme using dynamic dispatch?

It is not possible to do it using static dispatch. There is a tracking RFC issue on unboxed abstract return types, but Rust is not there yet (and I'm not sure if it could cover the use case of returning different types). Therefore, dynamic dispatch is the way to go.
You're pretty close, actually. Just make the return type Box<Iterator<Item=char>> and add more boxing:
pub fn iter(&self) -> Box<Iterator<Item=char>> {
match *self {
Class::SingleChar(c) => Box::new(Some(c).into_iter()),
Class::Range(ref range) => Box::new(range.iter()),
Class::And(ref classes) => {
let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
match iter {
None => Some(Box::new(class.iter())),
Some(iter) => Some(Box::new(iter.merge(class.iter()))),
}
});
iter.unwrap()
},
Class::Or(ref classes) => {
let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
match iter {
None => Some(Box::new(class.iter())),
Some(iter) => Some(Box::new(iter.interleave(class.iter()))),
}
});
iter.unwrap()
},
}
}
This should work.

Related

How to access nested enums without full match syntax

I am using the Serde crate to deserialise a JSON file, which has a nested structure like this:
struct Nested {
a: Vec<Foo>,
b: u8,
}
struct Foo {
c: Bar,
d: Vec<f32>,
}
Struct Bar {
e: u32,
f: String,
}
Part of the applications purpose is to check for missing parameters (or incorrect types in parameters), and then display a nicely printed list of errors found in the file, so I need to handle the structure missing parameters or wrongly typed.
I came across this great post that helped solved my issue, by wrapping each parameter in an enum result that contains the value if it passed, the value if it failed, or a final enum if it was missing (since the nested structures might also be missing I wrapped them in the same enum):
pub enum TryParse<T> {
Parsed(T),
Unparsed(Value),
NotPresent
}
struct Nested {
a: TryParse<Vec<Foo>>,
b: TryParse<u8>,
}
struct Foo {
c: TryParse<Bar>,
d: TryParse<Vec<f32>>,
}
Struct Bar {
e: TryParse<u32>,
f: TryParse<String>,
}
However, I'm not sure how to access them now without unpacking every step into a match statement. For example, I can access B very easily:
match file.b {
Parsed(val) => {println!("Got parameter of {}", val)},
Unparsed(val) => {println!("Invalid type: {:?}", val)}
NotPresent => {println!("b not found")},
};
However, I'm not sure how to access the nested ones (C D E and F). I can't use Unwrap or expect since this isn't technically a 'Result', so how do I unpack these?:
if file.a.c.e.Parsed() && file.a.c.e == 32 {... //invalid
if file.a.d && file.a.d.len() == 6... //invalid
I know in a way this flies against rust's 'handle every outcome' philosophy, and I want to handle them, but I want to know if there is a nicer way than 400 nested match statements (the above example is very simplified, the files I am using have up to 6 nested layers, each parameter in the top node has at least 3 layers, some are vectors as well)…
Perhaps I need to implement a function similar to unwrap() on my 'TryParse'? or would it be better to wrap each parameter in a 'Result', extend that with the deserialise trait, and then somehow store the error in the Err option that says if it was a type error or missing parameter?
EDIT
I tried adding the following, some of which works and some of which does not:
impl <T> TryParse<T> {
pub fn is_ok(self) -> bool { //works
match self {
Self::Parsed(_t) => true,
_ => false,
}
}
pub fn is_absent(self) -> bool { //works
match self {
Self::NotPresent => true,
_ => false,
}
}
pub fn is_invalid(self) -> bool { //works
match self {
Self::Unparsed(_) => true,
_ => false,
}
}
pub fn get(self) -> Result<T, dyn Error> { //doesnt work
match self {
Self::Parsed(t) => Ok(t),
Self::Unparsed(e) => Err(e),
Self::NotPresent => Err("Invalid")
}
}
}
I can't believe it is this hard just to get the result, should I just avoid nested enums or get rid of the TryParse enums/ functions all together and wrap everything in a result, so the user simply knows if it worked or didn't work (but no explanation why it failed)
Implementing unwrap() is one possibility. Using Result is another, with a custom error type. You can deserialize directly into result with #[serde(deserialize_with = "...")], or using a newtype wrapper.
However, a not-enough-used power of pattern matching is nested patterns. For example, instead of if file.a.c.e.Parsed() && file.a.c.e == 32 you can write:
if let TryParse::Parsed(a) = &file.a {
// Unfortunately we cannot combine this `if let` with the surrounding `if let`,
// because `Vec` doesn't support pattern matching (currently).
if let TryParsed::Parsed(
[Foo {
c:
TryParse::Parsed(Bar {
e: TryParse::Parsed(32),
..
}),
..
}, ..],
) = a.as_slice()
{
// ...
}
}
May not be the most Rust-y way of doing it, but for those like me moving from another language like C/Python/C++, this is the way I have done it that still allows me to quickly validate if I have an error and use the match syntax to handle it. Thanks to #Chayim Friedman for assisting with this, his way is probably better but this made the most sense for me:
#[derive(Debug)]
pub enum TryParse<T> {
Parsed(T),
Unparsed(Value),
NotPresent
}
impl<'de, T: DeserializeOwned> Deserialize<'de> for TryParse<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
match Option::<Value>::deserialize(deserializer)? {
None => Ok(TryParse::NotPresent),
Some(value) => match T::deserialize(&value) {
Ok(t) => Ok(TryParse::Parsed(t)),
Err(_) => Ok(TryParse::Unparsed(value)),
},
}
}
}
impl <T> TryParse<T> {
//pub fn is_ok(self) -> bool { ---> Use get().is_ok(), built into result
// match self {
// Self::Parsed(_t) => true,
// _ => false,
// }
//}
pub fn is_absent(self) -> bool {
match self {
Self::NotPresent => true,
_ => false,
}
}
pub fn is_invalid(self) -> bool {
match self {
Self::Unparsed(_) => true,
_ => false,
}
}
pub fn get(&self) -> Result<&T, String> {
match self {
Self::Parsed(t) => Ok(t),
Self::Unparsed(v) => Err(format!("Unable to Parse {:?}", v)),
Self::NotPresent => Err("Parameter not Present".to_string())
}
}
// pub fn get_direct(&self) -> &T {
// match self {
// Self::Parsed(t) => t,
// _ => panic!("Can't get this value!"),
// }
// }
}
match &nested.a.get().unwrap()[1].c.get.expect("Missing C Parameter").e{
Parsed(val) => {println!("Got value of E: {}", val)},
Unparsed(val) => {println!("Invalid Type: {:?}", val)}
NotPresent => {println!("Param E Not Found")},
};
//Note the use of '&' at the beginning because we need to borrow a reference to it
I know I need to change my mindset to use the rust way of thinking, and I am completely open to other suggestions if they can demonstrate some working code.

Access the value of an enum variant

I am working on some language bindings to Arrayfire using the arrayfire-rust crate.
Arrayfire has a typed struct Array<T> which represents a matrix. All acceptable types implement the HasAfEnum trait. This trait has a number of associated types, whose values are not the same for the types that implement this trait.
Since I need a reference to the array in a Rwlock for safe language interop, I have defined the following struct:
pub struct ExAfRef(pub RwLock<ExAfArray>);
impl ExAfRef {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
Self(RwLock::new(ExAfArray::new(slice, dim, dtype)))
}
pub fn value(&self) -> ExAfArray {
match self.0.try_read() {
Ok(refer) => (*refer),
Err(_) => unreachable!(),
}
}
}
which is contained by a struct:
pub struct ExAf {
pub resource: ResourceArc<ExAfRef>,
}
impl ExAf {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
Self {
resource: ResourceArc::new(ExAfRef::new(slice, dim, dtype)),
}
}
// This function is broken
pub fn af_value<T: HasAfEnum>(&self) -> &Array<T> {
self.resource.value().value()
}
}
With the help of the following enum:
pub enum ExAfArray {
U8(Array<u8>),
S32(Array<i32>),
S64(Array<i64>),
F32(Array<f32>),
F64(Array<f64>),
}
impl ExAfArray {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
let array = Array::new(slice, dim);
match dtype {
ExAfDType::U8 => ExAfArray::U8(array),
ExAfDType::S32 => ExAfArray::S32(array.cast::<i32>()),
ExAfDType::S64 => ExAfArray::S64(array.cast::<i64>()),
ExAfDType::F32 => ExAfArray::F32(array.cast::<f32>()),
ExAfDType::F64 => ExAfArray::F64(array.cast::<f64>()),
}
}
// This function is broken
pub fn value<T: HasAfEnum>(&self) -> &Array<T> {
// match self {
// ExAfArray::U8(array) => array,
// ExAfArray::S32(array) => array,
// ExAfArray::S64(array) => array,
// ExAfArray::F32(array) => array,
// ExAfArray::F64(array) => array,
// }
if let ExAfArray::U8(array) = self {
return array;
} else if let ExAfArray::S32(array) = self {
return array;
} else if let ExAfArray::S64(array) = self {
return array;
} else if let ExAfArray::F32(array) = self {
return array;
} else {
let ExAfArray::F64(array) = self;
return array;
}
}
pub fn get_type(&self) -> ExAfDType {
match self {
ExAfArray::U8(array) => ExAfDType::U8,
ExAfArray::S32(array) => ExAfDType::S32,
ExAfArray::S64(array) => ExAfDType::S64,
ExAfArray::F32(array) => ExAfDType::F32,
ExAfArray::F64(array) => ExAfDType::F64,
}
}
}
I have used an enum because generic structs are not supported in my language-interop "framework" and because the HasAfEnum trait has associated types (hence dynamic dispatch using dyn is not viable (at least to my knowledge)).
This has worked fine for initializing new arrays.
However when I need to apply some operation on an array, I need to be able to access the value stored by the enum variant. However I am unable to write a type signature for a function to access the value, as dynamic dispatch is not usable and generics are too boilerplate.
Since all variants are tuples, is there some way I can access the value of the tuple variant using a built-in enum feature?
EDIT:
I am using rustler
In short, no there is not a way to do what you seem to be trying to do in Rust presently.
Your functions are broken because you are trying to use generics orthogonally to how they work. When a generic function is called in Rust, the caller fills in the type parameters, not the callee. However, your enum in a sense "knows" what the concrete array type is, so only it can determine what that type parameter is supposed to be. If this mismatch is blocking your progress, this usually calls for a reconsideration of your code structure.
This also explains why there is no built-in enum method that does what you're trying to do. That method would run into the same issue as your value method. When you want to inspect the contents of an enum in Rust, you need to pattern match on it.
There is at least one way to try to accomplish your goal, but I would not really recommend it. One change that makes the code closer to being viable is by passing a closure into the function to make the modification, (the syntax below is not currently valid Rust but it gets the idea across):
pub fn modify<'a, F>(&'a self, op: F)
where
F: for<T: HasAfEnum> FnOnce(&'a Array<T>)
{
// This looks repetitive, but the idea is that in each branch
// the type parameter T takes on the appropriate type for the variant
match self {
ExAfArray::U8(array) => op(array),
ExAfArray::S32(array) => op(array),
ExAfArray::S64(array) => op(array),
ExAfArray::F32(array) => op(array),
ExAfArray::F64(array) => op(array),
}
}
Unfortunately the for<T> FnTrait(T) syntax does not exist yet and I'm not even sure if there's a proposal for it to be added. This can be worked around through a macro:
pub(crate) fn call_unary<F, T, U>(arg: T, f: F) -> U
where F: FnOnce(T) -> U {
f(arg)
}
macro_rules! modify {
($ex_af_array:expr, $op:expr) => {
match &$ex_af_array {
ExAfArray::U8(array) => call_unary(array, $op),
ExAfArray::S32(array) => call_unary(array, $op),
ExAfArray::S64(array) => call_unary(array, $op),
ExAfArray::F32(array) => call_unary(array, $op),
ExAfArray::F64(array) => call_unary(array, $op),
}
};
}
The call_unary helper is needed to ensure type inference works properly. ($op)(array) will fail to compile when the types of the arguments to $op need to be inferred.
Now this solution mostly covers the functionality that for<T> FnTrait(T) would provide, but it's not very clean code (especially after the macro body is sanitized), and the compiler errors will be poor if the macro is misused.

Function that generates a HashMap of Enum variants

I'm working with apollo_parser to parse a GraphQL query. It defines an enum, apollo_parser::ast::Definition, that has several variants including apollo_parser::ast::OperationDefintion and apollo_parser::ast::FragmentDefinition. I'd like to have a single Trait I can apply to apollo_parser::ast::Definition that provides a function definition_map that returns a HashMap mapping the operation name to the variant instance.
I've got as far as the trait, but I don't know how to implement it. Also, I don't know how to constrain T to be a variant of Definition.
trait Mappable {
fn definition_map<T>(&self) -> HashMap<String, T>;
}
EDIT:
Here's a Rust-ish pseudocode implementation.
impl Mappable for Document {
fn definition_map<T>(&self) -> HashMap<String, T> {
let defs = Vec<T> = self.definitions
.filter_map(|def: Definition| match def {
T(foo) => Some(foo),
_ => None
}).collect();
let map = HashMap::new();
for def: T in definitions {
map.insert(def.name(), def);
}
map
}
}
and it would output
// From a document consisting of OperationDefinitions "operation1" and "operation2"
// and FragmentDefinitons "fragment1" and "fragment2"
{
"operation1": OperationDefinition(...),
"operation2": OperationDefinition(...),
}
{
"fragment1": FragmentDefinition(...),
"fragment2": FragmentDefinition(...)
}
I don't know how to constrain T to be a variant of Definition.
There is no such thing in Rust. There's the name of the variant and the name of the type contained within that variant, there is no relationship between the two. The variants can be named whatever they want, and multiple variant can contain the same type. So there's no shorthand for pulling a T out of an enum which has a variant with a T.
You need to make your own trait that says how to get a T from a Definition:
trait TryFromDefinition {
fn try_from_def(definition: Definition) -> Option<Self> where Self: Sized;
fn name(&self) -> String;
}
And using that, your implementation is simple:
impl Mappable for Document {
fn definition_map<T: TryFromDefinition>(&self) -> HashMap<String, T> {
self.definitions()
.filter_map(T::try_from_def)
.map(|t| (t.name(), t))
.collect()
}
}
You just have to define TryFromDefinition for all the types you want to use:
impl TryFromDefinition for OperationDefinition {
fn try_from_def(definition: Definition) -> Option<Self> {
match definition {
Definition::OperationDefinition(operation) => Some(operation),
_ => None,
}
}
fn name(&self) -> String {
self.name().unwrap().ident_token().unwrap().text().into()
}
}
impl TryFromDefinition for FragmentDefinition {
fn try_from_def(definition: Definition) -> Option<Self> {
match definition {
Definition::FragmentDefinition(operation) => Some(operation),
_ => None,
}
}
fn name(&self) -> String {
self.fragment_name().unwrap().name().unwrap().ident_token().unwrap().text().into()
}
}
...
Some of this could probably be condensed using macros, but there's no normalized way that I can tell to get a name from a definition, so that would still have to be custom per type.
You should also decide how you want to handle definitions that don't have a name; you'd probably want to return Option<String> to avoid all those .unwrap()s, but I don't know how you'd want to put that in your HashMap.
Without knowing your whole workflow, I might suggest a different route instead:
struct Definitions {
operations: HashMap<String, OperationDefinition>,
fragments: HashMap<String, FragmentDefinition>,
...
}
impl Definitions {
fn from_document(document: &Document) -> Self {
let mut operations = HashMap::new();
let mut fragments = HashMap::new();
...
for definition in document.definitions() {
match definition {
Definition::OperationDefinition(operation) => {
let name: String = operation.name().unwrap().ident_token().unwrap().text().into();
operations.insert(name, operation);
},
Definition::FragmentDefinition(fragment) => {
let name: String = fragment.fragment_name().unwrap().name().unwrap().ident_token().unwrap().text().into();
fragments.insert(name, fragment);
},
...
}
}
Definitions {
operations,
fragments,
...
}
}
}

rust extend built-in enum Result?

tl;dr Is it possible to extend std::result::Result to add my own variant that signals "things are Okay but also..." and keep impl Result methods like is_ok()?
I want to extend Result to signal additional states that a function caller can use for special cases.
use std::result::Result
use std::io::Error;
/// Extend Result to also signal "things are okay but check on things"
enum ResultExt<T, E> {
Result<T, E>,
OkButCheckThings(T),
}
pub fn do_stuff() -> ResultExt<u64, Error> {
// ...
}
pub fn main() -> {
let var = match do_stuff() {
Ok(val) => { val },
Err(err) => { 0 },
OkButCheckThings(val) => { check_things(); val },
}
dbg!(var);
}
It's possible to plainly extend an Enum. But I would also like to use the underlying Result<T, E> functions like is_ok.
let var2 = do_stuff();
if var2.is_ok() {
println!("It is totally Ok, nothing to check!");
}
I created a rust playground example that successfully extends Result<T, E> but the extended enum cannot use functions like is_ok().
The real-world use-case is a function that calls std::io::Read may need to "modify" the returned Result to signal additional states beyond Ok and Err. But I want these various "meta states" to be captured by one enum, as opposed to returning various other bool flags (I want to avoid return signature with (Result<T>, bool, bool). This would allow one clean match statement of all possible states; Ok, Err, "Okay but...", "Err but ...", etc..
There is no current way of "extending" and enum perse.
But it could be simply solved by embedding your own enum type into the result itself.
Simple example, similar to yours:
use std::fmt::Display;
enum StuffToCheck<T> {
Ok(T),
CheckThis(T),
}
impl<T> StuffToCheck<T>
where
T: Display + Copy,
{
pub fn check_things(&self) -> T {
match self {
Self::Ok(val) => {
*val
}
Self::CheckThis(val) => {
println!("Checking stuff for {}", val);
*val
}
}
}
}
fn do_stuff() -> ResultExt<u64> {
Ok(StuffToCheck::CheckThis(10))
}
type ResultExt<T> = Result<StuffToCheck<T>, std::io::Error>;
fn main() {
let var = match do_stuff() {
Ok(result) => result.check_things(),
Err(_err) => 0,
};
dbg!(var);
}
Playground
You could even use nested pattern matching:
...
match do_stuff() {
Err(e) => {//handle error}
Ok(StuffToCheck::Ok(value)) => { value },
Ok(StuffToCheck::CheckThis(value)) => {
check_things(value);
value
}
}
...
I think this is an instance of the X-Y problem. You can use the built-in result, you just need a different error type, that returns an option: Some(partial_result) or None.
For example you have function parse, that can attempt to adjust for a malformed input, but report the error.
pub fn parse(b: &str) -> Result<&str, CustomParseError> {
// Do something that might fail,
if failed(){
return CustomParseError::new(None)
} else if partially_failed() {
return CustomParseError::new(Some(partial_result))
} else {
return completeResult
}
}
This way you have a clean code path where nothing failed, and all of your assumptions are correct, and if it's not => instead of unwrapping, you match and check which case you have. This is vastly superior, because the error often contains enough information for you to reconstruct both what went wrong, and what could be done to fix it.

Macro that wraps an arbitrary number of types

Is it possible to write a macro that defines an enum which wraps an arbitrary number of (distinct) input types?
I'd like to do a kind of type-level match.
type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"))
This would expand to:
{
enum Wrapper {
Variant1(i32),
Variant2(f32),
Variant3(Foo),
}
// impl From<i32>, From<f32>, From<Foo> for Wrapper
|x: Wrapper| match x {
Wrapper::Variant1(x) => println!("integer"),
Wrapper::Variant2(x) => println!("float"),
Wrapper::Variant3(x) => println!("foo"),
}
}
so that I can write like
let switch = type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"));
switch(32.into()); // prints "integer"
switch(24.0.into()); // prints "float"
Define a trait within your macro and implement it for each type:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(self);
}
$(
impl TypeMatch for $ty {
fn type_match(self) {
$expr
}
}
)+
TypeMatch::type_match
}}
}
Notice that the first time you call the function the compiler will bind the type so that subsequent calls must be the same type:
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(0);
s(Foo); // Error!
}
If you need to be able to call it with different types, this can be fixed (at a small cost) by using a trait object for dynamic dispatch:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(&self);
}
$(
impl TypeMatch for $ty {
fn type_match(&self) {
$expr
}
}
)+
|value: &dyn TypeMatch| {
value.type_match()
}
}}
}
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(&0);
s(&Foo);
}
Also notice that you have to pass references instead of values.
It can make sense to write wrapper types as you have proposed, but only if the type is needed in larger parts of your code.
Your specific example would define a new enum every time you use the macro, move the value into the new enum and then immediately throw it away.
That's not a idiomatic approach and if that is indeed your imagined use I'd recommend looking for different options.
That said, I have used wrapper types on a number of occasions.
Something like this will work for declaring a wrapper:
macro_rules! declare_wrapper {
(
$enum_name:ident {
$( $variant_name:ident( $typ:ty : $description:expr ) ),*
}
)=> {
pub enum $enum_name {
$(
$variant_name($typ),
)*
}
$(
impl From<$typ> for $enum_name {
fn from(value: $typ) -> Self {
$enum_name::$variant_name(value)
}
}
)*
impl $enum_name {
fn describe(&self) -> &'static str {
match self {
$(
&$enum_name::$variant_name(_) => $description,
)*
}
}
}
};
}
declare_wrapper!( MyWrapper {
MyInt(i64 : "int"),
MyString(String : "string")
});
fn main() {
let value = MyWrapper::from(22);
println!("{}", value.describe());
}
You can also extend this to add additional methods or trait impls that you need.
I've done similar things quite often.

Resources