In rust, you can have a trait, implement it into a struct, and upcast your struct to a trait object :
trait T {}
struct S {}
impl T for S {}
fn main() {
let s: S = S {};
let s_as_t: &dyn T = &s;
}
This is an incredibly useful feature, because if I have multiple objects which all implement the trait T
, I can now put them all in a single array of type Vec<Box<dyn T>>
, and define global behaviors really easily by calling a function on each element.
BUT
How do I do the same thing when my original trait also has a placeholder type ?
This works really well, no pb :
trait T_Subtype {}
trait T {
type subtype: T_Subtype;
}
struct S {}
impl T_Subtype for S {}
impl T for S {
type subtype = S;
}
fn main() {
let s: S = S {};
let s_as_t: &dyn T<subtype = S> = &s;
}
But I can't find any way to upcast the placeholder type, the following code cannot compile :
trait T_Subtype {}
trait T {
type subtype: T_Subtype;
}
struct S {}
impl T_Subtype for S {}
impl T for S {
type subtype = S;
}
fn main() {
let s: S = S {};
let s_as_t: &dyn T<subtype = dyn T_Subtype> = &s; // only line that changes
}
Without this feature, I cannot put (this is an illustration) multiple structs S1
S2
and S3
, that all implement T
but might have a different subtype, in a single array, and I have to define global behaviors for each subtype, making it really hard to maintain (especially if there are multiple subtypes), even though the function I want to call on all of them is defined !
CodePudding user response:
This isn't possible.
Consider the following code:
trait SubtypeTrait {}
trait T {
type Subtype: SubtypeTrait;
fn foo(arg: &<Self as T>::Subtype);
}
struct S {
val: i32,
}
impl SubtypeTrait for S {}
impl T for S {
type Subtype = S;
fn foo(arg: &S) {
println!("{}", arg.val);
}
}
struct X {}
impl SubtypeTrait for X {}
impl T for X {
type Subtype = S;
fn foo(arg: &S) {
println!("{}", arg.val);
}
}
// Everything compiles except `main`
fn main() {
let x: X = X {};
let x_as_t: &dyn T<Subtype = dyn SubtypeTrait> = &x;
let s: S = S {};
// Attempts to access `arg.val`...
// but `x` doesn't have `val`!
x_as_t.foo(&x);
}
The problem is that a trait bound on a placeholder type doesn't restrict the operations on the placeholder type, it only restricts the type itself. Once you specify the type you can choose any operation from the type that you want.