22

I am trying to get a dynamically dispatchable borrow to an instance of an object implementing both Reader and Seek.

I understand that Rust can do dynamic dispatch as long as there is only one trait involved.

use std::io::{Read, Seek};
fn user(stream: &mut Read) {}

With two or more trait bounds though, I am forced to use a type parameter:

fn user_gen<T: Read + Seek>(stream: &mut T) {}

As the actual type underneath is a builder, it would have to store the borrowed object in some way, and using a type parameter for this would make the implementation more complex (I have three type parameters already).

Ideally, I would be able to do something like that:

fn user_dynamic(stream: &mut (Read + Seek)) {}

This does not compile:

error[E0225]: only auto traits can be used as additional traits in a trait object
 --> src/main.rs:3:38
  |
3 | fn user_dynamic(stream: &mut (Read + Seek)) {}
  |                                      ^^^^ non-auto additional trait

I understand that dynamic dispatch is done through fat pointers, and usually these only refer to one method table, not to multiple ones. I have not seen a statically compiled language that would support this, but such a feature would help me a lot.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Byron
  • 3,908
  • 3
  • 26
  • 35

1 Answers1

29

You can create an empty trait that merges those two traits:

use std::io::{Read, Seek};

trait SeekRead: Seek + Read {}
impl<T: Seek + Read> SeekRead for T {}

fn user_dynamic(stream: &mut SeekRead) {}

This will create a new vtable for SeekRead that contains all the function pointers of both Seek and Read.

You will not be able to cast your &mut SeekRead to either &mut Seek or &mut Read without some trickery (see Why doesn't Rust support trait object upcasting?)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oli_obk
  • 28,729
  • 6
  • 82
  • 98