Skip to content

RPITIT causes incorrect "dropped while still borrowed" error in some cases #131490

Open

Description

@cdhowie

From this SO question, the following code does not compile:

trait Iterable {
    fn owned_iter(&self) -> impl Iterator<Item = usize> + 'static;
}

struct B<T> {
    x: T,
}

impl<T: Iterable + Clone> Iterable for B<T> {
    fn owned_iter(&self) -> impl Iterator<Item = usize> + 'static {
        let y = self.x.clone();
        y.owned_iter() // error[E0597]: `y` does not live long enough
    }
}

However, boxing the iterator as a trait object allows it to compile:

trait Iterable {
    fn owned_iter(&self) -> impl Iterator<Item = usize> + 'static;
}

struct B<T> {
    x: T,
}

impl<T: Iterable + Clone> Iterable for B<T> {
    fn owned_iter(&self) -> impl Iterator<Item = usize> + 'static {
        let y = self.x.clone();
        let b: Box<dyn Iterator<Item = usize>> = Box::new(y.owned_iter());
        b
    }
}

Notably, not cloning self.x and simply returning self.x.owned_iter() also works.

I don't see any reason why the first code block shouldn't compile, especially while the boxed version and the non-cloning version do.

Meta

rustc --version --verbose:

rustc 1.81.0 (eeb90cda1 2024-09-04)
binary: rustc
commit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c
commit-date: 2024-09-04
host: x86_64-unknown-linux-gnu
release: 1.81.0
LLVM version: 18.1.7

It also fails to compile on 1.83.0-nightly (2024-10-09 eb4e234).

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 10, 2024
  2. compiler-errors commented on Oct 10, 2024

    @compiler-errors
    Contributor

    This was fixed by #116040, but that was not sound. Fixing this is particularly nontrivial.

    This may be easier to address soon if #131033 lands and is then stabilized, since you should be able to express the +'static as +use<Self> instead.

  3. cdhowie commented on Oct 10, 2024

    @cdhowie
    Author

    But the returned iterator in this case doesn't even borrow self, so it doesn't seem like that should be necessary. It also changes the semantic meaning of the returned type to borrow self when the intent of this code is exactly the opposite. Am I missing something?

  4. compiler-errors commented on Oct 11, 2024

    @compiler-errors
    Contributor

    Am I missing something?

    I think so :)

    So the RPITIT wouldn't be capturing self: &Self, but instead it would be capturing Self, the implicit type parameter of the trait. That is to say, he capture we want to avoid is the elided lifetime 'a (which is elided in this case) like:

    trait Iterable {
        fn owned_iter<'a>(&'a self) -> impl Iterator<Item = usize> + 'static;
    }
  5. lqd commented on Oct 11, 2024

    @lqd
    Member

    Since #131033 landed, this should work on nightly now

    #![feature(precise_capturing_in_traits)]
    
    trait Iterable {
        fn owned_iter(&self) -> impl Iterator<Item = usize> + use<Self>;
    }
    
    struct B<T> {
        x: T,
    }
    
    impl<T: Iterable + Clone> Iterable for B<T> {
        fn owned_iter(&self) -> impl Iterator<Item = usize> + use<T> {
            let y = self.x.clone();
            y.owned_iter()
        }
    }
  6. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Nov 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions