First, one suggestion: Since a boxed iterator is also an iterator, you can change your lookup function to

fn lookup<'b, I: Iterator<Item = Key<'b>>>(&self, mut iter: I) -> bool { match iter.next() { Some(key) => match self.children.get(&key) { Some(node) => node.lookup(iter), None => false }, None => true } }

which is a bit more general. But the problem still persists. You're trying to pass a &Key<'b> in self.children.get(&key) to the HashMap which actually expects a &Q where Q implements BorrowFrom<Key<'a>> . The compiler's suggestion is now to replace 'b with 'a like this:

fn lookup<I: Iterator<Item = Key<'a>>>(&self, mut iter: I) -> bool { //' match iter.next() { Some(key) => match self.children.get(&key) { Some(node) => node.lookup(iter), None => false }, None => true } }

which surely will make the compiler happy. But this is not really what you want! It would unnecessarily restrict the set of string slices you could use as parameters for your lookup. This way, you can only use string slices which refer to memory that is at least as long-lived as the scope that 'a is referring to. But for a lookup this restriction is not actually needed.

The solution is to completely get rid of any lifetime parameters in the type parameter Q of the HashMap's get function. Instead of using Q=Key<'something> , we can actually use Q=str . We just need to add the following BorrowFrom implementation

impl<'a> BorrowFrom<Key<'a>> for str { fn borrow_from<'s>(owned: &'s Key<'a>) -> &'s str { owned.v } }

and make the Key type public (since it's used as parameter in a public trait). The lookup function that worked for me looks like this:

fn lookup_iter<'b, I: Iterator<Item = Key<'b>>>(&self, mut i: I) -> bool { if let Some(key) = i.next() { match self.children.get(key.v) { Some(node_box_ref) => node_box_ref.lookup_iter(i), None => false } } else { true } }

And if we piece everything together, we get

#![feature(core)] #![feature(hash)] #![feature(std_misc)] #![feature(collections)] use std::collections::HashMap; use std::collections::hash_map::Entry::{ Occupied, Vacant }; use std::borrow::BorrowFrom; #[derive(PartialEq, Eq, Hash, Clone)] pub struct Key<'a> { v: &'a str } impl<'a> BorrowFrom<Key<'a>> for str { fn borrow_from<'s>(owned: &'s Key<'a>) -> &'s str { owned.v } } fn str_to_key(s: &str) -> Key { Key { v: s } } struct Node<'a> { children: HashMap<Key<'a>, Box<Node<'a>>> } impl<'a> Node<'a> { fn add_str(&mut self, s: &'a str) { self.add_iter(s.split('.').map(str_to_key)) } fn add_iter<I>(&mut self, mut i: I) where I: Iterator<Item = Key<'a>> { //' if let Some(key) = i.next() { let noderef = match self.children.entry(key) { Vacant(e) => { let n = Node { children: HashMap::new() }; e.insert(Box::new(n)) } Occupied(e) => { e.into_mut() } }; noderef.add_iter(i); } } fn lookup_str(&self, s: &str) -> bool { self.lookup_iter(s.split('.').map(str_to_key)) } fn lookup_iter<'b, I>(&self, mut i: I) -> bool where I: Iterator<Item = Key<'b>> { if let Some(key) = i.next() { match self.children.get(key.v) { Some(node_box_ref) => node_box_ref.lookup_iter(i), None => false } } else { true } } } fn main() { let mut node: Node<'static> = Node { children: HashMap::new() }; //' node.add_str("one.two.three"); { // <-- "inner scope" let s = String::from_str("one.two.three"); println!("lookup: {:?}", node.lookup_str(&*s)); } println!("The End"); }

As you can see, I deliberately made node a Node<'static> , so the node's lifetime parameter 'a actually refers to the lifetime of the whole program. It's OK in this example because the only string slice it will store is a string literal. Note that for the lookup I created a short-lived String object. So, the lifetime parameter 'b in node.lookup_str will refer to the "inner scope" which is obviously shorter than 'a='static . And it all works out! :)

Oh, I also got rid of the iterator boxing.