Reputation: 2095
I have:
struct Plumbus<'a> {
grumbo: &'a dyn Grumbo,
}
trait Grumbo {
fn dinglebop<T>(&self, x: &mut T) -> bool { false }
}
but I get:
error[E0038]: the trait `Grumbo` cannot be made into an object
--> plumbus.rs:4:5
|
4 | grumbo: &'a Grumbo,
| ^^^^^^^^^^^^^^^^^^ the trait `Grumbo` cannot be made into an object
|
= note: method `dinglebop` has generic type parameters
I want to have dinglebop
do nothing by default, but depending on the Grumbo
and the T
, possibly fill x
with a T
if it makes sense for that particular Grumbo
implementation.
In C++ this could probably be accomplished with partial specialization. I am not sure what to aim for with Rust.
dinglebop()
for arbitrary T
without specializing my Plumbus
for a particular T
?Upvotes: 8
Views: 10827
Reputation: 10701
Many problems in computer science can be solved with one level of indirection, I was told. Coming across the same issue, here is a solution that does not requires to put any new type parameter on Plumbus
.
Because dynamic dispatch has only one vtable, and with your function signature you actually need two vtables, the solution is to have your initial function return another object so that the call is performed in two steps, like this:
pub struct Plumbus<'a> { // No extra type parameters
pub grumbo: &'a dyn Grumbo,
}
pub struct GrumboDinglebo<'a> { // Internal structure
pub this: &'a dyn Grumbo
}
impl <'a> GrumboDinglebo<'a> {
pub fn apply<T>(&self, _x: &mut T) -> bool {
false
}
}
pub trait Grumbo {
// Replace fn dinglebop<T>(&self, x: &mut T) -> bool { false } with this:
fn dinglebop(&self) -> &GrumboDinglebo;
}
At call site, replace anything you'd have written like
grumbo.dinglebox::<X>(self, xmutT)
which does not work, by
grumbo.dinglebox(self).apply::<X>(xmutT)
which works as expected.
Note, if you wanted your method dinglebox
to also be abstract, I don't have yet a solution for that
Upvotes: 0
Reputation: 58735
Is it possible to have a generic function on a trait?
Yes. But you are then trying to use the trait as an object. If a trait has a generic method then you can't use it as an object, but you can still use it as a bound for a type parameter.
That is, instead of using &'a Gumbo
, use a T: Gumbo
:
struct Plumbus<'a, T: Gumbo> {
grumbo: &'a T,
}
With the trait object, the implementation is only known at runtime. And its generic parameter is part of the implementation type, so the compiler couldn't know how to call it. With T: Gumbo
you are putting a constraint on what T
can be, but T
will always be known by the compiler at the point of use, which includes any parameters of its own.
See also:
Upvotes: 7