1
votes

I'm working on a Rust library that provides a trait which may optionally be implemented by a user. It can look like this:

pub trait MyHandler {
    fn do_something(&mut self);
}

The main struct in the library looks somewhat like this:

pub struct MyType<H: MyHandler {
    field_a: u8,
    field_b: u32,
    handler: Option<H>,
}

This works, but in reality a very small percentage of the users will implement a custom handler. The problem from an ergonomics point of view is that the type H will always need to be specified, even when no handler is passed in:

let t = MyType {
    field_a: 1,
    field_b: 3000,
    handler: None,
};
error[E0282]: type annotations needed for `MyType<H>`
  --> src/main.rs:12:13
   |
12 |     let t = MyType {
   |         -   ^^^^^^ cannot infer type for type parameter `H` declared on the struct `MyType`
   |         |
   |         consider giving `t` the explicit type `MyType<H>`, where the type parameter `H` is specified

All the library can do to simplify this, is to provide a dummy implementation that can be specified by the user:

pub struct DefaultHandler {}

impl MyHandler for DefaultHandler {
    fn do_something(&mut self) {}
}
let t = MyType::<DefaultHandler> {
    field_a: 1,
    field_b: 3000,
    handler: None,
};

Is there a good pattern to make this more ergonomic, without having to specify a dummy trait impl?

1
You can still use Dummy trait with hiding it from the api user, please check: play.rust-lang.org/… - Ömer Erden
Or you can use inner pattern to remove dummy trait and also option, please check: play.rust-lang.org/… - Ömer Erden

1 Answers

1
votes

Is there a difference between None and Some(DefaultHandler)?

If not, I would make handler of type H, and possibly require the user to specify DefaultHandler (instead of None).

Second, you could add a constructor function new that just creates a MyType<DefaultHandler> with handler: DefaultHandler, and you could - in addition to that and for callers that actually want a custom handler - add a constructor function new_with_handler that accepts a custom handler as an argument that is then used to create a MyType with the appropriate handler. (This is somewhat similar to e.g. HashMap::new (constructing a new HashMap with defaults) and HashMap::with_hasher (constructing a new HashMap with a given hasher).)