actually @Schard was actually correct, and i do believe the first example should indeed not compile :
consider
struct PrintOnDrop<'a>(&'a str);
impl<'a> Drop for PrintOnDrop<'a> {
fn drop(&mut self) {
dbg!(self.0);
}
}
struct MyEviler;
impl<'arena> Env<'arena> for MyEviler {
type Arena = PrintOnDrop<'arena>;
fn init() -> Self::Arena {
PrintOnDrop("a")
}
}
if you call eval_bool<E: for<'arena> Env<'arena>>() on it.
then necessarily the lifetime on init and eval_arena must be the same.
fn eval_bool<E = MyEviler>() {
let arena : PrintOnDrop<'local> = <MyEviler as Env<'local>>::init();
eval_arena::<'local, MyEviler>(&'local arena);
// arena cannot be dropped here or before because it is still borrowed
// 'local ends here
// arena cannot be dropped here or after because the value it holds is invalid
}
the solution @Schard mentionned :
fn eval_arena<'borrow, 'arena: 'borrow, E: Env<'arena>>(_: &'borrow E::Arena) {
todo!()
}
is probably best.
and you should definitely read &'a Struct<'a> and covariance - Learning Rust
edit : i have found a type that would make this do UB :
use std::cell::Cell;
struct Sketchy<'a> {
string : String,
cell : Cell<Option<&'a Sketchy<'a>>>
}
impl<'a> Drop for Sketchy<'a> {
// if very_dangerous was called on self UB wil happen
// this can be called by creating a `Sketchy` normally and dropping it
fn drop(&mut self) {
let cp = self.cell.get();
if let Some(other) = cp {
let other_string : &str = &other.string;
self.string = String::new(); // deallocate
dbg!(other_string); // if very_dangerous was called on self this would do use after free
}
}
}
// if this is called and the value is dropped UB wil happen
// this can be called safely through Box::leak
fn very_dangerous<'a>(x: &'a Sketchy<'a>) -> &'a Sketchy<'a>{
x.cell.set(Some(x));
x
}
and i have found another fix that works without changing eval_arena by making sure drop isn't called.
trait Env<'arena> {
type Arena;
fn init() -> Self::Arena;
}
fn eval_arena<'arena, E: Env<'arena>>(_: &'arena E::Arena) {
todo!()
}
fn eval_bool< E: for<'arena> Env<'arena>>() {
let mut no_drop_slot : MaybeUninit<E::Arena> = MaybeUninit::uninit();
let arena = no_drop_slot.write(E::init());
eval_arena::<E>(arena);
}
proving the destruction is indeed the problem