How to return an iterator over the keys of a HashMap from a trait implementation? - rust

I'm trying to build a simple graph library in Rust. There is a trait Graph that any graph must implement. This trait has only one function at the moment, nodes, which allows iteration of the graph's nodes using a for-in loop.
An implementation of Graph, MapGraph, is a lightweight wrapper around a HashMap. MapGraph must implement the Graph trait method nodes. I'm having problems getting this to work.
Here's the code for Graph:
pub trait Graph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}
And here's the code for MapGraph:
use std::collections::HashMap;
use crate::rep::Graph;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item=&N>> {
let keys = self.map.keys();
Box::new(keys)
}
}
The compiler gives this error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:19:29
|
19 | let keys = self.map.keys();
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn nodes(&self) -> Box<dyn Iterator<Item = &N>> {
19 | | let keys = self.map.keys();
20 | |
21 | | Box::new(keys)
22 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:19:20
|
19 | let keys = self.map.keys();
| ^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
I've found other references to this error, but those cases don't seem to look like the one I have here.
I'm using Box because the Graph trait has a function that itself returns a trait. What is the correct way to return an Iterator (or any other trait)? gives this approach as one option, and I haven't been able to implement any of the the others. If there's another way to do this, that would be fine.
What are my options for resolving this specific problem?

It works if you explicitly specify that the trait object (dyn Iterator) that you are returning contains references that are tied to the lifetime of self.
Without adding this bound, the compiler cannot infer from the function signature that the iterator cannot be used after self is moved or destroyed. Because the compiler cannot infer this, it cannot safely use self.map.keys() in the function's output.
Working example with this bound added:
pub trait Graph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a>;
}
use std::collections::HashMap;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>,
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a> {
let keys = self.map.keys();
Box::new(keys)
}
}
Playground
I had thought that a bound of Item = &'a N would also be required, but I guess that's already covered by the "+ 'a"...
N.B. that to make sense of an error like:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
you have to understand that the compiler, for ergonomic reasons, automatically adds a + 'static lifetime qualifier to any unqualified trait object. This means that an unqualified Box<dyn MyTrait> is transformed by the compiler into a Box<(dyn MyTrait + 'static)>, which in turn means that the object cannot contain any references except those that last for the lifetime of the entire program.
With this in mind you can see why self.map.keys() does not fit this strict bound, and a more specific explicit bound is required.

Related

Shared &str along multiple structs conflicts with Lifetimes

I have the following code:
pub trait Regex: RegexClone {
fn check(&self) -> Result<u32,(/* errors should detail where it fails*/)>;
fn next(&self) -> Option<Box<dyn Regex>>;
}
pub trait RegexClone {
fn regex_clone(&self) -> Box<dyn Regex>;
}
pub struct PatternAnyCharacter<'a>{
string: &'a str
}
impl RegexClone for PatternAnyCharacter<'_> {
fn regex_clone(&self) -> Box<dyn Regex> {
return Box::new(PatternAnyCharacter {string: self.string})
}
}
impl Regex for PatternAnyCharacter<'_> {
fn check(&self) -> Result<u32, ()> {
if self.string.len() > 0 {
return Ok(1);
}
Err(())
}
fn next(&self) -> Option<Box<dyn Regex>> {
None
}
}
The idea is that when i call regex_clone i get a new Box<dyn Regex> with the same &str as member, i supossed that since im only using inmutable references when calling regex_clone it would give me a new struct with the same string slice, since is a reference im not moving anything, however the compiler complains the following:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/lib.rs:63:25
|
63 | return Box::new(PatternAnyCharacter {string: self.string})
| ^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
--> src/lib.rs:61:41
|
61 | impl RegexClone for PatternAnyCharacter<'_> {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:63:54
|
63 | return Box::new(PatternAnyCharacter {string: self.string})
| ^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
--> src/lib.rs:63:16
|
63 | return Box::new(PatternAnyCharacter {string: self.string})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `Box<(dyn Regex + 'static)>`
found `Box<dyn Regex>`
How can i solve this so i can share the same string slice with multiple struct?
i thought about foregoing the string slice as member entirely and passing it as parameter to check, but hopefully i can avoid it.
You need to define at the trait that the returned dyn Regex can't outlive &self if you want to allow it to borrow from parts of &self.:
pub trait RegexClone {
fn regex_clone<'a>(&'a self) -> Box<dyn Regex + 'a>;
}
(You can also use an anonymous lifetime (Box<dyn Regex + '_>), but this is easier to understand.)
Side note: I don't think "clone" is the right name for such a function.

What lifetime to use with Iterator for a hierarchy of `Rc`s?

I've got a Rust struct that represents a hierarchical data structure and each struct contains a Vec<Rc<Self>> that creates the hierarchical structure. I want to create an iterator that iterates over Rcs to every member of the hierarchy, starting with a given node. The Node struct has a lifetime parameter 'a because the entire hierarchy refers some &'a strs from a text document that the whole hierarchy is based on (contained inside NodeDetail<'a>).
pub struct Node<'a> {
pub detail: NodeDetail<'a>,
pub children: Vec<Rc<Self>>,
}
impl<'a> Node<'a> {
// Returns an iterator over `Rc`s to all the children
pub fn iter(&self) -> Box<(dyn Iterator<Item=Rc<Self>> + 'static)> {
let mut ans:Box<(dyn Iterator<Item=Rc<Self>> + 'static)> = Box::new(std::iter::empty());
for c in self.children.iter() {
ans = Box::new(ans.chain(std::iter::once(c.clone())));
ans = Box::new(ans.chain(c.iter()));
}
ans
}
}
With this attempt, I'm getting this error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/model/updm/impl_common.rs:23:40
|
23 | ans = Box::new(ans.chain(c.iter()));
| ^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 5:6...
--> src/model/updm/impl_common.rs:5:6
|
5 | impl<'a> super::UpdmCommon<'a> {
| ^^
note: ...so that the types are compatible
--> src/model/updm/impl_common.rs:23:40
|
23 | ans = Box::new(ans.chain(c.iter()));
| ^^^^
= note: expected `&UpdmCommon<'_>`
found `&UpdmCommon<'a>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/model/updm/impl_common.rs:23:19
|
23 | ans = Box::new(ans.chain(c.iter()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `Box<(dyn Iterator<Item = Rc<UpdmCommon<'a>>> + 'static)>`
found `Box<dyn Iterator<Item = Rc<UpdmCommon<'a>>>>`
I think it's somehow confusing the lifetime that the Node struct is generic over with a lifetime specific to the iterator. I'm cloning the Rcs so the Item of the iterator is owned and has a 'static lifetime. I think the iterator itself should also be owned, which would make it 'static. I first tried Box<dyn Iterator<Item=Rc<Self>>>, then added the 'static annotation to try to fix the first errors I got. Anyone know how to fix this?
You need to tie the lifetime of the value you're returning from iter() to &self and you need to cast the boxed iterator to a trait object.
use std::rc::Rc;
pub struct NodeDetail<'a>(&'a str);
pub struct Node<'a> {
pub detail: NodeDetail<'a>,
pub children: Vec<Rc<Self>>,
}
impl<'a> Node<'a> {
// Returns an iterator over `Rc`s to all the children
pub fn iter(&'a self) -> Box<(dyn Iterator<Item=Rc<Self>> + 'a)> {
let mut ans = Box::new(std::iter::empty()) as Box<dyn Iterator<Item=Rc<Self>>>;
for c in self.children.iter() {
ans = Box::new(ans.chain(std::iter::once(c.clone())));
ans = Box::new(ans.chain(c.iter()));
}
ans
}
}
I figured it out. I do my best troubleshooting immediately after I ask for help. Kind of like how I do my best email proofreading after pressing send.
It turns out that I needed Box<(dyn Iterator<Item=Rc<Self>> + 'a)> which makes sense now that I think about it because the iterator can't outlive the original &'a strs that the whole hierarchy depends on.

How to get a reference to a struct with lifetime annotations from a trait object? [duplicate]

I ran into a lifetime problem with a little game. The below code represents a very boiled down version of the update loop.
I need the container mutable reference to get references to other game objects or to create new ones or trigger a functionality.
For that reason, I need the Any trait to be able to cast the trait to a struct, so in my GameObj trait I added an as_any method, but this resulted in a lifetime issue.
use std::any::Any;
trait GameObj<'a> {
fn as_any<'b>(&'b self) -> &'b (dyn Any + 'a);
fn update(&mut self, cont: &mut container);
}
struct object<'a> {
content: &'a String,
}
impl<'a> GameObj<'a> for object<'a> {
fn as_any<'b>(&'b self) -> &'b (dyn Any + 'a) {
return self;
}
fn update(&mut self, cont: &mut container) {
let val = cont.get_obj().unwrap();
let any = val.as_any();
}
}
struct container<'a> {
data: Vec<Box<dyn GameObj<'a> + 'a>>,
}
impl<'a> container<'a> {
fn get_obj<'b>(&'b self) -> Option<&'b Box<dyn GameObj<'a> + 'a>> {
return Some(&self.data[0]);
}
}
pub fn main() {
let a = String::from("hallo");
let b = String::from("asdf");
{
let abc = object { content: &a };
let def = object { content: &b };
let mut cont = container { data: Vec::new() };
cont.data.push(Box::new(abc));
cont.data.push(Box::new(def));
loop {
for i in 0..cont.data.len() {
let mut obj = cont.data.remove(0);
obj.update(&mut cont);
cont.data.insert(i, obj);
}
}
}
}
playground
When I try to build the code, it results in the following error message.
If I comment out/delete let any = val.as_any(); in the update function it compiles fine.
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/main.rs:18:24
|
18 | let val = cont.get_obj().unwrap();
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the method body at 17:5...
--> src/main.rs:17:5
|
17 | / fn update(&mut self, cont: &mut container) {
18 | | let val = cont.get_obj().unwrap();
19 | | let any = val.as_any();
20 | | }
| |_____^
= note: ...so that the types are compatible:
expected &container<'_>
found &container<'_>
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the declared lifetime parameter bounds are satisfied
--> src/main.rs:19:23
|
19 | let any = val.as_any();
| ^^^^^^
How I can make this work without using 'static, or why is this impossible?
Any is declared trait Any: 'static and can only store 'static types. So in order to make dyn Any + 'a a well-formed type, your as_any method was given an implicit 'a: 'static bound, leading to the lifetime error you showed.
If not for this restriction, you would be able to break safety by putting in an 'a type into an Any and getting out a 'static type, because TypeId can’t tell the difference—lifetimes are erased during compilation. See the discussion on RFC 1849 for more information.
You should think more carefully about why you want to use Any. It’s almost never what you actually want. Perhaps something as simple as an enum type of all the different object types you might want to store would satisfy your use case better?
If you really want to use Any, then you’ll need to find a way to make your types 'static. Rc (or Arc, if threads are involved) is often helpful for this purpose; for example, you could have your object store Rc<String> (or better, Rc<str>) instead of &'a String.

Lifetime issue when using the Any trait to get references to structs containing references

I ran into a lifetime problem with a little game. The below code represents a very boiled down version of the update loop.
I need the container mutable reference to get references to other game objects or to create new ones or trigger a functionality.
For that reason, I need the Any trait to be able to cast the trait to a struct, so in my GameObj trait I added an as_any method, but this resulted in a lifetime issue.
use std::any::Any;
trait GameObj<'a> {
fn as_any<'b>(&'b self) -> &'b (dyn Any + 'a);
fn update(&mut self, cont: &mut container);
}
struct object<'a> {
content: &'a String,
}
impl<'a> GameObj<'a> for object<'a> {
fn as_any<'b>(&'b self) -> &'b (dyn Any + 'a) {
return self;
}
fn update(&mut self, cont: &mut container) {
let val = cont.get_obj().unwrap();
let any = val.as_any();
}
}
struct container<'a> {
data: Vec<Box<dyn GameObj<'a> + 'a>>,
}
impl<'a> container<'a> {
fn get_obj<'b>(&'b self) -> Option<&'b Box<dyn GameObj<'a> + 'a>> {
return Some(&self.data[0]);
}
}
pub fn main() {
let a = String::from("hallo");
let b = String::from("asdf");
{
let abc = object { content: &a };
let def = object { content: &b };
let mut cont = container { data: Vec::new() };
cont.data.push(Box::new(abc));
cont.data.push(Box::new(def));
loop {
for i in 0..cont.data.len() {
let mut obj = cont.data.remove(0);
obj.update(&mut cont);
cont.data.insert(i, obj);
}
}
}
}
playground
When I try to build the code, it results in the following error message.
If I comment out/delete let any = val.as_any(); in the update function it compiles fine.
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/main.rs:18:24
|
18 | let val = cont.get_obj().unwrap();
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the method body at 17:5...
--> src/main.rs:17:5
|
17 | / fn update(&mut self, cont: &mut container) {
18 | | let val = cont.get_obj().unwrap();
19 | | let any = val.as_any();
20 | | }
| |_____^
= note: ...so that the types are compatible:
expected &container<'_>
found &container<'_>
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the declared lifetime parameter bounds are satisfied
--> src/main.rs:19:23
|
19 | let any = val.as_any();
| ^^^^^^
How I can make this work without using 'static, or why is this impossible?
Any is declared trait Any: 'static and can only store 'static types. So in order to make dyn Any + 'a a well-formed type, your as_any method was given an implicit 'a: 'static bound, leading to the lifetime error you showed.
If not for this restriction, you would be able to break safety by putting in an 'a type into an Any and getting out a 'static type, because TypeId can’t tell the difference—lifetimes are erased during compilation. See the discussion on RFC 1849 for more information.
You should think more carefully about why you want to use Any. It’s almost never what you actually want. Perhaps something as simple as an enum type of all the different object types you might want to store would satisfy your use case better?
If you really want to use Any, then you’ll need to find a way to make your types 'static. Rc (or Arc, if threads are involved) is often helpful for this purpose; for example, you could have your object store Rc<String> (or better, Rc<str>) instead of &'a String.

Cannot infer an appropriate lifetime for lifetime parameter cloning trait object

The duplicates of this question don't seem to solve things for me. The following code gives me errors:
use std::collections::HashMap;
use std::thread;
pub trait Spider : Sync + Send {
fn add_request_headers(&self, headers: &mut Vec<String>);
}
pub struct Google {}
impl Spider for Google {
fn add_request_headers(&self, headers: &mut Vec<String>) {
headers.push("Hello".to_string())
}
}
fn parallel_get(spiders: &HashMap<String, Box<Spider>>) -> std::thread::JoinHandle<()> {
let thread_spiders = spiders.clone();
thread::spawn(move || {
let headers = &mut vec![];
let spider = thread_spiders.get("Google").unwrap();
spider.add_request_headers(headers);
})
}
fn main() {
let spiders = HashMap::new();
let spider = Box::new(Google{});
spiders.insert("Google", spider);
}
Run on the playground here.
I get:
rustc 1.14.0 (e8a012324 2016-12-16)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> <anon>:18:34
|
18 | let thread_spiders = spiders.clone();
| ^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 17:87...
--> <anon>:17:88
|
17 | fn parallel_get(spiders: &HashMap<String, Box<Spider>>) -> std::thread::JoinHandle<()> {
| ^
note: ...so that types are compatible (expected &&std::collections::HashMap<std::string::String, Box<Spider>>, found &&std::collections::HashMap<std::string::String, Box<Spider + 'static>>)
--> <anon>:18:34
|
18 | let thread_spiders = spiders.clone();
| ^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#<anon>:19:19: 23:6 thread_spiders:&std::collections::HashMap<std::string::String, Box<Spider>>]` will meet its required lifetime bounds
--> <anon>:19:5
|
19 | thread::spawn(move || {
| ^^^^^^^^^^^^^
I think it's telling me that it can't automatically infer the lifetime of thread_spiders because it needs to be 'static to live long enough for the thread, but it can't outlive 'a which is the lifetime of the input parameter.
The thing is, I can clone other objects in parallel_get and they get moved into the new thread without issue. But for some reason thread_spiders seems to trip up the compiler. It should have a lifetime of 'a if I'm correct and then get moved into the thread closure.
I've tried adding explicit lifetime parameters to parallel_get but haven't been able to get anything working. How can I make this code compile?
The error message is quite confusing in this case, but notice a double ampersand here:
(expected &&std::collections::HashMap<std::string::String, Box<Spider>>, found &&std::collections::HashMap<std::string::String, Box<Spider + 'static>>).
It looks like it tries to clone the reference. I assume you wanted to clone the entire HashMap. Calling clone explicitly as Clone::clone(spiders) gives much clearer error message:
error[E0277]: the trait bound `Spider: std::clone::Clone` is not satisfied
error[E0277]: the trait bound `Spider: std::marker::Sized` is not satisfied
--> error_orig.rs:19:26
|
19 | let thread_spiders = Clone::clone(spiders);
| ^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `Spider`
|
= note: `Spider` does not have a constant size known at compile-time
= note: required because of the requirements on the impl of `std::clone::Clone` for `Box<Spider>`
= note: required because of the requirements on the impl of `std::clone::Clone` for `std::collections::HashMap<std::string::String, Box<Spider>>`
= note: required by `std::clone::Clone::clone`
You are calling Clone::clone on Box<Spider>, an owned trait object.
How do I clone a HashMap containing a boxed trait object? illustrates that it can be implemented by introducing a cloning method to your trait, like so:
pub trait Spider: Sync + Send {
fn add_request_headers(&self, headers: &mut Vec<String>);
fn clone_into_box(&self) -> Box<Spider>;
}
impl Clone for Box<Spider> {
fn clone(&self) -> Self {
self.clone_into_box()
}
}
#[derive(Clone)]
pub struct Google {}
impl Spider for Google {
fn add_request_headers(&self, headers: &mut Vec<String>) {
headers.push("Hello".to_string())
}
fn clone_into_box(&self) -> Box<Spider> {
Box::new(self.clone())
}
}
How to clone a struct storing a trait object? suggests creating a separate trait for this polymorphic cloning method.

Resources