Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a trait object to refer to struct that has generic methods

Tags:

rust

In relation to this question, Storing a closure in a HashMap, I learned that properly passing closures to a function requires the function to be generic and take any type that implements the Fn, FnMut, or FnOnce trait.

In implementing part of a library from C++ as a learning exercise, I require some type abstraction kind of like this.

use std::collections::HashMap;

struct Event;

trait IObject {
    fn makeFunc<F : FnMut(&Event)>(&mut self, s : &str, f : F);
}

struct Object1<'a> {
    m_funcs : HashMap<String, Box<FnMut(&Event) + 'a>>
}

impl <'a> Object1<'a> {
    fn new() -> Object1<'a> {
        Object1 {m_funcs : HashMap::new()}
    }
}

impl <'a> IObject for Object1<'a> {
    fn makeFunc<F : FnMut(&Event) + 'a>(&mut self, s: &str, f: F) {
        self.m_funcs.insert(String::from_str(s), Box::new(f));
    }
}

fn main() {
    let obj : &IObject = &Object1::new();
    println!("Hello, world!");
}

However, the error returned says that IObject cannot be a trait object because it contains a method with generic parameters. However, to pass a closure to a function at all, I require generics. Can someone show me how to achieve the abstraction I'm looking for while still being able to pass closures to functions?

like image 877
FatalCatharsis Avatar asked Mar 23 '15 03:03

FatalCatharsis


People also ask

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.

How do you implement a trait in Rust?

To implement a trait, declare an impl block for the type you want to implement the trait for. The syntax is impl <trait> for <type> . You'll need to implement all the methods that don't have default implementations.

What does Dyn mean in Rust?

dyn is a prefix of a trait object's type. The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed.

Is there polymorphism in Rust?

In Rust, polymorphic functions are fully type-checked when they are declared, not when they are used. This means you can never call a Rust function and get a type error within the function because you gave it the wrong type.


1 Answers

You cannot get around this; static and dynamic dispatch do not mix. The monomorphisation that static dispatch (generics) does simply cannot work with the vtable used in dynamic dispatch (trait objects).

One of the two will have to go: either the usage of IObject as a trait object, or the generic function argument, in favour of accepting a Box<FnMut(&Event) + 'a>.

By the way, note how your IObject implementation is not matching the trait—the trait gives no lifetime bound on F, where your implementation does. You’d need to add 'a as a generic on the trait definition anyway (generic lifetimes are OK with trait objects).

like image 81
Chris Morgan Avatar answered Oct 13 '22 04:10

Chris Morgan