You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Apr 5, 2024. It is now read-only.
I just learned about variance in Rust, and was struck by its potential to cause unintended API breakages.
Does this tool check for changes in a type's variance over its lifetime and type parameters?
For example, a generic struct of the form TheStruct<T> can go from being "covariant in T" to being "invariant in T" simply because a hidden field was added to the struct. Same with lifetime parameters: AnotherStruct<'a> can go from being "covariant in 'a" to being "invariant in 'a".
The variance of other struct, enum, and union types is decided by looking at the variance of the types of their fields.
Variance is an aspect of a type's API that is not immediately obvious, yet it has the potential to break existing code when it changes.
Here's an example where crate_1_0_0::TheStruct is covariant in T, which allows the function cast_return_value_1_0_0 to compile successfully. But in crate_1_0_1, TheStruct now has a Mutex field which causes TheStruct to become invariant in T, and as a result, cast_return_value_1_0_1 no longer compiles.
mod crate_1_0_0 {pubstructTheStruct<T>{inner:T,}impl<T>TheStruct<T>{pubfnnew(inner:T) -> Self{Self{ inner }}pubfninto_inner(self) -> T{self.inner}}}mod crate_1_0_1 {pubstructTheStruct<T>{inner: std::sync::Mutex<T>,}impl<T>TheStruct<T>{pubfnnew(inner:T) -> Self{Self{inner: std::sync::Mutex::new(inner),}}pubfninto_inner(self) -> T{self.inner.into_inner().unwrap()}}}// Compiles successfullyfncast_return_value_1_0_0<'a>(x: crate_1_0_0::TheStruct<&'astr>,y: crate_1_0_0::TheStruct<&'staticstr>,) -> crate_1_0_0::TheStruct<&'astr>{println!("Discarding: {}", x.into_inner());
y
}// Does not compilefncast_return_value_1_0_1<'a>(x: crate_1_0_1::TheStruct<&'astr>,y: crate_1_0_1::TheStruct<&'staticstr>,) -> crate_1_0_1::TheStruct<&'astr>{println!("Discarding: {}", x.into_inner());
y
}fnmain(){let s = String::from("Short-lived string");let struct_short_lived = crate_1_0_0::TheStruct::new(s.as_str());let struct_static = crate_1_0_0::TheStruct::new("Static string");let one = cast_return_value_1_0_0(struct_short_lived, struct_static);println!("Remaining: {}", one.into_inner());let s = String::from("Short-lived string");let struct_short_lived = crate_1_0_1::TheStruct::new(s.as_str());let struct_static = crate_1_0_1::TheStruct::new("Static string");let one = cast_return_value_1_0_1(struct_short_lived, struct_static);println!("Remaining: {}", one.into_inner());}
This prints:
error: lifetime may not live long enough
--> src/main.rs:46:5
|
41 | fn cast_return_value_1_0_1<'a>(
| -- lifetime `'a` defined here
...
46 | y
| ^ returning this value requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `crate_1_0_1::TheStruct<&str>`, which makes the generic argument `&str` invariant
= note: the struct `crate_1_0_1::TheStruct<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
The text was updated successfully, but these errors were encountered:
In practice, one easy way to depend on a type's variance is when using self_cell or yoke to make self-referential types. These crates require the "dependent" type to be covariant in its lifetime parameter. So if your dependent type is struct Dependent<'a> { parsed_object: object::read::File<'a>, }, you're now at the mercy of the object::read::File type and your code will break if the object::File type changes to be no longer covariant in its lifetime parameter.
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
I just learned about variance in Rust, and was struck by its potential to cause unintended API breakages.
Does this tool check for changes in a type's variance over its lifetime and type parameters?
For example, a generic struct of the form
TheStruct<T>
can go from being "covariant inT
" to being "invariant inT
" simply because a hidden field was added to the struct. Same with lifetime parameters:AnotherStruct<'a>
can go from being "covariant in'a
" to being "invariant in'a
".From https://doc.rust-lang.org/reference/subtyping.html:
Variance is an aspect of a type's API that is not immediately obvious, yet it has the potential to break existing code when it changes.
Here's an example where
crate_1_0_0::TheStruct
is covariant inT
, which allows the functioncast_return_value_1_0_0
to compile successfully. But incrate_1_0_1
,TheStruct
now has aMutex
field which causesTheStruct
to become invariant inT
, and as a result,cast_return_value_1_0_1
no longer compiles.This prints:
The text was updated successfully, but these errors were encountered: