I'm in a not-so-great situation and have to fake a lifetime. It looks a little bit like this:
struct Bar<'a> {
cr: &'a mut char,
}
fn foo<D, F>(data: D, f: F)
where
D: 'static, // <-- !!!
F: FnOnce(D),
{ ... }
let mut c = '⚠';
let bar = Bar { cr: &mut c };
foo(???, |c| /* I need access to a `Bar` here! */);
I have to call the strange function foo. In the closure I pass to it, I need to get access to a Bar (with any lifetime) that was passed through foo. (I know in this minimal example I could just access the bar directly as closures have access to their environment, but let's pretend that's not possible here.) Unfortunately, foo requires D: 'static.
Of course, in reality everything is more complicated. I know Bar and foo don't make too much sense, but this is my attempt at breaking my problem into a minimal example.
How do I make this work? I believe it is possible to make this work safely (i.e. without undefined behavior), but I'm sure it requires the unsafe keyword.
The basic idea is to cast the Bar<'not_static> to a Bar<'static> temporarily, then make sure that it does not outlive the original c. I want to know how to best do that. My idea was the following:
let mut c = '⚠';
let bar = Bar { cr: &mut c };
let bar_static: Arc<Bar<'static>> = unsafe {
let extended = mem::transmute::<Bar<'_>, Bar<'static>>(bar);
Arc::new(extended)
};
foo(bar_static.clone(), |bar| println!("{}", bar.cr));
if Arc::strong_count(&bar_static) != 1 && Arc::weak_count(&bar_static) != 0 {
eprintln!("bad!");
std::process::abort();
}
The idea is to dynamically check that no references to c exist anymore (apart from the one we are holding) after calling foo. That should protect against foo storing the data in a static variable or something like that. I don't expect it, but I rather end the whole process instead of having memory unsafety in my program.
With this, I think, I make sure that the reference (which is incorrectly 'static) does not outlive the actual data. But:
- Is that reasoning sane? Does it make sense?
- What worries me is that rustc doesn't think
cis borrowed after theunsafeblock. I could (in my function) arbitrarily accessc, although there exists a reference pointing to it. Is that a case of "I'm fine as long as I don't actually accessc"? Or rather one of those "immediate UB" cases? - Is
mem::transmutethe right tool for the job or should I use pointer casts or something else? - Any better ideas?