Compiler support for more ergonomic tags/brands

Libraries such as ghost-cell use lifetimes to enforce soundness invariants, requiring that only handles from the instance can be used with it. Unfortunately, a closure is required to create these unique tags, which is problematic in various ways.

Lifetimes are great for this because they don't monomorphize. If there was a way to conjure one of these fully-constrained lifetimes, it could viably make such APIs more ergonomic to work with.

2 Likes

Would Experimental feature gate for `super let` by m-ou-se · Pull Request #139080 · rust-lang/rust · GitHub plus macros be sufficient?

That doesn't create a lifetime you can reference in code. It only causes the Drop/StorageDead for the value to be moved to a later point.

Note that the generativity crate offers a way to create a token with such lifetime using a macro which doesn't need to wrap the code using it.

I have mused the idea of a 'virtual lifetime, which is invariant and can only appear in return type annotations. It would be created out of thin air, avoiding the need to use a closure for HRTBs. It's an extremely niche use case, but is incredibly useful when used.

A macro, such as generativity's make_guard, can conjure up a unique lifetime. However, without super-let, it invariably leaks through your API.

make_guard!(guard);
let foo = my_lib::Struct::new(guard);

Compare that with

let foo = my_lip::new_struct!();

which can expand to

let foo = {
    make_super_guard!(guard); // uses super-let
    my_lib::Struct::new(guard)
};
1 Like

One really annoying part about generative lifetimes is that they are never 'static as this lifetime is obviously nameable. And this implies that any branded data structure can not be 'static itself which creates a whole follow-up mess because these can then never be stored as globals, these guards suddenly interact with other lifetime bounds if behind a reference (e.g. you need to justify unsafe to Box::leak the guard/guarded value as a 'static even though this is obviously sound with unique tokens), all kinds of nasty surprising restrictions.

All that is necessary for lifetimes due to Any which paints Rust into a corner.

A brand kind that is just as ephemeral as lifetimes (including being irrelevant to codegen) while orthogonal to lifetimes would be neat. Quite a large extension of the language of course.

1 Like

Any can't work with brands even if they were not lifetime based. Brands have to be codegen erased to be useful. Any however can't distinguish between types where the only difference is codegen erased. And thus if we were to allow Any for brands, it would be possible to cast a type from one brand to another, which would make brands unsound.

2 Likes

Oh interesting. How would you use it? Something like this?

pub struct Foo<'brand> { ... }
pub struct Bar<'brand> { ... }

impl<'brand> Foo<'brand> {
    pub fn bar(&self) -> Bar<'brand>
    pub fn use_bar(&self, b: Bar<'brand>)
}
impl Foo<'virtual> {
    pub fn new() -> Self
}

// Compiler gives you a unique lifetime for each
let a = Foo::new();
let b = Foo::new();

a.use_bar(b.bar()) // ERROR
1 Like

Exactly. Due to lifetimes already existing and the bound on Any being nothing more than static we can not change generative lifetime parameters to allow for 'static structs anymore. Introducing another kind of generic—let's call it 'ghost'—would allow the freedom of adding orthogonal bounds such that signature of Any stays backwards compatible, i.e. an implied bound to the effect of 'type has no ghost generic parameters' with the same caveat of having to default other generics just like we did with ?Sized.

Not that the whole 'static-bound design isn't flimsy for Any either way; as by current implementation outright unsound and always was a tiny step away. having a new kind would be a good place for a fresh start of such bounds ..

Yes, exactly. It's the effect of HRTBs without a closure.

Re: 'static types containing branded lifetimes

I believe we can have such types if the branded lifetime is entirely contained within it. Accesses to it would then require generating a new fresh branded lifetime that will be unique for that access of its data.