Can a Rust enum use methods implemented on one of its variants? - struct

I think I misunderstand the purpose of enums.
I am looking to implement the XML DOM for practice. DOM nodes all have a list of methods associated with them, but some nodes (such as the document node) have additional methods not available on other nodes.
I was hoping to have all the main methods set up on the enum and then the variants would have the methods specific to them, but it seems like the method variants are not callable on an enum, but continue to be callable on the parameter.
#[derive(Clone, Debug, Copy)]
enum MyEnum {
MyType(MyType),
}
impl MyEnum {
pub fn do_a_thing(&self) -> i32 {
// Some code here
return 1;
}
}
#[derive(Clone, Debug, Copy)]
pub struct MyType {}
impl MyType {
pub fn do_another_thing(self) -> i32 {
// Some more code here
return 2;
}
}
fn main() {
let x = MyEnum::MyType(MyType {});
println!("{:?}", x);
println!("I can call do_a_thing, it is {}", x.do_a_thing());
println!(
"Why can't I call do_another_thing? {}",
x.do_another_thing()
);
}
This works fine, but my gut tells me I am going the wrong way about things:
impl MyEnum {
pub fn do_a_thing(&self) -> i32 {
// Some code here
return 1;
}
pub fn do_another_thing(self) -> i32 {
match self {
MyEnum::MyType(MT) => MT.do_another_thing(),
}
}
}
How should I implement this?

You are using an enum, so it's likely you're going to add more variants at some point (otherwise, why would you use enums, right?).
When Rust sees a value whose type is an enum, at compile-time it assumes the value could be any of the enum's variants, so it won't let you call a method on any variant until you check its type.
To do that is easy, as you've already shown in your question:
match self {
MyEnum::MyType(t) => t.do_another_thing()
}
You might be interested to know there's also if let in case you only want to check a single variant:
let x = MyEnum::MyType(MyType{});
if let MyEnum::MyType(t) = x {
t.do_another_thing();
}
You might want to have methods that are callable on ALL variants as well, in which case you'll need to either implement the method directly on the enum as you've done above, or use a trait that is implemented by the enum, which perhaps makes your code look more polymorphic as in most other languages (where there would be an interface for the nodes).
That will make your code in main work.
It might look like this:
#[derive(Clone, Debug, Copy)]
enum MyEnum {
MyType(MyType)
}
impl MyEnum {
pub fn do_a_thing(&self) -> i32{
// Some code here
return 1
}
}
#[derive(Clone, Debug, Copy)]
pub struct MyType {
}
trait MyTrait {
fn do_another_thing(&self) -> i32;
}
impl MyTrait for MyEnum {
fn do_another_thing(&self) -> i32 {
// Some more code here
return 2
}
}
fn main() {
let x = MyEnum::MyType(MyType{});
println!("{:?}",x);
println!("I can call do_a_thing, it is {}", x.do_a_thing());
println!("Why can't I call do_another_thing? {}", x.do_another_thing());
}

Related

Composition over inheritance, sure, but do we have any syntactic sugar for the function passthrough boilerplate?

If I have a struct implementing a trait, and then I encapsulate that struct in another struct, is there an easy way to pass through all the function calls to implement that trait for the second struct?
trait HasAnX {
fn get_x(&self) -> i32;
}
struct StructA {
x: i32
}
impl HasAnX for StructA {
fn get_x(&self) -> i32 {
self.x
}
}
struct StructB {
a: StructA
}
// This is the part I don't want to have to write out for every function in StructA
impl HasAnX for StructB {
fn get_x(&self) -> i32 {
self.a.get_x()
}
}
I think half of the problem is that I'm not even sure what this problem is called.
The need to implement HasAnX for StructB usually comes from looking at the problem with inheritance in mind.
To avoid this situation try to work with only the part you really care for.
If you just want to call a method on a you can do so from the outside:
struct_b.a.get_x();
If you want to work with anything like a StructA you can just implement AsRef<StructA> for it, similarly you can use AsMut for mutable access.
Then functions can just take an impl AsRef<StructA> and don't need to care what actual type it is, with the added benefit that it now also can take owned types and references.
impl AsRef<StructA> for StructB {
fn as_ref(&self) -> &StructA {
&self.a
}
}
// you can pass in any of `StructA`, `&StructA`, `StructB`, `&StructB`
pub fn reads_from_a(a: impl AsRef<StructA>) {
let a = a.as_ref();
println!("{}", a.get_x());
}
If your StructB is a sort of smart pointer you can implement Deref and just use StructAs methods directly on StructB
use std::ops::Deref;
impl Deref for StructB {
type Target = StructA;
fn deref(&self) -> &StructA {
&self.a
}
}
//...
struct_b.get_x();

Best way to implement method on enum containing values which all implement a trait

Here's a simplified version of the code I'm working with (rust playground):
struct FirstStruct(u64);
struct SecondStruct(u32);
trait GetValue {
fn get_value(&self) -> u32;
}
impl GetValue for FirstStruct {
fn get_value(&self) -> u32 {
self.0 as u32
}
}
impl GetValue for SecondStruct {
fn get_value(&self) -> u32 {
self.0
}
}
enum MyEnum {
First(FirstStruct),
Second(SecondStruct),
}
impl MyEnum {
fn get_struct_value(&self) -> u32 {
match self {
Self::First(data) => data.get_value(),
Self::Second(data) => data.get_value(),
}
}
}
Basically, I have an enum where each variant has different data, but the structs contained in each variant all implement the same trait. The real code has many more variants, and the trait is much more complex.
This code works as is, but I'm looking for a better way to implement get_struct_value. I have a bunch of similar functions, and enumerating every variant in every one of them is noisy and error-prone.
Could I use macros to fill in the full match statement automatically? Or is there some other better approach?

How to use traits for function overloading in Rust? [duplicate]

I am modeling an API where method overloading would be a good fit. My naïve attempt failed:
// fn attempt_1(_x: i32) {}
// fn attempt_1(_x: f32) {}
// Error: duplicate definition of value `attempt_1`
I then added an enum and worked through to:
enum IntOrFloat {
Int(i32),
Float(f32),
}
fn attempt_2(_x: IntOrFloat) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
// Can't pass the value directly
// attempt_2(i);
// attempt_2(f);
// Error: mismatched types: expected enum `IntOrFloat`
attempt_2(IntOrFloat::Int(i));
attempt_2(IntOrFloat::Float(f));
// Ugly that the caller has to explicitly wrap the parameter
}
Doing some quick searches, I've found some references that talk about overloading, and all of them seem to end in "we aren't going to allow this, but give traits a try". So I tried:
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_3(_x: &dyn IntOrFloatTrait) {}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_3(&i);
attempt_3(&f);
// Better, but the caller still has to explicitly take the reference
}
Is this the closest I can get to method overloading? Is there a cleaner way?
Yes, there is, and you almost got it already. Traits are the way to go, but you don't need trait objects, use generics:
#[derive(Debug)]
enum IntOrFloat {
Int(i32),
Float(f32),
}
trait IntOrFloatTrait {
fn to_int_or_float(&self) -> IntOrFloat;
}
impl IntOrFloatTrait for i32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Int(*self)
}
}
impl IntOrFloatTrait for f32 {
fn to_int_or_float(&self) -> IntOrFloat {
IntOrFloat::Float(*self)
}
}
fn attempt_4<T: IntOrFloatTrait>(x: T) {
let v = x.to_int_or_float();
println!("{:?}", v);
}
fn main() {
let i: i32 = 1;
let f: f32 = 3.0;
attempt_4(i);
attempt_4(f);
}
See it working here.
Here's another way that drops the enum. It's an iteration on Vladimir's answer.
trait Tr {
fn go(&self) -> ();
}
impl Tr for i32 {
fn go(&self) {
println!("i32")
}
}
impl Tr for f32 {
fn go(&self) {
println!("f32")
}
}
fn attempt_1<T: Tr>(t: T) {
t.go()
}
fn main() {
attempt_1(1 as i32);
attempt_1(1 as f32);
}
Function Overloading is Possible!!! (well, sorta...)
This Rust Playground example has more a more detailed example, and shows usage of a struct variant, which may be better for documentation on the parameters.
For more serious flexible overloading where you want to have sets of any number of parameters of any sort of type, you can take advantage of the From<T> trait for conversion of a tuple to enum variants, and have a generic function that converts tuples passed into it to the enum type.
So code like this is possible:
fn main() {
let f = Foo { };
f.do_something(3.14); // One f32.
f.do_something((1, 2)); // Two i32's...
f.do_something(("Yay!", 42, 3.14)); // A str, i32, and f64 !!
}
First, define the different sets of parameter combinations as an enum:
// The variants should consist of unambiguous sets of types.
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
Now, the conversion code; a macro can be written to do the tedious From<T> implementations, but here's what it could produce:
impl From<(i32, i32)> for FooParam {
fn from(p: (i32, i32)) -> Self {
FooParam::Bar(p.0, p.1)
}
}
impl From<f32> for FooParam {
fn from(p: f32) -> Self {
FooParam::Baz(p)
}
}
impl From<(&'static str, i32, f64)> for FooParam {
fn from(p: (&'static str, i32, f64)) -> Self {
FooParam::Qux(p.0, p.1, p.2)
}
}
And then finally, implement the struct with generic method:
struct Foo {}
impl Foo {
fn do_something<T: Into<FooParam>>(&self, t: T) {
use FooParam::*;
let fp = t.into();
match fp {
Bar(a, b) => print!("Bar: {:?}, {:?}\n", a, b),
Baz(a) => print!("Baz: {:?}\n", a),
Qux(a, b, c) => {
print!("Qux: {:?}, {:?}, {:?}\n", a, b, c)
}
}
}
}
Note: The trait bound on T needs to be specified.
Also, the variants need to be composed of combinations of types that the compiler wouldn't find ambiguous - which is an expectation for overloaded methods in other languages as well (Java/C++).
This approach has possibilities... it would be awesome if there's a decorator available - or one were written that did the From<T> implementations automatically when applied to an enum. Something like this:
// THIS DOESN'T EXIST - so don't expect the following to work.
// This is just an example of a macro that could be written to
// help in using the above approach to function overloading.
#[derive(ParameterOverloads)]
enum FooParam {
Bar(i32, i32),
Baz(f32),
Qux(&'static str, i32, f64),
}
// If this were written, it could eliminate the tedious
// implementations of From<...>.
The Builder
Another approach that addresses the case where you have multiple optional parameters to an action or configuration is the builder pattern. The examples below deviate somewhat from the recommendations in the link. Typically, there's a separate builder class/struct which finalizes the configuration and returns the configured object when a final method is invoked.
One of the most relevant situations this can apply to is where you want a constructor that takes a variable number of optional arguments - since Rust doesn't have built-in overloading, we can't have multiple versions of ___::new(). But we can get a similar effect using a chain of methods that return self. Playground link.
fn main() {
// Create.
let mut bb = BattleBot::new("Berzerker".into());
// Configure.
bb.flame_thrower(true)
.locomotion(TractorTreads)
.power_source(Uranium);
println!("{:#?}", bb);
}
Each of the configuration methods has a signature similar to:
fn power_source(&mut self, ps: PowerSource) -> &mut Self {
self.power_source = ps;
self
}
These methods could also be written to consume self and return non-reference copies or clones of self.
This approach can also be applied to actions. For instance, we could have a Command object that can be tuned with chained methods, which then performs the command when .exec() is invoked.
Applying this same idea to an "overloaded" method that we want to take a variable number of parameters, we modify our expectations a bit and have the method take an object that can be configured with the builder pattern.
let mut params = DrawParams::new();
graphics.draw_obj(params.model_path("./planes/X15.m3d")
.skin("./skins/x15.sk")
.location(23.64, 77.43, 88.89)
.rotate_x(25.03)
.effect(MotionBlur));
Alternatively, we could decide on having a GraphicsObject struct that has several config tuning methods, then performs the drawing when .draw() is invoked.

How can I denote a field that can be either Rc<T> or Weak<T>

I'd like to have a field in struct like this:
struct Foo<T> {
bar: Smart<T>
}
where bar could be either Rc<T or Weak<T>, depending on the "ownership relationships" between different instances of Foo. Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Just like most other "either this or that" choices in Rust, an enum is the idiomatic way.
An Peter's answer suggested, there is is no such abstraction in the stdlib. You can define a small enum to handle both cases:
use std::rc::{Rc, Weak};
enum MaybeStrong<T> {
Strong(Rc<T>),
Weak(Weak<T>),
}
impl<T> MaybeStrong<T> {
fn get(&self) -> Option<Rc<T>> {
match self {
MaybeStrong::Strong(t) => Some(Rc::clone(t)),
MaybeStrong::Weak(w) => w.upgrade(),
}
}
}
struct Foo<T> {
bar: MaybeStrong<T>
}
impl<T> Foo<T> {
fn from_weak(inner: Weak<T>) -> Self {
Self { bar: MaybeStrong::Weak(inner) }
}
fn from_strong(inner: Rc<T>) -> Self {
Self { bar: MaybeStrong::Strong(inner) }
}
fn say(&self) where T: std::fmt::Debug {
println!("{:?}", self.bar.get())
}
}
fn main() {
let inner = Rc::new("foo!");
Foo::from_weak(Rc::downgrade(&inner)).say();
Foo::from_strong(inner).say();
}
self.bar() will always return a Some if it was created from a strong pointer and return None in case it's a Weak and it's dangling. Notice that due to the fact that get() needs to create an owned Rc first, the method can't return a &T (including Option<&T>) because that &T could be dangling. This also means that all users of bar() will own one strong count on the inner value while processing, making it safe to use in any case.
This kind of construct is often called "Either" and there's a crate that looks like it can address some of the usual use-cases: https://docs.rs/either/1.5.3/either/
Then you could write
struct Foo<T> {
bar: Either<Weak<T>, Rc<T>>
}
Then an example function to get an Option<Rc<T>> might be:
impl <T> Foo<T> {
fn get_rc(self) -> Option<Rc<T>> {
self.bar
.map_left( |weak| weak.upgrade() )
.map_right( |v| Some(v) )
.into_inner()
}
}
Then it can be used like this:
fn main() {
let x = Rc::new(1);
let f_direct = Foo{ bar:Either::Right(x.clone()) };
println!("f_direct.get_rc() = {:?}", f_direct.get_rc());
let f_weak = Foo{ bar:Either::Left(Rc::downgrade(&x)) };
println!("f_weak.get_rc() = {:?}", f_weak.get_rc());
}
Link to complete example in the playground:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c20faaa46277550e16a3d3b24f3d1750

How can I wrap any impl of std::error::Error to ease error propagation?

I'm trying to simplify the error flow in a webapp I'm working on, and my plan was to make a struct that implements std::error::Error and just forwards the result of description() for whatever kind of error it's wrapped around. I've implemented From for the types of errors I want to wrap, so this struct makes it easy to use try! to get a uniform error result. Here's what I have so far for the struct:
#![feature(convert)]
use std::error::{Error};
use std::fmt::{self,Display,Formatter};
use std::io::{self,Read};
use std::ops::Deref;
use std::fs::{File};
#[derive(Debug)]
pub struct StrErr{
desc:String,
c: Option<Box<Error>>
}
impl StrErr{
pub fn new(msg:String) ->Self{
StrErr{desc:msg, c:None}
}
}
impl Error for StrErr{
fn description(&self) -> &str{
self.desc.as_str()
}
fn cause(& self) -> Option<& Error> {
self.c.map(|e| e.deref())
}
}
impl Display for StrErr {
fn fmt(&self, f:&mut Formatter) -> Result<(),fmt::Error> {
f.write_str(self.desc.as_str())
}
}
impl From<io::Error> for StrErr {
fn from(o:io::Error) -> Self {
StrErr{desc: String::from(o.description()),c:Some(Box::new(o))}
}
}
fn main(){
let contrived = Some("foo.txt")
.ok_or_else(|| StrErr::new(String::from("error message")))
.and_then(|filename| Ok(try!(File::open(filename))))
.and_then(|mut file| {
let mut content = String::new();
try!(file.read_to_string(&mut content));
Ok(content)
});
if let Ok(content) = contrived {
println!("Got content: {}", content);
} else {
println!("got an error");
}
}
playground
The problem is with the cause() method - I can't return a reference to the inner Error instance because e doesn't live long enough. Is there a different way I can structure this so that I can keep the generic reference to anything that implements Error (which I currently do by putting it in a Box) but I can still fully implement the Error trait (which is expecting a ref to the parent Error)?
I've worked around it by just punting on cause() and having it return None, but I'd much rather conform to the intent of the trait.
rustc 1.2.0-nightly (613e57b44 2015-06-01) (built 2015-06-02)
This is one way you can convert an Option<Box<Trait>> to an Option<&Trait>. I'm avoiding all of the trait implementation to clearly show the interesting code:
use std::error::Error;
pub struct StrErr {
c: Option<Box<Error>>
}
impl StrErr {
fn cause(&self) -> Option<&Error> {
self.c.as_ref().map(|e| &**e)
}
}
fn main() {}
We use Option::as_ref to avoid consuming the self.c item. The map closure is provided with a &Box<Trait>, so we dereference it twice to get to a Trait, and then reference it once to get to a &Trait.

Resources