Is this a limitation of the borrow checker or am I doing something wrong?

The following code does not pass the borrow checker.

struct A<'a>(&'a str);

impl<'a> Drop for A<'a> {
    fn drop(&mut self) {
        println!("Dropped");
    }
}

fn main() {
    
    let mut r: Option<A> = None;
    let s = String::from("test");
    {
        r = Some(A(&s));
        r = None;
    }

}

The borrow checker complains that s does not live long enough. However, if I comment out the drop for A, the code compiles successfully.

struct A<'a>(&'a str);

fn main() {
    
    let mut r: Option<A> = None;
    let s = String::from("test");
    {
        r = Some(A(&s));
        r = None;
    }

}

Does the custom drop function make the way how drop is called different or Is this a limitation of the borrow checker?
link

2 Likes

It is both of those things.

First, implementing Drop means that the type must be valid at the end of its scope, and having &s inside it means that it won't be, since variables are dropped in the reverse order of their declaration.

Second, the borrow checker doesn't know that you overwrote it with None. You can comment out that line and it makes no difference. This could be allowed since r doesn't actually contain &s anymore.

1 Like

A non-trivial destructor does change what is required for borrow checking. "Non-trivial destructor" includes a Drop implementation, but can also happen due to the types of your fields. Non-trivial destructors require exclusive access, like a move or taking a &mut _.

In the example, A<'_> has a trivial destructor without the Drop implementation.

Incidentally, the inner block does nothing in the example.

Clarification: The inner block doesn't change the lifetimes or other outcomes of borrow checking in any way.

1 Like

Not even binding 'a to be a local rather than 'static?

s dropping at the end of the outer block (main) is incompatible with the lifetime being 'static, with or without the inner block.[1]

But the inner block doesn't restrict the inferred lifetime in anyway.[2]

  • The lifetimes of references[3] aren't confined to the block they're created in[4]

  • There are no uses at the end of the block (such as a variable going out of scope) that would conflict with a lifetime longer than the block

  • And from the complementary point-of-view, there's also nothing in the code that would require 'a: 'static with or without the block; the lifetime can expire after the last use of r[5] (be that in an inner block, the outer block, the end of some block, the middle of some block...[6])


  1. Try making the annotation Option<A<'static>>, with or without the inner block. ↩︎

  2. Try using r after the inner block. ↩︎

  3. i.e. the '_ lifetime in their type ↩︎

  4. since NLL landed 5 years ago ↩︎

  5. the compiler doesn't even have to calculate a specific lifetime, it just has to prove a sound lifetime exists ↩︎

  6. i.e. the lifetime is non-lexical ↩︎

Oh alright this was just an English issue. I thought by "inner block" you meant "inner block and its contents".

2 Likes

Ah! Thanks, I see the confusion now.

2 Likes

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.