Warning
This post was published 110 days ago. The information described in this article may have changed.
So I was playing with the variance rule, checking if I'm understanding anything, which turns out to be a... big NO. Consider this snippet (if you prefer playground)
#[derive(Debug)]
struct Foo<'f>(#[allow(unused)] &'f str);
impl Foo<'static> {
fn is_static_shared(&self) {}
fn is_static_exclusive(&mut self) {}
}
fn foo(_f: &mut Foo<'static>) {
/*
* This line fails to compile, as expected:
* the fact `Foo<'static>` and `Foo<'_>` has subtype relation
* does not imply relation between `&mut Foo<'static>` and `&mut Foo<'_>`:
* `&mut T` is invariant over `T`
*/
// std::mem::swap(&mut Foo(&String::from("not static")), _f);
}
fn bar<'f>(#[allow(unused)] f: &mut Foo<'f>) {
// group A and group B cannot coexist!
let mut g = Foo("static");
{
// group A
// g.is_static_shared();
// (&mut g).is_static_shared();
// (&mut g).is_static_exclusive();
}
{
// group B
std::mem::swap(f, &mut g);
std::mem::swap::<Foo<'f>>(f, &mut g);
}
}
fn main() {
let main = String::from("main");
let mut main = Foo(&main);
bar(&mut main);
foo(&mut Foo("static"));
dbg!(main);
}
My question is around bar
:
Foo
that may borrow from some other variables, to one that borrow nothing from the context of program execution. But from a variance PoV things get tricky: we have &'_ mut Foo<'f>
and &'_ mut Foo<'static>
, and they are not subtype of one another by the fact &'a mut T
being invariant over T
, but somehow std::mem::swap
thinks there's some T
upon which both of the operand exclusive references agree via subtype coersion. Again, such T
should not exist, no?g
is not Foo<'static>
in the first place. So I added group A. Group A itself alone, without group B, compiles just fine. It's when enabling both group A and group B the compiler becomes confused.error[E0521]: borrowed data escapes outside of function
--> src/main.rs:29:9
|
20 | fn bar<'f>(#[allow(unused)] f: &mut Foo<'f>) {
| -- - `f` is a reference that is only valid in the function body
| |
| lifetime `'f` defined here
...
29 | (&mut g).is_static_exclusive();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| `f` escapes the function body here
| argument requires that `'f` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `Foo<'static>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` (bin "playground") due to 1 previous error
I'm assuming the reason here is Rust doing the variance type checking and subtype coersion not just at function call site, but also at local variable definition site, specifically the definition for g
, i.e. the let mut g = Foo("static");
line before both group A and B.
g
as Foo<'f>
, i.e. a subtype coersion happens here; it should be okay since Foo<'f>
being covariant over 'f
, and sunshine and rainbows.g
as Foo<'static>
, again all sunshine and rainbows.&'a mut T
being invariant over T
rejects the code, just like the line in foo
should be commented out.
Does such statement, i.e. Rust variance type coersion happens also at local variable definition site besides function call site, make any sense? If so, are there some materials around this aspect of Rust? Or maybe I missed something s.t. the statement is completely off and things just don't work like this?
1 post - 1 participant
🏷️ rust_feed