Is there any way to track value "owner" so that it passing it to other owners is a compile-time error? I've been looking into invariant lifetimes and the generativity
crate, but my rust-fu is not quite up to the task.
Code probably explains what I'm trying to do the best:
use core::marker::PhantomData;
// The lifetime `'a` is somehow only valid for the `Bar` that created `Foo`
#[derive(Default, Copy, Clone)]
struct Foo<'a> {
phantom: PhantomData<&'a Owner>,
}
#[derive(Default)]
struct Owner {}
impl Owner {
fn get<'a, 'b: 'a>(&'a self) -> Foo<'b> {
Foo::<'b>::default();
}
// Somehow requires that the unique lifetime of `self` applies also to `Foo`.
fn set<'a>(&'a mut self, _foo: Foo<'a>) {
// whatever code
}
}
fn main() {
let mut owner = Owner::default();
let mut owner2 = Owner::default();
let anon_foo = Foo::default();
let foo2 = owner2.get();
owner.set(foo2); // should not compile
owner.set(anon_foo); // also should not compile
owner2.set(foo2); // compiles!
}
This is a rather theoretical question, I'm not trying to solve a specific problem, just trying to see if such zero-cost abstraction construct is possible in Rust.
EDIT: This is not required to work in non-trivial cases, it just has to fail-safe (should never allow set
if compiler cannot prove the Owner
is the actual owner).
Also, the creation of the Owner struct can be with a macro, it doesn't have to be runtime constructable.
I think that instead of lifetimes, that have variance and weird rules, it would be easier to use a token type. You can create a local nested token type by using a macro, or requiring that the users specify the token manually.
This solution tracks the owner by its creation point, so if you create two owners in the same line of code, they will share ownership. Consider that a non-trivial case.
The code would be something like this:
struct Foo<T> {
_pd: std::marker::PhantomData<T>,
}
//Default is only for anonymous Foo
impl Default for Foo<()> {
fn default() -> Self {
Foo {
_pd: std::marker::PhantomData,
}
}
}
struct Owner<T> {
_pd: std::marker::PhantomData<T>,
}
impl<T> Default for Owner<T> {
fn default() -> Self {
Owner {
_pd: std::marker::PhantomData,
}
}
}
impl<T> Owner<T> {
fn get(&self) -> Foo<T> {
Foo {
_pd: std::marker::PhantomData,
}
}
fn set(&self, foo: Foo<T>) {
}
}
macro_rules! new_owner {
() => {{
struct Tok;
Owner::<Tok>::default()
}}
}
This code passes your test case perfectly fine:
fn main() {
let mut vec = Vec::new();
for i in 0..2 {
vec.push(new_owner!());
}
let mut owner = new_owner!();
let mut owner2 = new_owner!();
let anon_foo = Foo::default();
let foo2 = owner2.get();
owner.set(foo2); // error
owner.set(anon_foo); // error
owner2.set(foo2); // ok!
}
The error emitted is something like:
error[E0308]: mismatched types
--> src/main.rs:53:15
|
53 | owner.set(foo2); // error
| --- ^^^^ expected `main::Tok`, found a different `main::Tok`
| |
| arguments to this method are incorrect
|
= note: expected struct `Foo<main::Tok>` (`main::Tok`)
found struct `Foo<main::Tok>` (`main::Tok`)
Since the token is build by the macro, if you somehow create both owners in the same macro call, it will compile:
fn main() {
let mut owners = Vec::new();
for _ in 0..2 {
owners.push(new_owner!()); // just one macro invocation
}
let foo = owners[0].get();
owners[1].set(foo); // OK, same token type.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With