Consider the following Rust program:
#![feature(generic_associated_types)]
pub trait Func {
type Input<'a>;
type Output;
fn call(self, input: Self::Input<'_>) -> Self::Output;
}
fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
where F: 'static + Func<Input<'cx> = &'cx u8, Output = u8>,
{
let input = &*ctx;
let out = f.call(input);
*ctx = out;
}
I've used #![feature(generic_associated_types)]
, but I think the question I'm asking is still relevant if you move 'a
from Func::Input
to Func
and use a higher-rank trait bound on invoke
.
This code errors, but I don't think it's unsound:
error[E0506]: cannot assign to `*ctx` because it is borrowed
--> src/lib.rs:15:5
|
10 | fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
| --- lifetime `'cx` defined here
...
13 | let input = &*ctx;
| ----- borrow of `*ctx` occurs here
14 | let out = f.call(input);
| ------------- argument requires that `*ctx` is borrowed for `'cx`
15 | *ctx = out;
| ^^^^^^^^^^ assignment to borrowed `*ctx` occurs here
First ctx
is reborrowed as input
, which is passed to f.call
and then never used again. f.call
returns a value that does not contain any lifetimes (u8: 'static
), so there is no connection between out
and ctx
.
Likewise, the type of f
contains no lifetimes (F: 'static
), so it cannot hold a reference with lifetime 'cx
. Furthermore, the lifetime 'cx
cannot be safely coerced to 'static
inside call
, so there's no way to "smuggle out" a reference with that lifetime that's accessible beyond the invocation of f.call
. Therefore, I don't see how anything can alias ctx
, and I think assigning to it in the last line should be sound.
Am I missing something? Would accepting this code be unsound? If not, why does Rust fail to take advantage of 'static
bounds in this way?