How do I deal with wrapper type invariance in Rust? -
references wrapper types &rc<t>
, &box<t>
invariant in t
(&rc<t>
not &rc<u>
if t
u
). concrete example of issue (rust playground):
use std::rc::rc; use std::rc::weak; trait mytrait {} struct mystruct { } impl mytrait mystruct {} fn foo(rc_trait: weak<mytrait>) {} fn main() { let = rc::new(mystruct {}); foo(rc::downgrade(&a)); }
this code results in following error:
<anon>:15:23: 15:25 error: mismatched types: expected `&alloc::rc::rc<mytrait>`, found `&alloc::rc::rc<mystruct>`
similar example (with similar error) box<t>
(rust playground):
trait mytrait {} struct mystruct { } impl mytrait mystruct {} fn foo(rc_trait: &box<mytrait>) {} fn main() { let = box::new(mystruct {}); foo(&a); }
in these cases of course annotate a
desired type, in many cases won't possible because original type needed well. do then?
what see here not related variance , subtyping @ all.
first, informative read on subtyping in rust this chapter of nomicon. can find there in rust subtyping relationship (i.e. when can pass value of 1 type function or variable expects variable of different type) limited. can observed when you're working lifetimes.
for example, following piece of code shows how &box<t>
(co)variant:
fn test<'a>(x: &'a box<&'a i32>) {} fn main() { static x: i32 = 12; let xr: &'static i32 = &x; let xb: box<&'static i32> = box::new(xr); // <---- start of box lifetime let xbr: &box<&'static i32> = &xb; test(xbr); // covariance in action: since 'static longer or // same 'a, &box<&'static i32> can passed // function expects &'a box<&'a i32> // // note important both "inner" , "outer" // references in function signature defined // same lifetime parameter, , in `test(xbr)` call // 'a gets instantiated lifetime associated // scope i've marked <----, nevertheless // able pass &'static i32 &'a i32 because // aforementioned scope less 'static, therefore // shared reference type 'static lifetime subtype of // reference type lifetime of scope } // <---- end of box lifetime
this program compiles, means both &
, box
covariant on respective type , lifetime parameters.
unlike of "conventional" oop languages have classes/interfaces c++ , java, in rust traits do not introduce subtyping relationship. though, say,
trait show { fn show(&self) -> string; }
highly resembles
interface show { string show(); }
in language java, quite different in semantics. in rust bare trait, when used type, never supertype of type implements trait:
impl show i32 { ... } // above not mean i32 <: show
show
, while being trait, indeed can used in type position, denotes special unsized type can used form trait objects. cannot have values of bare trait type, therefore not make sense talk subtyping , variance bare trait types.
trait objects take form of &sometrait
or &mut sometrait
or smartpointer<sometrait>
, , can passed around , stored in variables , needed abstract away actual implementation of trait. however, &t
t: sometrait
is not subtype of &sometrait
, , these types not participate in variance @ all.
trait objects , regular pointers have incompatible internal structure: &t
regular pointer concrete type t
, while &sometrait
fat pointer contains pointer original value of type implements sometrait
, second pointer vtable implementation of sometrait
of aforementioned type.
the fact passing &t
&sometrait
or rc<t>
rc<sometrait>
works happens because rust automatic coercion references , smart pointers: able construct fat pointer &sometrait
regular reference &t
if knows t
; quite natural, believe. instance, example rc::downgrade()
works because rc::downgrade()
returns value of type weak<mystruct>
gets coerced weak<mytrait>
.
however, constructing &box<sometrait>
out of &box<t>
if t: sometrait
more complex: one, compiler need allocate new temporary value because box<t>
, box<sometrait>
has different memory representations. if have, say, box<box<t>>
, getting box<box<sometrait>>
out of more complex, because need creating new allocation on heap store box<sometrait>
. thus, there no automatic coercions nested references , smart pointers, , again, not connected subtyping , variance @ all.