Skip to content

Mixing 'static and 'this with iterators or vectors doesn't correctly shorten lifetime to the workable shorter version. #132291

Open
@VorpalBlade

Description

@VorpalBlade

This is either a borrow checker bug, a library bug, or a diagnostics bug. I don't know which one.

I tried this code:

use std::collections::HashMap;
use std::collections::BTreeSet;

struct DataRoot {
    entries: Vec<Entry>
}

impl DataRoot {
    pub fn columns<'this>(&'this self) -> impl Iterator<Item = &'this str> {
        let mut dynamic_columns = BTreeSet::new();

        for entry in &self.entries {
            dynamic_columns.extend(entry.dynamic_coluimns());
        }

        // This combines 'this and 'static, and I want the result to be 'this
        // (which is *shorter*)
        // This doesn't work for some reason, why? And what can be done about it?
        Entry::leading_columns()
            .chain(dynamic_columns.into_iter())
            .chain(Entry::trailing_columns())
    }

    pub fn columns2<'this>(&'this self) -> impl Iterator<Item = &'this str> {
        let mut dynamic_columns = BTreeSet::new();

        for entry in &self.entries {
            dynamic_columns.extend(entry.dynamic_coluimns());
        }

        // This doesn't work either!
        let mut values = vec![];
        values.extend(Entry::leading_columns());
        values.extend(dynamic_columns.into_iter());
        values.extend(Entry::trailing_columns());
        values.into_iter()
    }


    pub fn columns3(&self) -> impl Iterator<Item = String> {
        let mut dynamic_columns = BTreeSet::new();

        for entry in &self.entries {
            dynamic_columns.extend(entry.dynamic_coluimns());
        }

        // Okay, it makes no sense that this doesn't work.
        // * I turned it into String, so it is owned
        // * I collected into a vec and then returned that (which normally works)
        let v: Vec<_> = Entry::leading_columns()
            .chain(dynamic_columns.into_iter())
            .chain(Entry::trailing_columns())
            .map(|v| v.to_string()).collect();
        v.into_iter()
    }
}

struct Entry {
    // Various fixed fields here...

    // Other fields that are specific to this case, capture them dynamically
    // #[serde(flatten)]
    other: HashMap<String, String>
}

impl Entry {
    fn leading_columns() -> impl Iterator<Item = &'static str> {
        ["Crate name", "URL", "Maintained", "License", "Std"].into_iter()
    }

    fn dynamic_coluimns(&self) -> impl Iterator<Item = &str> {
        self.other.keys().map(|s| s.as_str())
    }

    fn trailing_columns() -> impl Iterator<Item = &'static str> {
        ["Notes"].into_iter()
    }
}

I expected to see this happen: One of the following:

  • All three cases works, and 'static is shortened to 'this
  • The compiler errors nudge you towards a solution to the issue rather than suggesting incorrect things like adding + 'this

Ideally (if this can't be fixed so that it works) the compiler error should nudge you in the direction that jendrikw suggested on URLO:

Entry::leading_columns().map(|s: &'static str| -> &'this str {s})
    .chain(dynamic_columns.into_iter())
    .chain(Entry::trailing_columns().map(|s: &'static str| -> &'this str {s}))

Instead, this happened:

   Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
  --> src/lib.rs:19:9
   |
9  |       pub fn columns<'this>(&'this self) -> impl Iterator<Item = &'this str> {
   |                      ----- lifetime `'this` defined here
...
19 | /         Entry::leading_columns()
20 | |             .chain(dynamic_columns.into_iter())
21 | |             .chain(Entry::trailing_columns())
   | |_____________________________________________^ returning this value requires that `'this` must outlive `'static`
   |
help: to declare that `impl Iterator<Item = &'this str>` captures data from argument `self`, you can add an explicit `'this` lifetime bound
   |
9  |     pub fn columns<'this>(&'this self) -> impl Iterator<Item = &'this str> + 'this {
   |                                                                            +++++++

error[E0521]: borrowed data escapes outside of method
  --> src/lib.rs:27:22
   |
24 |     pub fn columns2<'this>(&'this self) -> impl Iterator<Item = &'this str> {
   |                     -----  ----------- `self` is a reference that is only valid in the method body
   |                     |
   |                     lifetime `'this` defined here
...
27 |         for entry in &self.entries {
   |                      ^^^^^^^^^^^^^
   |                      |
   |                      `self` escapes the method body here
   |                      argument requires that `'this` must outlive `'static`

error[E0521]: borrowed data escapes outside of method
  --> src/lib.rs:43:22
   |
40 |     pub fn columns3(&self) -> impl Iterator<Item = String> {
   |                     -----
   |                     |
   |                     `self` is a reference that is only valid in the method body
   |                     let's call the lifetime of this reference `'1`
...
43 |         for entry in &self.entries {
   |                      ^^^^^^^^^^^^^
   |                      |
   |                      `self` escapes the method body here
   |                      argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` (lib) due to 3 previous errors

Meta

rustc --version --verbose:

rustc 1.82.0 (f6e511eec 2024-10-15)
binary: rustc
commit-hash: f6e511eec7342f59a25f7c0534f1dbea00d01b14
commit-date: 2024-10-15
host: x86_64-unknown-linux-gnu
release: 1.82.0
LLVM version: 19.1.1
Backtrace

No crash, so not applicable?

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions