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.


Popular posts from this blog

php - How should I create my API for mobile applications (Needs Authentication) -

5 Reasons to Blog Anonymously (and 5 Reasons Not To)

Google AdWords and AdSense - A Dynamic Small Business Marketing Duo