5

I want to have a function foo taking an instance of a type which implements a trait A. I always prefer to use generics in order to have static dispatch:

trait A {}

fn foo<T: A>(t: T) {}

However, this approach introduces some inflexibility, I can't pass a trait object like here:

trait A {}

fn foo(t: &A) {}

The thing is, sometimes I know the type and sometimes not. Is there a way to have both dynamic dispatch for trait objects and static dispatch for compile time known types while not implementing things twice?

1 Answer 1

6

That is indeed possible. One option is to explicitly implement A for its reference types:

impl<'a, T: A + ?Sized> A for &'a T {}

The argument becomes a trait object when T = &A, while still doing static dispatch for known implementors of A. The following code should now compile:

fn foo<T: A>(a: T) {}

struct MyA;
impl A for MyA {}

fn main() {
    foo(MyA{});
    foo(&MyA{});
    foo(&MyA{} as &A);
}

If you are willing to always pass a borrowed argument, you can also do this instead:

fn foo<T: A + ?Sized>(a: &T) {}

The argument becomes a trait object when T = A.

Sign up to request clarification or add additional context in comments.

2 Comments

I'm not sure if I fully understand your fn foo. Isn't there always an implicit bound on Sized?
@torkleyy On the function argument, yes. That is ok because what we're providing is indeed always sized, in either case. The second function takes &T, which is sized, even if T is not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.