rust icon indicating copy to clipboard operation
rust copied to clipboard

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

Open cdhowie opened this issue 1 year ago • 4 comments

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 eb4e2346748e1760f74f).

cdhowie avatar Oct 10 '24 09:10 cdhowie

This was fixed by https://github.com/rust-lang/rust/pull/116040, but that was not sound. Fixing this is particularly nontrivial.

This may be easier to address soon if https://github.com/rust-lang/rust/pull/131033 lands and is then stabilized, since you should be able to express the +'static as +use<Self> instead.

compiler-errors avatar Oct 10 '24 13:10 compiler-errors

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?

cdhowie avatar Oct 10 '24 17:10 cdhowie

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;
}

compiler-errors avatar Oct 11 '24 08:10 compiler-errors

Since https://github.com/rust-lang/rust/pull/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()
    }
}

lqd avatar Oct 11 '24 09:10 lqd