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

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.

Related

Propagating higher-kinded-lifetimes? implementation of `Predicate` not general enough

I'm trying to write a Rust library that lets users define and compose predicates for certain kinds of data. This is all easy when the predicates act on owned data, but it's turning out to be harder when they act on borrowed data. I think it's easier to understand my problem by reading an annotated code example (I'm sorry I couldn't make it shorter, but I think everything in it is required to understand my situation).
// First, some of the types that I'm interested in writing predicates for. MyValue and
// MyParsedBorrowedValue
struct MyValue {
a: String
}
struct MyParsedBorrowedValue<'a> {
_name: &'a str,
_last_name: &'a str
}
fn parse_value(value: &MyValue) -> MyParsedBorrowedValue<'_> {
// (imagine some parsing that borrows from MyValue)
MyParsedBorrowedValue {
_name: &value.a,
_last_name: &value.a
}
}
// Now, imagine I have this trait
trait Predicate<T> {
fn passes(&self, t: &T) -> bool;
}
// I can write a generic combinator like:
struct Not<T, P: Predicate<T>> {
p: P,
_p: std::marker::PhantomData<T>
}
impl<T, P: Predicate<T>> Not<T, P> {
fn new(p: P) -> Not<T, P> { Not { p, _p: std::marker::PhantomData }}
}
impl<T, P: Predicate<T>> Predicate<T> for Not<T, P> {
fn passes(&self, t: &T) -> bool {
!self.p.passes(t)
}
}
// I can write predicates on MyValue. For example:
struct IsMyValueGood {}
impl Predicate<MyValue> for IsMyValueGood {
fn passes(&self, _t: &MyValue) -> bool {
// some processing
true
}
}
// I can also write predicates on MyBorrowedParsedValue
struct IsMyParsedBorrowedValueGood {}
impl<'a> Predicate<MyParsedBorrowedValue<'a>> for IsMyParsedBorrowedValueGood {
fn passes(&self, _t: &MyParsedBorrowedValue<'a>) -> bool {
// some processing ...
false
}
}
// Finally, and this is the most important bit: I want a function that would take a
// Predicate on a "MyParsedBorrowedValue" and transform it into a predicate for a
// MyValue. Something like:
fn map_predicates(p: impl for<'a> Predicate<MyParsedBorrowedValue<'a>>)
-> impl Predicate<MyValue> {
// I believe this is a good use of higher-kinded lifetimes, though I'm not very
// familiar with them.
struct PredicateMapper<P: for<'a> Predicate<MyParsedBorrowedValue<'a>>> {
p: P
}
impl<P: for<'a> Predicate<MyParsedBorrowedValue<'a>>> Predicate<MyValue> for PredicateMapper<P> {
fn passes(&self, value: &MyValue) -> bool {
self.p.passes(&parse_value(value))
}
}
PredicateMapper { p }
}
// This is all good but sadly the following does not compile:
fn compose_predicates() {
// This compiles:
let complex_predicate_1 = map_predicates(IsMyParsedBorrowedValueGood{});
// This doesn't compile!
// Error: implementation of `Predicate` is not general enough
let complex_predicate_2 = map_predicates(Not::new(
IsMyParsedBorrowedValueGood {}
));
}
Link to compiler explorer https://godbolt.org/z/oonfcMdK3
I understand that the Not combinator is not propagating the for <'a> outside, which is what map_predicate needs to do its job. However, I don't know how I would get Not to do that, given that its definition doesn't involve any lifetimes. I guess I could make a custom NotMyParsedBorrowedValue that handles the lifetimes in the correct way, but I would prefer having a single Not combinator that works for all kinds of predicates. Is this possible?

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

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());
}

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

Mutually exclusive traits

I need to create operations for an operation sequence. The operations share the following behaviour. They can be evaluated, and at construction they can either be parametrized by a single i32 (eg. Sum) or not parametrized at all (eg. Id).
I create a trait Operation. The evaluate part is trivial.
trait Operation {
fn evaluate(&self, operand: i32) -> i32;
}
But I don't know how to describe the second demand. The first option is to simply let concrete implementations of Operation handle that behaviour.
pub struct Id {}
impl Id {
pub fn new() -> Id {
Id {}
}
}
impl Operation for Id {
fn evaluate(&self, operand: i32) -> i32 {
operand
}
}
pub struct Sum {
augend: i32,
}
impl Sum {
pub fn new(augend: i32) -> Sum {
Sum { augend }
}
}
impl Operation for Sum {
fn evaluate(&self, addend: i32) -> i32 {
augend + addend
}
}
Second option is a new function that takes an optional i32. Then the concrete implementations deal with the possibly redundant input. I find this worse than the first option.
trait Operation {
fn evaluate(&self, operand: i32) -> i32;
fn new(parameter: std::Option<i32>)
}
Google has lead me to mutually exclusive traits: https://github.com/rust-lang/rust/issues/51774. It seems promising, but it doesn't quite solve my problem.
Is there a way to achieve this behaviour?
trait Operation = Evaluate + (ParametrizedInit or UnparametrizedInit)
How about you use an associated type to define the initialization data?
trait Operation {
type InitData;
fn init(data: Self::InitData) -> Self;
fn evaluate(&self, operand: i32) -> i32;
}
impl Operation for Id {
type InitData = ();
fn init(_: Self::InitData) -> Self {
Id {}
}
fn evaluate(&self, operand: i32) -> i32 {
operand
}
}
impl Operation for Sum {
type InitData = i32;
fn init(augend: Self::InitData) -> Self {
Sum { augend }
}
fn evaluate(&self, addend: i32) -> i32 {
augend + addend
}
}
For the Id case you specify () to say that the initialization does not need data. It's still a bit meh to call Operation::init(()), but I think the trait at least captures the logic fairly well.
To actually get mutually exclusive traits (which is apparently what you want), you have to use some workaround. The Rust language does not support mutually exclusive traits per-se. But you can use associated types and some marker types to get something similar. This is a bit strange, but works for now.
trait InitMarker {}
enum InitFromNothingMarker {}
enum InitFromI32Marker {}
impl InitMarker for InitFromNothingMarker {}
impl InitMarker for InitFromI32Marker {}
trait Operation {
type InitData: InitMarker;
fn init() -> Self
where
Self: Operation<InitData = InitFromNothingMarker>;
fn init_from(v: i32) -> Self
where
Self: Operation<InitData = InitFromI32Marker>;
}
trait UnparametrizedInit: Operation<InitData = InitFromNothingMarker> {}
trait ParametrizedInit: Operation<InitData = InitFromI32Marker> {}
impl<T: Operation<InitData = InitFromNothingMarker>> UnparametrizedInit for T {}
impl<T: Operation<InitData = InitFromI32Marker>> ParametrizedInit for T {}
(Ideally you want to have a Sealed trait that is defined in a private submodule of your crate. That way, no one (except for you) can implement the trait. And then make Sealed a super trait for InitMarker.)
This is quite a bit of code, but at least you can make sure that implementors of Operation implement exactly one of ParametrizedInit and UnparametrizedInit.
In the future, you will likely be able to replace the marker types with an enum and the associated type with an associated const. But currently, "const generics" are not finished enough, so we have to take the ugly route by using marker types. I'm actually discussing these solutions in my master's thesis (section 4.2, just search for "mutually exclusive").

Can a trait guarantee certain type properties such as a vector is non-empty?

Imagine I have functions like this:
fn min_max_difference(row: &Vec<u32>) -> u32 {
let mut min_elem: u32 = row[0];
let mut max_elem: u32 = min_elem;
for &element in row.iter().skip(1) {
if element < min_elem {
min_elem = element;
} else if element > max_elem {
max_elem = element;
}
}
result = max_elem - min_elem;
}
fn execute_row_operation(row: &Vec<u32>, operation: Fn(&Vec<u32>) -> u32) -> Option<(u32, u32)> {
let mut result = None;
if row.len() > 0 {
result = operation(row);
}
result
}
Note that the if block in execute_row_operation guarantees that the Vec<u32> I am passing to the operation function is non-empty. In general, I want "operations" to be functions which only accept non-empty rows. I would like it if I could do something like this:
fn min_max_difference<T: &Vec<u32> + NonEmpty>(row: T) -> u32 {
//snip
}
This would allow the compiler to disallow passing references to empty vectors to a function like min_max_difference which expects this.
But traits as I understand them specify what methods a type has, rather than what properties a type has. In my head, I am imagining a trait for a type T that is composed of boolean predicates with type: Fn<T> -> bool, and such a trait is "implemented" for a type if it all those predicates evaluate to true.
Can something like this be achieved?
Can a trait guarantee certain type properties
Yes, that is what they are for. In many cases, these properties are that a set of functions exist (e.g. PartialEq::eq) and that a set of behaviors are present (e.g. symmetric and transitive equality, required by PartialEq).
Traits can also have no methods, such as Eq. These only add a set of behaviors (e.g. reflexive equality). These types of traits are often referred to as marker traits.
such as a vector is non-empty?
However, you aren't asking for what you really want. You actually want a way to implement a trait for certain values of a type. This is not possible in Rust.
At best, you can introduce a newtype. This might be sufficient for your needs, but you could also implement your own marker traits for that newtype, if useful:
struct NonEmptyVec<T>(Vec<T>);
impl<T> NonEmptyVec<T> {
fn new(v: Vec<T>) -> Result<Self, Vec<T>> {
if v.is_empty() {
Err(v)
} else {
Ok(NonEmptyVec(v))
}
}
}
fn do_a_thing<T>(items: NonEmptyVec<T>) {}
fn main() {
let mut a = Vec::new();
// do_a_thing(a); // expected struct `NonEmptyVec`, found struct `std::vec::Vec`
a.push(42);
let b = NonEmptyVec::new(a).expect("nope");
do_a_thing(b);
}
T: &Vec<u32> + NonEmpty
This isn't valid because Vec is a type and NonEmpty would presumably be a trait — you can't use types as trait bounds.
Historical note:
Way back in the long ago, as I understand it, Rust actually did support what you wanted under the name typestate. See What is typestate? and Typestate Is Dead, Long Live Typestate!.
An example of emulating it:
struct MyVec<T, S>
where
S: VecState,
{
vec: Vec<T>,
state: S,
}
trait VecState {}
struct Empty;
struct NonEmpty;
impl VecState for Empty {}
impl VecState for NonEmpty {}
impl<T> MyVec<T, Empty> {
fn new() -> Self {
MyVec {
vec: Vec::new(),
state: Empty,
}
}
fn push(mut self, value: T) -> MyVec<T, NonEmpty> {
self.vec.push(value);
MyVec {
vec: self.vec,
state: NonEmpty,
}
}
}
fn do_a_thing<T>(items: MyVec<T, NonEmpty>) {}
fn main() {
let a = MyVec::new();
// do_a_thing(a); // expected struct `NonEmpty`, found struct `Empty`
let b = a.push(42);
do_a_thing(b);
}

Resources