How to indicate that the return type contains references to multiple inputs

I thought I could give each of them a lifetime and give the return type the lifetime 'a + 'b but the compiler seems to disagree, insisting that they have different lifetimes...

use std::collections::HashMap;
use std::sync::Arc;
use url::Url;

trait A: Send + Sync {
    fn yay(&self, url: &Url) -> Option<usize>;
}

struct B {
    hash: HashMap<&'static str, Arc<dyn A>>,
    arr: Vec<Arc<dyn A>>,
}

impl B {
    fn test<'a, 'b>(&'a self, url: &'b Url) -> impl Iterator<Item=(&'a Arc<dyn A>, usize)> + 'a + 'b {
        let domain = url.domain().map(|d| d.to_ascii_lowercase());
        let checker = |item: &'a Arc<dyn A>| {
			let yay = item.yay(url);
			yay.map(|i| (item, i))
		};
		let a = domain.and_then(|d| self.hash.get(d.as_str()).and_then(checker));
		let b = self.arr.iter().flat_map(checker);
		a.into_iter().chain(b)
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0623]: lifetime mismatch
  --> src/lib.rs:15:48
   |
15 |     fn test<'a, 'b>(&'a self, url: &'b Url) -> impl Iterator<Item=(&'a Arc<dyn A>, usize)> + 'a + 'b {
   |                                    -------     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                                    |           |
   |                                    |           ...but data from `self` is returned here
   |                                    this parameter and the return type are declared with different lifetimes...

error[E0623]: lifetime mismatch
  --> src/lib.rs:15:48
   |
15 |     fn test<'a, 'b>(&'a self, url: &'b Url) -> impl Iterator<Item=(&'a Arc<dyn A>, usize)> + 'a + 'b {
   |                     --------                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                     |                          |
   |                     |                          ...but data from `url` is returned here
   |                     this parameter and the return type are declared with different lifetimes...

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0623`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Possible solution: Let all inputs have the same lifetime

impl B {
    fn test<'a>(&'a self, url: &'a Url) -> impl Iterator<Item=(&'a Arc<dyn A>, usize)> + 'a {
        let domain = url.domain().map(|d| d.to_ascii_lowercase());
        let checker = move |item: &'a Arc<dyn A>| {
			let yay = item.yay(url);
			yay.map(|i| (item, i))
		};
		let a = domain.and_then(|d| self.hash.get(d.as_str()).and_then(checker));
		let b = self.arr.iter().flat_map(checker);
		a.into_iter().chain(b)
    }
}

Wouldn't that say that the things given out by the iterator could rely on url in addition to self which isn't required here (the url is no longer needed once you've retrieved the items from the iterator)?

Admittedly my current use shouldn't require that distinction but it would be nice to know if there's a way for future reference...

'a + 'b means the returned value should be usable for the union of both lifetime regions, but the implementation returns a value which only be valid for the intersection of two lifetime regions.

Iterators are lazy, as long as you're not using an owned Url it must be borrowed (you could accept a Url instead). Same for self, which is required for self.arr and self.hash (items are retrieved lazily).

This works: Rust Playground

By using a new lifetime parameter and some lifetime constraints, you can keep the output of the iterator independent of url.

2 Likes

I guess what I was trying to say is if they shared the same lifetime this wouldn't work even though it really should

impl B {
    // Compiler thinks data from url is returned from this function
    // because of the shared lifetime in test
    // In reality, no data from url is actually returned so this should be okay
    fn test2(&self, url: &Url) -> Option<(&Arc<dyn A>, usize)> {
        self.test(url).next()
    }
}

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.