I want to use deeply nested enums to represent blocks in my game:
enum Element { Void, Materal(Material) }
enum Material { Gas(Gas), NonGas(NonGas) }
enum NonGas { Liquid(Liquid), Solid(Solid) }
enum Solid { MovableSolid(MovableSolid), ImmovableSolid(ImmovableSolid) }
enum Gas { Smoke }
enum Liquid { Water }
enum ImmovableSolid { Bedrock }
enum MovableSolid { Sand, GunPowder }
I found it very verbose to declare an Element:
let block: Element = Element::Materal(Material::NonGas(NonGas::Solid(Solid::ImmovableSolid(ImmovableSolid::Bedrock))));
Is it possible to create a macro to add syntactic sugar for my enum declaration?
I'm hoping to create a macro that can automagically resolve the enum path, for example
let block: Element = NewElement!(ImmovableSolid::Bedrock);
Using cdhowie's From idea, I think you'd only need trait impls from your lowest level enums. You can skip ones like impl From<Material> for Element because you need a child to create a Material, so it doesn't really make sense to start at that level.
impl From<Gas> for Element {
fn from(e: Gas) -> Element {
Element::Materal(Material::Gas(e))
}
}
impl From<Liquid> for Element {
fn from(e: Liquid) -> Element {
Element::Materal(Material::NonGas(NonGas::Liquid(e)))
}
}
impl From<ImmovableSolid> for Element {
fn from(e: ImmovableSolid) -> Element {
Element::Materal(Material::NonGas(NonGas::Solid(Solid::ImmovableSolid(e))))
}
}
impl From<MovableSolid> for Element {
fn from(e: MovableSolid) -> Element {
Element::Materal(Material::NonGas(NonGas::Solid(Solid::MovableSolid(e))))
}
}
fn main() {
println!("{:?}", Element::from(ImmovableSolid::Bedrock));
}
Related
I'm designing an API and I currently wish to have a function that may or may not accept an argument with default behavior for empty arguments. Using enums works ok, but that requires the user to enter certain arguments which will add up quickly. My current implementation is this:
enum ArgType {
Square,
Circle,
Triangle,
}
fn draw_image(arg: ArgType) {
match arg {
ArgType::Square => println!("Square!"),
ArgType::Circle => println!("Circle!"),
ArgType::Triangle => println!("Triangle!"),
_ => println!("Empty argument, initiating default behavior!"),
}
}
fn main() {
let circle = ArgType::Triangle;
draw_image(Triangle);
draw_image(); // Does not compile
}
You could use an Option<T>.
fn draw_image(arg: Option<ArgType>) {
match arg.unwrap_or(ArgType::DefaultArgType) {
ArgType::Square => println!("Square!"),
ArgType::Circle => println!("Circle!"),
ArgType::Triangle => println!("Triangle!"),
}
}
//...
draw_image(Some(ArgType::Circle));
draw_image(None); // will use the default ArgType
If you don't want to use a default ArgType, but instead want a completely separate functionality, I'd just define another function that immediately invokes the default behaviour.
fn draw_image_default() {
//...
}
What you are describing is a functionality called "function overloading".
Rust explicitely does not allow that. A longer discussion about the reasoning behind it can be found here.
As a direct alternative, you can represent optional parameters via the Option type. It can either be Some(value) or None.
With it, your code would look like this:
pub enum ArgType {
Square,
Circle,
Triangle,
}
fn draw_image(arg: Option<ArgType>) {
match arg {
Some(ArgType::Square) => println!("Square!"),
Some(ArgType::Circle) => println!("Circle!"),
Some(ArgType::Triangle) => println!("Triangle!"),
None => println!("Empty argument, initiating default behavior!"),
}
}
fn main() {
let triangle = ArgType::Triangle;
draw_image(Some(triangle));
draw_image(None);
}
Triangle!
Empty argument, initiating default behavior!
As others have said, function overloading is not possible in Rust and you can use Option to represent optional parameters. However this can be unwieldy if you have a lot of parameters and you expect that your users will only use a few at a time. In that case, typing all the None values for the other parameters can be quite tedious. In order to avoid that you can use the builder pattern, where you initialize a struct with the default values, then call methods to set the parameters you want and have an execute method to do the work:
#[derive (Debug)]
pub enum ArgType {
Default,
Square,
Circle,
Triangle,
}
#[derive (Debug)]
pub enum ArgColor {
Default,
White,
Black,
}
pub struct ImageDraw {
ty: ArgType,
col: ArgColor,
// ...
}
impl ImageDraw {
fn default() -> Self {
ImageDraw { ty: ArgType::Default, col: ArgColor::Default, /* ... */ }
}
fn with_type (self, ty: ArgType) -> Self {
ImageDraw { ty, ..self }
}
fn with_color (self, col: ArgColor) -> Self {
ImageDraw { col, ..self }
}
fn draw (self) {
println!("Drawing {:?} in {:?}", self.ty, self.col);
}
}
fn main() {
ImageDraw::default()
.with_type (ArgType::Triangle)
.with_color (ArgColor::White)
.draw();
ImageDraw::default()
.with_type (ArgType::Circle)
.draw();
ImageDraw::default()
.with_color (ArgColor::Black)
.draw();
}
Playground
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,
...
}
}
}
I've been trying to implement a Strategy pattern in rust, but I'm having trouble understanding how to make it work.
So let's imagine we have a trait Adder and Element:
pub trait Element {
fn to_string(&self) -> String;
}
pub trait Adder {
type E: Element;
fn add (&self, a: &Self::E, b: &Self::E) -> Self::E;
}
And we have two implementations StringAdder with StringElements and UsizeAdder with UsizeElements:
// usize
pub struct UsizeElement {
pub value: usize
}
impl Element for UsizeElement {
fn to_string(&self) -> String {
self.value.to_string()
}
}
pub struct UsizeAdder {
}
impl Adder for UsizeAdder{
type E = UsizeElement;
fn add(&self, a: &UsizeElement, b: &UsizeElement) -> UsizeElement{
UsizeElement { value: a.value + b.value }
}
}
// String
pub struct StringElement {
pub value: String
}
impl Element for StringElement {
fn to_string(&self) -> String {
self.value.to_string()
}
}
pub struct StringAdder {
}
impl Adder for StringAdder {
type E = StringElement;
fn add(&self, a: &StringElement, b: &StringElement) -> StringElement {
let a: usize = a.value.parse().unwrap();
let b: usize = b.value.parse().unwrap();
StringElement {
value: (a + b).to_string()
}
}
}
And I want to write a code that uses trait methods from Adder trait and it's corresponding elements without knowing at compile time which strategy is going to be used.
fn main() {
let policy = "usize";
let element = "1";
let adder = get_adder(&policy);
let element_a = get_element(&policy, element);
let result = adder.add(element_a, element_a);
}
To simplify I'm going to assign a string to policy and element but normally that would be read from a file.
Is the only way to implement get_adder and get_element using dynamic dispatch? And by extension should I define Adder and Element traits to use trait objects and or the Any trait?
Edit: Here is what I managed to figure out so far.
An example of possible implementation is using match to help define concrete types for the compiler.
fn main() {
let policy = "string";
let element = "1";
let secret_key = "5";
let result = cesar(policy, element, secret_key);
dbg!(result.to_string());
}
fn cesar(policy: &str, element: &str, secret_key: &str) -> Box<dyn Element>{
match policy {
"usize" => {
let adder = UsizeAdder{};
let element = UsizeElement{ value: element.parse().unwrap() };
let secret_key = UsizeElement{ value: secret_key.parse().unwrap() };
Box::new(cesar_impl(&adder, &element, &secret_key))
}
"string" => {
let adder = StringAdder{};
let element = StringElement{ value: element.to_string() };
let secret_key = StringElement{ value: secret_key.to_string() };
Box::new(cesar_impl(&adder, &element, &secret_key))
}
_ => {
panic!("Policy not supported!")
}
}
}
fn cesar_impl<A>(adder: &A, element: &A::E, secret_key: &A::E) -> A::E where A: Adder, A::E : Element {
adder.add(&element, &secret_key)
}
However the issue is that I have to wrap every function I want to implement using a match function to determine the concrete type, and also case for every policy available.
It does not seem like the proper way of implementing it as it will bloat the code, make it more error prone and less maintainable unless I end up using macros.
Edit 2: Here you can find an example using dynamic dispatch. However I'm not convinced it's the proper way to implement the solution.
Example using dynamic dispatch
Thank you for your help :)
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.
This is just pseudocode:
macro_rules! attribute {
$e: expr<f32> => { /* magical float stuff */ };
$e: expr<i64> => { /* mystical int stuff */ };
};
I would like to have a differently expanded macro depending on the type that I passed to the macro.
This is how it would work in C++
template <typename T>
struct Attribute{ void operator(T)() {} };
template <>
struct Attribute<float> {
void operator(float)(float) { /* magical float stuff */ }
};
template <>
struct Attribute<long> {
void operator()(long) { /* mystical int stuff */ }
}
Rust macros aren't able to do that. Macros operate at the syntactic level, not at the semantic level. That means that although the compiler knows it has an expression (syntax), it doesn't know what the type of the expression's value (semantic) is at the moment the macro is expanded.
A workaround would be to pass the expected type to the macro:
macro_rules! attribute {
($e:expr, f32) => { /* magical float stuff */ };
($e:expr, i64) => { /* mystical int stuff */ };
}
fn main() {
attribute!(2 + 2, i64);
}
Or, more simply, define multiple macros.
If you want to do static (compile-time) dispatch based on the type of an expression, you can use traits. Define a trait with the necessary methods, then implement the trait for the types you need. You can implement a trait for any type (including primitives and types from other libraries) if the impl block is in the same crate as the trait definition.
trait Attribute {
fn process(&self);
}
impl Attribute for f32 {
fn process(&self) { /* TODO */ }
}
impl Attribute for i64 {
fn process(&self) { /* TODO */ }
}
macro_rules! attribute {
($e:expr) => { Attribute::process(&$e) };
}
fn main() {
attribute!(2 + 2);
}
Note: You could also write $e.process() in the macro's body, but then the macro might call an unrelated process method.
As already explained, you cannot expand differently depending on the type of an expr. But as a workaround, you can use the any module and try to downcast from the Any trait:
use std::any::Any;
macro_rules! attribute {
( $e:expr ) => {
if let Some(f) = (&$e as &Any).downcast_ref::<f32>() {
println!("`{}` is f32.", f);
} else if let Some(f) = (&$e as &Any).downcast_ref::<f64>() {
println!("`{}` is f64.", f);
} else {
println!("I dunno what is `{:?}` :(", $e);
}
};
}
fn main() {
attribute!(0f32);
attribute!(0f64);
attribute!(0);
}
Displays:
`0` is f32.
`0` is f64.
I dunno what is `0` :(
While all the answers here are correct, I'd like to provide an answer more akin to your C++ version.
Rust provides its own version of templates, generics, and they can be used in the same way you use templates.
So, to define a struct and implement functions for certain types:
struct Attribute<T> {
value: T,
}
impl Attribute<u32> {
fn call(&self) {
println!("{} is a u32", self.value);
}
}
impl Attribute<f64> {
fn call(&self) {
println!("{} is a f64", self.value);
}
}
impl Attribute<String> {
fn call(&self) {
println!("{} is a string", self.value);
}
}
We'd use it like that:
fn main() {
let a = Attribute{
value: 5_u32
};
a.call();
}
Or simply like this:
Attribute{value: 6.5}.call()
Sadly, Rust doesn't provide () operator overloading in its stable version. You can still define a macro to do the job:
macro_rules! attribute {
( $e:expr ) => {
Attribute{value: $e}.call();
};
}
And use it as so:
attribute!("Hello World!".to_string());
I'd recommend using the trait based approach shown in this answer, as it doesn't use a struct, but a trait, which is considered better practice. This answer may still be helpful in many situations.