Edit: I opened an issue on GitHub: https://github.com/rust-lang/rust/issues/58868.
Edit 2: With the help of someone at Github I was able to get a working version. Key was to add a HRTB to the definition of Controller
inside AsWorkspaceController
:
trait AsWorkspaceController<'a> {
type Controller: for<'b> WorkspaceController<'b>+'a;
fn get_controller(&'a mut self) -> Self::Controller;
}
While working on a project I hit a brick wall when designing a more complicated way to reborrow an object so that a different interface is exposed. This is the definition of the types used (stripped down):
trait WorkspaceLog {
fn get(&self) -> usize;
}
struct TheLog<'a>(&'a FSWorkspaceController<'a>);
impl<'a> WorkspaceLog for TheLog<'a> {
fn get(&self) -> usize {
((self.0).0).0
}
}
trait WorkspaceController<'a> {
type Log: WorkspaceLog+'a;
fn get_log(&'a self) -> Self::Log;
fn set_log(&mut self, x: usize);
}
struct FilesystemOverlay(usize);
struct FSWorkspaceController<'a>(&'a mut FilesystemOverlay);
impl<'a, 'b> WorkspaceController<'b> for FSWorkspaceController<'a> {
type Log = TheLog<'b>;
fn get_log(&'b self) -> Self::Log {
TheLog(&*self)
}
fn set_log(&mut self, x: usize) {
(self.0).0 = x;
}
}
trait AsWorkspaceController<'a, 'b> {
type Controller: WorkspaceController<'b>+'a;
fn get_controller(&'a mut self) -> Self::Controller;
}
impl<'a, 'b> AsWorkspaceController<'a, 'b> for FilesystemOverlay {
type Controller = FSWorkspaceController<'a>;
fn get_controller(&'a mut self) -> FSWorkspaceController<'a> {
FSWorkspaceController(self)
}
}
fn init1(control_dir: &mut FilesystemOverlay) -> usize {
let controller = control_dir.get_controller();
let log = controller.get_log();
log.get()
}
Now, if I directly use the type FilesystemOverlay everything is just fine:
fn init1(control_dir: &mut FilesystemOverlay) -> usize {
let controller = control_dir.get_controller();
let log = controller.get_log();
log.get()
}
However, if I try to generalize control_dir, I get a problem:
fn init2<O>(control_dir: &mut O) -> usize
where for<'a, 'b> O: AsWorkspaceController<'a, 'b> {
let controller = control_dir.get_controller();
let log = controller.get_log();
log.get()
}
Although this compiles, there is a warning which might eventually be turned into an error as there seems to be an unsoundness that was fixed by NLL. However, I do not understand what the problem is and how to fix it:
warning[E0597]: `controller` does not live long enough
--> src/main.rs:59:15
|
59 | let log = controller.get_log();
| ^^^^^^^^^^ borrowed value does not live long enough
60 | log.get()
61 | }
| -
| |
| `controller` dropped here while still borrowed
| borrow might be used here, when `controller` is dropped and runs the destructor for type `<O as AsWorkspaceController<'_, '_>>::Controller`
|
= warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
It represents potential unsoundness in your code.
This warning will become a hard error in the future.
How do I need to change the code to get rid of the unsoundness? This is the full code in the playground.