Why is the mutable reference not being dropped after the function call?

Why is the mutable reference, passed into borrow_mut, not being dropped after the function call? Does it have anything to do with the generics involved?

The following code:

use std::borrow::Cow;

fn borrow_mut<'a>(s: &'a mut Cow<'a, String>) {
    
}

fn borrow<'a>(s: &'a Cow<'a, String>) {
    
}

fn main() {
    let mut s = Cow::Owned(String::new());

    // immutable reference
    borrow_mut(&mut s);

    // immutable borrow
    borrow(&s);
}

Generates to error:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> src/main.rs:22:12
   |
18 |         borrow_mut(&mut s);
   |                    ------ mutable borrow occurs here
...
22 |     borrow(&s);
   |            ^^
   |            |
   |            immutable borrow occurs here
   |            mutable borrow later used here

Because you're borrowing the underlying struct forever. Make this change so you don't force the inner and outer lifetimes to be the same:

-fn borrow_mut<'a>(s: &'a mut Cow<'a, String>) {}
+fn borrow_mut(s: &mut Cow<'_, String>) {}

-fn borrow<'a>(s: &'a Cow<'a, String>) {}
+fn borrow(s: &Cow<'_, String>) {}

&mut X is invariant in X, so the 'a in &mut Cow<'a, String> cannot be "shrunk", and thus a &'a mut Cow<'a, String> borrows the Cow for however long the original 'a is.


Side note: you probably want Cow<'_, str>.

4 Likes

Thanks @quinedot!

I'm currently reading through the resources you linked, still wrapping my head around them, and trying to understand why this solution doesn't work for me in a similar case.

If you happen to be trying to borrow one of your own fields, that's covered in the couple of pages after "borrowing forever".

If not, feel free to share more context. :slight_smile:

1 Like

I've been struggling with this for days, so would definitely appreciate your help.

For context, I'm trying to create a container that contains an item I'm calling parent, and an item I'm calling child with a lifetime bound to parent. The container doesn't have any lifetime generics, but is able to hold child even though child has a lifetime generic, by ensuring that the parent outlives the child, and keeping the parent pinned.

I got it to mostly work, but I'm struggling with the set_child method that let's the consumer create the child from a parent reference. I can't wrap my head around how to set up the lifetimes correctly.
I would super appreciate if you can point out where I'm going wrong.

1 Like

Let us...

        // Call this `Junior<'x>`  vvvvvv
        let mut ltpc: Ltpc<Senior, Junior> = Ltpc::new(Senior {
            val: String::from("foo")
        });
        // The compiler will (try to) infer a worable lifetime for `'x`

Then your closure here is[1] a

FnOnce(&'x mut Senior) -> Junior<'x>

and when you call set_child, you exclusively borrow ltpc for 'x.

Then after set_child, you call ltpc.child(); that returns some &'z Junior<'x>. 'x has to be alive here, since you return a way to observe the Junior<'x>.

But ltpc is exclusively borrowed for 'x, so you can't call child(). That's the borrow error.


Building safe abstractions around self-referencing structs is notoriously difficult. Every attempt I'm aware of has had multiple soundness issues. I'm not sure any are completely sound now, even; I doubt it. But you may still want to look at some of the more successful[2] attempts like yoke or ouroboros to see what they've done, and to look at the history of things they've done wrong (current or closed soundness issues).


For whatever it's worth, you've gotten to a state which is also possible without unsafe or pinning.


  1. technically the input lifetime could be longer than 'x ↩︎

  2. or at least tenacious ↩︎

2 Likes

Thanks @quinedot for your input! I super appreciate it!

To be honest, I really don't wanna do any of this. I'm quite frustrated that there's no simple way to pair two items together, and ensure that one doesn't outlive the other, without using lifetimes. All I need is the ability to store items in a data structure that requires it's items to be Any (i.e. owned), and I need to store there a type that has a generic lifetime. It's frustrating that this keeps me from making any progress at all, especially considering that this is all just for a prototype.

Any recommendations on how to go about this?

I'm not sure I understand exactly what the requirements are. You say

And I think you're talking about specific values here? So that you cannot allow the parent nor the child (if present) to be replaced. Correct?

Also in your playground you stated

// 1) Parent lives longer than Child.
// 2) Parent dosent move while inside.
// 3) Child doesn't reffer to other Parent.

and

/// LTPC: LifeTime Parent Child.
/// Get child that holds lifetime reference to parent into a conatiner
/// struct without a lifetime, while making sure child can't outlive parent.

And what comes to mind is

  • When you say "refer to", are we talking references (&, &mut) specifically? If so then you're definitely in self-referencial struct territory and your best bet will be to either figure out a different design, or to use one of the aforementioned dedicated crates (ouroboros, yoke).[1][2]

  • Is the pinning a requirement for you, or were you just trying to keep things sound?

  • When you say "without a lifetime", do you really mean "meets a 'static bound"? From your latest post, I think you do. But struct with generic type parameters only meets a 'static bound if all the parameters meet a 'static bound (even if it has no direct lifetime parameters). So a Ltpc<Senor, Junior<'x>> only meets a 'static bound if 'x is exactly 'static (even though Ltpc itself has no lifetime parameters).[3]

And more recently you state

This is mostly the same idea as the last bullet point. You can't store something with a non-'static lifetime in a data structure that is 'static, unless you unsafely erase that lifetime (in which case it will be very difficult to remain sound).

One possible safe alternative is to use something like Rc<_> or Arc<_> instead of references, but this probably doesn't work with the requirement to "pair" two items together. Once you're handing out references to the parent or child, the referenced value can be replaced (always with &mut, but also as-far-as-you-know with &T in a generic context, because the T could have interior mutability).

I think you understand this given what was and wasn't a public function in your playground, but I'm not 100% sure about that.


  1. And the rest of my questions probably don't matter. ↩︎

  2. It's also technically possible to safely create something self-referencial and then only operate below that point on the stack (if there are no non-trivial destructors), but always or almost always you can do the same thing by keeping the structs separate (non-self-referencial) and only operating below that point on the stack. I.e. self-referencial is more restrictive for no benefit. ↩︎

  3. Otherwise you could trivially make the items in a Vec<&Whatever> dangle, say. ↩︎

1 Like

@quinedot

So, I've spent the last few days trying to make ouroboros work for me. (don't think yoke would work in my case). I got it to mostly compile, and I think this is the solution I needed.

I only wanted pin because I wanted to ensure soundness, wasn't a requirement outside of that.

I don't think Rc<T>/Arc<T> would work for me, because T has a lifetime generic.

I super appreciate your guidance here! You kept me from going down a path of building my own container, without really understanding the soundness issues. I've also learned a ton about lifetimes from you, that will inevitably be very useful.

Thanks so much :heart:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.