Unconstrained lifetime error when implementing Index trait - rust

I have a struct that owns a HashMap<String, String>,
struct Test {
data: HashMap<String, String>,
}
I am trying to implement the Index trait for this type to map to the Index implementation of the hashmap (there's other logic involved so I cannot expose the hashmap).
This works if I am just getting a reference to the value in the hashmap:
impl<'b> Index<&'b str> for Test {
type Output = String;
fn index(&self, k: &'b str) -> &String {
self.data.get(k).unwrap()
}
}
However, I want to get &Option<&String> out of it, like data.get(). So I tried this:
impl<'b, 'a> Index<&'b str> for Test {
type Output = Option<&'a String>;
fn index(&'a self, k: &'b str) -> &Option<&'a String> {
&self.data.get(k)
}
}
This results in:
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> <anon>:8:10
|
8 | impl<'b, 'a> Index<&'b str> for Test {
| ^^ unconstrained lifetime parameter
I understand the "unconstrained lifetime parameter in 'a". Now 'a is the lifetime of Test itself, so I want (I think) where 'Self: 'a (so self lives at least as long as 'a ) . I cannot seem to figure this out for Index impl? I tried some things with adding PhantomData to my Test. But I am not getting anywhere. Any suggestions?

As has been pointed out in the comments, you won't be able to do exactly what you want. But, what it seems like you really want is to replicate HashMap's get method. So I would suggest either writing your own, or implmenting Deref (and not DerefMut) to give the struct's owner immutable access directly to the internal HashMap. Hopefully that means the user can't mess up your struct's internal logic. Keep in mind that if you do both then Deref will not be used to called HashMap::get because Test::get will be available.
struct FooMap {
data: HashMap<String, String>
}
Replicating get:
impl FooMap {
pub fn get(&self, index: &str) -> Option<&String> { self.data.get(index) }
}
Using Deref:
impl Deref for FooMap {
type Target = HashMap<String, String>;
fn deref(&self) -> &Self::Target { &self.data }
}
Example code on Rust Playground

Related

How do I properly add lifetimes to an iterator containing other iterators in Rust?

I currently have code that looks kind of like this:
struct People {
names: Vec<String>,
ages: Vec<i32>,
}
impl People {
fn iter_people<'a>(&'a self) -> PeopleIterator<'a> {
return PeopleIterator {
names_iterator: Box::new(self.names.iter()),
ages: Box::new(self.ages.iter()),
};
}
}
struct PeopleIterator<'a> {
names_iterator: Box<dyn Iterator<Item = &'a String>>,
ages: Box<dyn Iterator<Item = &'a i32>>,
}
impl<'a> Iterator for PeopleIterator<'a> {
...snip...
}
I am aware that I should model a person as a struct Person and then have a Vec<Person> to model people but this is just a simplification of my actual code.
Anyway, the Rust compiler tells me this:
lifetime may not live long enough
requirement occurs because of the type PeopleIterator<'_>, which makes the generic argument '_ invariant
I have looked at the suggested link for subtyping and variance but I need to read it a few more times to actually understand it.
What stumps me is that I would expect both my iterators self.names.iter() and self.ages.iter() to live as long as self and I have declared that self should live as long as PeopleIterator. However, when I look at the iter() function, it does not make this constraint but instead has an anonymous lifetime '_. I am guessing this is the problem but I am confused and don't know how to fix it :(
The problem is the lifetime of the iterator itself in Box<dyn Iterator<Item = &'a String>> is by default bound to be 'static, but that's not possible for an iterator containing non static references like anything from &'a self. The solution is to specify an explicit lifetime bound:
struct PeopleIterator<'a> {
names_iterator: Box<dyn Iterator<Item = &'a String> + 'a>,
ages: Box<dyn Iterator<Item = &'a i32> + 'a>,
}
Personally I'd just use generics instead of static dispatch avoiding some indirection and the whole problem from the beginning:
impl People {
fn iter_people(&self) -> PeopleIterator<impl Iterator<Item = &String>, impl Iterator<Item = &i32>> {
return PeopleIterator {
names_iterator: self.names.iter(),
ages: self.ages.iter(),
};
}
}
struct PeopleIterator<N, A> {
names_iterator: N,
ages: A,
}

How can I create values that outlive 'static lifetime?

I want to implement Into trait for my struct.
'static is hinted by the compiler, I do not know why I have to add that.
I hope:
invoking into() consumes the RequestBody object. (which already did)
the references to str inside returned HashMap live until the end of scope where into() is invoked.
Below is the error message and code, how can I make it compile as I wish?
error: lifetime may not live long enough
--> src/portal_request.rs:35:9
|
26 | fn into(self) -> HashMap<&'static str, &'static str> {
| ---- has type `RequestBody<'1>`
...
35 | list.into_iter().collect()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
pub enum BodyEntry<'a> {
MenuId(&'a str),
//...
}
pub struct RequestBody<'a> {
pub entries: Vec<BodyEntry<'a>>,
}
use BodyEntry::*;
impl Into<HashMap<&str, &str>> for RequestBody<'_> {
fn into(self) -> HashMap<&'static str, &'static str> {
let mut list = Vec::new();
for entry in self.entries {
let entry_literal = match entry {
MenuId(val) => ("_menuId", val),
//...
_ => ("else", "else"),
};
list.push(entry_literal);
}
list.into_iter().collect()
}
}
You need to declare the lifetimes like so:
impl<'a> Into<HashMap<&'static str, &'a str>> for RequestBody<'a> {
fn into(self) -> HashMap<&'static str, &'a str> {
Or better:
impl<'keys, 'a> Into<HashMap<&'keys str, &'a str>> for RequestBody<'a> {
fn into(self) -> HashMap<&'keys str, &'a str> {
You can't. You can't create new 'static string at runtime. Your keys are fine, because they're all determined at compile-time (at least in this minimal example; if your real example needs dynamically-generated ones then the same caveat applies). But you want to consume the BodyEntry and create a HashMap which owns the strings as values. An owned string is not a &str. An owned string is a String.
Consider having your Into implementation return HashMap<&str, String> (again, you might have to change the key type to String as well, if your real use case demands it). Then, inside your match, consider
let entry_literal = match entry {
MenuId(val) => ("_menuId", String::from(val)),
//...
_ => ("else", String::from("else")),
};
String::from copies the underlying &str string slice into a new owned String.

Rust - implementing trait for Deref: the parameter type `T` may not live long enough

I have a trait:
trait Foo {
fn bar(&self) -> Cow<str>;
}
And I want to implement it for any type that implements Deref with a target of a type that implements Foo. Basically:
impl<T: Foo, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(std::ops::Deref::deref(self))
}
}
Unfortunately, this gives the error the parameter type T may not live long enough.
My understanding is that T could have a reference within it that has a short lifetime, and the lifetime bound of the return value of Cow<str> is linked to the lifetime of &self due to lifetime elision, which would cause problems.
I'm not sure how I can fix this, since I'm not able to bound any of the lifetimes in bar. I can try to make sure T lives as long as &self, but this doesn't work.
impl<'a, T: Foo + 'a, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&'a self) -> Cow<'a, str> {
<T as Foo>::bar(std::ops::Deref::deref(self))
}
}
I get the error method not compatible with trait since the lifetimes don't match the trait defenition anymore. I've tried all sorts of different ways of adding lifetime bounds and I always get one of those two errors.
I am able to implement Foo for a specific type that implements Deref:
impl<T: Foo> Foo for Box<T> {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(self)
}
}
I'm not sure why that works but the original example doesn't.
The Box version works because of the deref coercion the compiler will do when it sees a reference and expects a different reference.
You can use the same mechanic when using a generic implementor of Deref to ensure that it Derefs to an owned type you can simply add a 'static lifetime bound on T like this:
impl<T: Foo + 'static, D: std::ops::Deref<Target = T>> Foo for D {
fn bar(&self) -> Cow<str> {
<T as Foo>::bar(self)
}
}
playground
Note: there is rarely a need to call methods of std::ops traits directly, they're all just the methods behind Rusts operators, deref for example is the method behind unary *
Update:
Since there is an additional requirement that T might not be static we have to thread through the lifetime like you tried in your second example, like the error you're getting suggest you have to adjust the trait to take a lifetime as well:
use std::borrow::Cow;
trait Foo<'a> {
fn bar(&self) -> Cow<'a, str>;
}
impl<'a, T: Foo<'a>, D: std::ops::Deref<Target = T>> Foo<'a> for D {
fn bar(&self) -> Cow<'a, str> {
<T as Foo>::bar(self)
}
}
struct S<'a> {
val: &'a str,
}
impl<'a> Foo<'a> for S<'a> {
fn bar(&self) -> Cow<'a, str> {
todo!()
}
}
fn main() {
let val = String::from("test");
let s = S { val: &val }; // error: `val` does not live long enough
let b = Box::new(s);
let cow = Foo::bar(&b); // argument requires that `val` is borrowed for `'static`
}

Generic parameter with reference used as function pointer argument

I am having trouble figuring out what lifetime parameter will work for this, so my current workarounds include transmutes or raw pointers. I have a structure holding a function pointer with a generic as a parameter:
struct CB<Data> {
cb: fn(Data) -> usize
}
I would like to store an instance of that, parameterized by some type containing a reference, in some other structure that implements a trait with one method, and use that trait method to call the function pointer in CB.
struct Holder<'a> {
c: CB<Option<&'a usize>>
}
trait Exec {
fn exec(&self, v: &usize) -> usize;
}
impl<'a> Holder<'a> {
fn exec_aux(&self, v: &'a usize) -> usize {
(self.c.cb)(Some(v))
}
}
impl<'a> Exec for Holder<'a> {
fn exec(&self, v: &usize) -> usize
{
self.exec_aux(v)
}
}
This gives me a lifetime error for the 'Exec' impl of Holder:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
Simply calling exec_aux works fine as long as I don't define that Exec impl:
fn main() {
let h = Holder { c: CB{cb:cbf}};
let v = 12;
println!("{}", h.exec_aux(&v));
}
Also, making CB not generic also makes this work:
struct CB {
cb: fn(Option<&usize>) -> usize
}
The parameter in my actual code is not a usize but something big that I would rather not copy.
The lifetimes in your Exec trait are implicitly this:
trait Exec {
fn exec<'s, 'a>(&'s self, v: &'a usize) -> usize;
}
In other words, types that implement Exec need to accept any lifetimes 's and 'a. However, your Holder::exec_aux method expects a specific lifetime 'a that's tied to the lifetime parameter of the Holder type.
To make this work, you need to add 'a as a lifetime parameter to the Exec trait instead, so that you can implement the trait specifically for that lifetime:
trait Exec<'a> {
// ^^^^ vv
fn exec(&self, v: &'a usize) -> usize;
}
impl<'a> Exec<'a> for Holder<'a> {
// ^^^^ vv
fn exec(&self, v: &'a usize) -> usize
{
self.exec_aux(v)
}
}
The problem here is that the Exec trait is too generic to be used in this way by Holder. First, consider the definition:
trait Exec {
fn exec(&self, v: &usize) -> usize;
}
This definition will cause the compiler to automatically assign two anonymous lifetimes for &self and &v in exec. It's basically the same as
fn exec<'a, 'b>(&'a self, v: &'b usize) -> usize;
Note that there is no restriction on who needs to outlive whom, the references just need to be alive for the duration of the method call.
Now consider the definition
impl<'a> Holder<'a> {
fn exec_aux(&self, v: &'a usize) -> usize {
// ... doesn't matter
}
}
Since we know that &self is a &Holder<'a> (this is what the impl refers to), we need to have at least a &'a Holder<'a> here, because &'_ self can't have a lifetime shorter than 'a in Holder<'a>. So this is saying that the two parameters have the same lifetime: &'a self, &'a usize.
Where it all goes wrong is when you try to combine the two. The trait forces you into the following signature, which (again) has two distinct implicit lifetimes. But the actual Holder which you then try to call a method on forces you to have the same lifetimes for &self and &v.
fn exec(&self, v: &usize) -> usize {
// Holder<'a> needs `v` to be `'a` when calling exec_aux
// But the trait doesn't say so.
self.exec_aux(v)
}
One solution is to redefine the trait as
trait Exec<'a> {
fn exec(&'a self, v: &'a usize) -> usize;
}
and then implement it as
impl<'a> Exec<'a> for Holder<'a> {
fn exec(&'a self, v: &'a usize) -> usize {
self.exec_aux(v)
}
}

How do you create a generic function in Rust with a trait requiring a lifetime?

I am trying to write a trait which works with a database and represents something which can be stored. To do this, the trait inherits from others, which includes the serde::Deserialize trait.
trait Storable<'de>: Serialize + Deserialize<'de> {
fn global_id() -> &'static [u8];
fn instance_id(&self) -> Vec<u8>;
}
struct Example {
a: u8,
b: u8
}
impl<'de> Storable<'de> for Example {
fn global_id() -> &'static [u8] { b"p" }
fn instance_id(&self) -> Vec<u8> { vec![self.a, self.b] }
}
Next, I am trying to write this data using a generic function:
pub fn put<'de, S: Storable>(&mut self, obj: &'de S) -> Result<(), String> {
...
let value = bincode::serialize(obj, bincode::Infinite);
...
db.put(key, value).map_err(|e| e.to_string())
}
However, I am getting the following error:
error[E0106]: missing lifetime specifier
--> src/database.rs:180:24
|
180 | pub fn put<'de, S: Storable>(&mut self, obj: &'de S) -> Result<(), String> {
| ^^^^^^^^ expected lifetime parameter
Minimal example on the playground.
How would I resolve this, possibly avoid it altogether?
You have defined Storable with a generic parameter, in this case a lifetime. That means that the generic parameter has to be propagated throughout the entire application:
fn put<'de, S: Storable<'de>>(obj: &'de S) -> Result<(), String> { /* ... */ }
You can also decide to make the generic specific. That can be done with a concrete type or lifetime (e.g. 'static), or by putting it behind a trait object.
Serde also has a comprehensive page about deserializer lifetimes. It mentions that you can choose to use DeserializeOwned as well.
trait Storable: Serialize + DeserializeOwned { /* ... */ }
You can use the same concept as DeserializeOwned for your own trait as well:
trait StorableOwned: for<'de> Storable<'de> { }
fn put<'de, S: StorableOwned>(obj: &'de S) -> Result<(), String> {
You have the 'de lifetime in the wrong place -- you need it to specify the argument to Storable, not the lifetime of the reference obj.
Instead of
fn to_json<'de, S: Storable>(obj: &'de S) -> String {
use
fn to_json<'de, S: Storable<'de>>(obj: &S) -> String {
Playground.
The lifetime of obj doesn't actually matter here, because you're not returning any values derived from it. All you need to prove is that S implements Storable<'de> for some lifetime 'de.
If you want to eliminate the 'de altogether, you should use DeserializeOwned, as the other answer describes.

Resources