I’ve got a LayoutCtx that wraps a &mut reference. I’m passing this down a tree of “widgets” as they do layout. But I’ve hit an bit of ugliness in my design, and feel I need to cheat a bit and create an OtherCtx from a LayoutCtx, just for a few lines of code.
This looks like what people are calling a “reborrow”, but I’m not sure.
The compile error is show below. Is there any way to do this? Maybe I need to pass my LayoutCtx by value, not by &mut reference. That would mean I have to keep reconstructing them, which is annoying.
struct State { }
struct LayoutCtx<'s> {
window_state: &'s mut State
}
struct OtherCtx<'s> {
window_state: &'s mut State
}
impl<'s> LayoutCtx<'s> {
fn fee(&self) { }
fn to_other_ctx(&mut self) -> OtherCtx<'s> {
// fn to_other_ctx(mut self) -> OtherCtx<'s> {
OtherCtx {
window_state: self.window_state
}
}
}
impl<'s> OtherCtx<'s> {
fn faa(&self) { }
}
fn layout(lo_ctx: &mut LayoutCtx) {
// But I need OtherCtx, just for a bit!
let mut other_ctx = lo_ctx.to_other_ctx();
other_ctx.faa();
lo_ctx.fee();
}
fn main() {
println!("Test");
let mut state = State { };
let mut lo_ctx = LayoutCtx { window_state: &mut state };
layout(&mut lo_ctx);
}
Compile error:
error: lifetime may not live long enough
--> learning/rust_lang/examples/transform_ctxs.rs:13:9
|
9 | impl<'s> LayoutCtx<'s> {
| -- lifetime `'s` defined here
10 | fn fee(&self) { }
11 | fn to_other_ctx(&mut self) -> OtherCtx<'s> {
| - let's call the lifetime of this reference `'1`
13 | / OtherCtx {
14 | | window_state: self.window_state
15 | | }
| |_________^ method was supposed to return data with lifetime `'s` but it is returning data with lifetime `'1`
It's not reborrowing, but it is emulating reborrowing to the extent possible with a function signature.
The reason you can't return OtherCtx<'s> is that you can't get a &'long mut U through dereferencing a &'short mut &'long mut U, which is what window_state: self.window_state is effectively trying to do with the original signature. (If the OP compiled, after the method returned you would have two active &mut _ to the same State, and that's undefined behavior.)
I don't see why OtherCtx and LayoutCtx need be different types, but that's a separate issue.
Reborrowing is only possible with references (so far[1]). The idea behind reborrowing is: you have an existing reference r and referent *r. The reborrow is a borrow of *r. It cannot be valid for any longer than r was valid, but -- crucially -- the reborrow is not a borrow of r itself.
That means, for example, r can go out of scope without making the reborrow invalid (the reborrow reference can "out last" the original reference).
In contrast, with to_other_ctx, the original LayoutCtx<'s> remains borrowed so long as the returned OtherCtx<'_> is in use.
Here's a demonstration in code form:
As demonstrated in the playground, creating OtherCtx<'_> (or LayoutCtx<'_>) in place may work in situations where to_other_ctx does not. On the other hand, to_other_ctx may be enough for your use case.
There are some experiments around being able to implement reborrowing for your own struct, but they're in the infant stages. ↩︎
Okay, let me see if I can get some terms right, and some other pieces of this.
So in the fixed code, the lifetime parameter ‘x of other_ctx: OtherCtx<‘x> is the lifetime of the reference lo_ctx: &'x mut LayoutCtx? It knows that while I have other_ctx, I can’t use lo_ctx. Do you say that other_ctx is a “borrow” of lo_ctx in that case (inside the layout function)?
Part of what’s confusing me now is that the lifetime of other_ctx, is obviously less than the lifetime of lo_ctx, but the lifetime parameter in OtherCtx is taken from the lo_ctx.
fn layout<'x, 'lo>(lo_ctx: &'x mut LayoutCtx<'lo>) {
let mut other_ctx: OtherCtx< /* 'other */ > = lo_ctx.to_other_ctx();
other_ctx.faa();
// `'other` can end here
lo_ctx.fee();
}
other_ctx ends up being OtherCtx<'other> and not OtherCtx<'x>. How? *lo_ctx was automatically reborrowed when you called .to_other_ctx().[1]
The lifetimes are related: you can't reborrow *lo_ctx for longer than 'x. But you can reborrow it for some shorter duration. The relationship in code form is 'x: 'other.
So the parameter of OtherCtx<'_> is not 'x exactly. But it is true that while you have other_ctx, you can't use lo_ctx.
I guess I'd say something like: other_ctx contains a reborrow of *lo_ctx.
That helps a lot, thanks. There’s still a lot I don’t get about lifetimes, but one step at a time. I need to read your entire site that you linked to above.