How can I create values that outlive 'static lifetime? - rust

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.

Related

Borrow Checker Not Releasing Immutable Borrow

I'm having a problem in the code below where the borrow checker complains about an immutable borrow in the call to self.entries.push() despite placing all the temporary code inside its own scope.
I have checked other posts with similar problems, but I can't figure out how to adapt the code to my own situation -- I'm still pretty new to Rust. Suggestions?
impl Entry {
pub fn chain(&self, spair: &SigningPair, expires: u16)
-> Result<(Entry, HashMap<&str,CryptoString>), MensagoError> {
// New Entry and HashMap allocated in here to be returned
}
}
pub struct Keycard {
_type: EntryType,
pub entries: Vec<Entry>,
}
impl Keycard {
pub fn get_current(&self) -> Option<&Entry> {
// ...
}
pub fn chain(&mut self, spair: &SigningPair, expires: u16)
-> Result<HashMap<&str,CryptoString>, MensagoError> {
let (newentry, keys) = {
let entry = match self.get_current() {
Some(v) => v,
None => { return Err(MensagoError::ErrEmptyData) }
};
match entry.get_field("Type")?.as_str() {
"Organization" | "User" => (),
_ => { return Err(MensagoError::ErrInvalidKeycard) }
}
entry.chain(spair, expires)?
};
self.entries.push(newentry);
Ok(keys)
}
}
I'm fairly certain that the error is because the returnvalue of chain() is incorrect:
HashMap<&str,CryptoString> should be HashMap<&'static str,CryptoString> or HashMap<String,CryptoString>
The explanation is a little bit longer, though.
Rust borrow checker demands that a value can be:
borrowed immutably by many borrowers or
borrowed mutably by exactly one borrower
no other borrows can exist (mutable or immutable) when a value gets modified
You try to modify the value self.entry at self.entries.push(newentry). Therefore, there must not be any borrows that reference self.entry.
Sadly, a borrow chain exists that borrows self, which indirectly also borrows self.entry at that point in time:
the type of newentry contains a reference, &str. newentry got created in .chain(), where &str has no lifetime annotations and therefore has the same lifetime as entry.
entry is created by self.get_current(), where again, &Entry is a reference with no explicit lifetime annotation and therefore has the same lifetime as self.
Which means through the chain newentry -> entry -> self, the self object is still borrowed while you try to call self.entries.push(). This is exactly what the error message is trying to tell you.
There are several solutions to those problems usually:
Introduce Rc instead of references
.clone() somewhere in between to break the reference chain
make sure that the reference chain actually makes sense in the first place, and if not, introduce lifetimes appropriately
In your case I think it's solution #3, as there is no reason why newentry should borrow entry. The type &str is most likely incorrect and should be &'static str. In my experience, using &str as a key for HashMap doesn't make much sense, it should be either &'static str (= global constant string like "hello") or the owned, mutable version String. Using a temporary reference as a key is quite strange and therefore most likely a beginner error.
The fixed version is:
use std::collections::HashMap;
pub struct Entry;
pub struct SigningPair;
pub struct CryptoString;
pub enum MensagoError {
ErrEmptyData,
ErrInvalidKeycard,
}
pub struct EntryType;
impl Entry {
pub fn chain(
&self,
_: &SigningPair,
_: u16,
) -> Result<(Entry, HashMap<&'static str, CryptoString>), MensagoError> {
todo!()
}
fn get_field(&self, _: &str) -> Result<String, MensagoError> {
todo!()
}
}
pub struct Keycard {
_type: EntryType,
pub entries: Vec<Entry>,
}
impl Keycard {
pub fn get_current(&self) -> Option<&Entry> {
todo!()
}
pub fn chain(
&mut self,
spair: &SigningPair,
expires: u16,
) -> Result<HashMap<&str, CryptoString>, MensagoError> {
let (newentry, keys) = {
let entry = match self.get_current() {
Some(v) => v,
None => return Err(MensagoError::ErrEmptyData),
};
match entry.get_field("Type")?.as_str() {
"Organization" | "User" => (),
_ => return Err(MensagoError::ErrInvalidKeycard),
}
entry.chain(spair, expires)?
};
self.entries.push(newentry);
Ok(keys)
}
}

Why do Rust lifetimes matter when I move values into a spawned Tokio task?

I'm trying to create a struct that will manage a Tokio task with one tokio::sync::mpsc::Sender that sends input to the task, one tokio::sync::mpsc::Receiver that receives output from the task, and a handle that I can join at the end.
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
// A type that implements BlockFunctionality consumes instances of T and
// produces either Ok(Some(U)) if an output is ready, Ok(None) if an output
// is not ready, or an Err(_) if the operation fails
pub trait BlockFunctionality<T, U> {
fn apply(&mut self, input: T) -> Result<Option<U>, &'static str>;
}
pub struct Block<T, U> {
pub tx_input: mpsc::Sender<T>,
pub rx_output: mpsc::Receiver<U>,
pub handle: JoinHandle<Result<(), &'static str>>,
}
impl<T: Send, U: Send> Block<T, U> {
pub fn from<B: BlockFunctionality<T, U> + Send>(b: B) -> Self {
let (tx_input, mut rx_input) = mpsc::channel(10);
let (mut tx_output, rx_output) = mpsc::channel(10);
let handle: JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
let mut owned_b = b;
while let Some(t) = rx_input.recv().await {
match owned_b.apply(t)? {
Some(u) => tx_output
.send(u)
.await
.map_err(|_| "Unable to send output")?,
None => (),
}
}
Ok(())
});
Block {
tx_input,
rx_output,
handle,
}
}
}
When I try to compile this, I get this error for B and a similar one for the other two type parameters:
|
22 | pub fn from<B: BlockFunctionality<T, U> + Send>(b:B) -> Self {
| -- help: consider adding an explicit lifetime bound...: `B: 'static +`
...
27 | let handle:JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
| ^^^^^^^^^^^^ ...so that the type `impl std::future::Future` will meet its required lifetime bounds
I'm having a hard time understanding where the problem is with lifetimes. The way I understand it, lifetime problems usually come from references that don't live long enough, but I'm moving values, not using references. I move b, rx_input, and tx_output into the closure and I keep tx_input, rx_output, and handle in the calling scope. Does anyone know how to satisfy the compiler in this case?
Those values may be references or contain references. Reference types are valid types: B could be &'a str. Or B could be SomeType<'a>, a type with a lifetime parameter, that itself contains a &'a str.
To say that B: 'static means that all lifetime parameters of B outlive 'static (ref). For example, types which own their own data and thus have no lifetime parameters (e.g. String) satisfy this bound. But &'static str also satisfies the bound.
Because tokio::spawn creates something whose lifetime is not statically scoped, it requires a 'static argument.
So to satisfy the compiler, add the 'static bounds:
impl<T: 'static + Send, U: 'static + Send> Block<T, U> {
pub fn from<B: 'static + BlockFunctionality<T, U> + Send>(b:B) -> Self {

Why does borrow checker need life time tags for output when the inputs are very clear?

Why does the borrow checker gets confused about the lifetimes in the below code
fn main() {
let ss = "abc"; // lets say 'a scope
let tt = "def"; // lets say 'b scope
let result = func(ss, tt);
}
fn func(s: &str, t: &str) -> &str {
t
}
| fn func(s: &str, t: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `t`
Why does it even matter what is going out in this code? Am I missing something very important edge case?
but when I annotate them with life time tags it works.
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
t
}
I read that each variable binding (let) creates an Implicit scope, then how come 2 input variables have same scope. Correct me If I'm worng. In the function call 'func' stack, "s" will be pushed first and then "t", So "s" and "t" have different lifetimes. First "t" is dropped and then "s".
You haven’t told the compiler whether the return value may borrow from s, from t, from both, or from neither:
fn from_s<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
// can be abbreviated: fn from_s<'a>(s: &'a str, t: &str) -> &'a str
s
}
fn from_t<'a, 'b>(s: &'a str, t: &'b str) -> &'b str {
// can be abbreviated: fn from_t<'a>(s: &str, t: &'a str) -> &'a str
t
}
fn from_both<'a>(s: &'a str, t: &'a str) -> &'a str {
if s < t {
s
} else {
t
}
}
fn from_neither<'a, 'b>(s: &'a str, t: &'b str) -> &'static str {
// can be abbreviated: fn func(s: &str, t: &str) -> &'static str
"foo"
}
The compiler can assume the last one isn’t what you wanted if you didn’t write 'static. But you still need to disambiguate between the first three.
To see why the difference would matter, consider a caller like
fn main() {
let s = String::from("s");
let r;
{
let t = String::from("t");
r = from_s(&s, &t);
// t goes out of scope
}
println!("{}", r);
}
If the compiler allowed you to call from_t instead of from_s, you’d be printing a string that had already been freed.
If I understand correctly, the question is "why both arguments may have the same lifetime?" The short answer is that the lifetime annotations are not concrete values, but rather bounds - it states that "this value must live no more/no less then this lifetime".
When you're writing your code as you do in question: fn func<'a>(s: &'a str, t: &'a str) -> &'a str, you're saying literally the following:
there is some lifetime - let's name it 'a, which can be different on every call site.
arguments s and t must both live no less then 'a (for string literals, this is always the case, since they are 'static, but this may not hold for &String coerced to &str) - that is, function type is contravariant over arguments types (and the lifetime is part of a type).
return value must live no more then 'a - function type is covariant over the return type.
(for more information on variance see the Rustonomicon)
Simplified, this means that both arguments must outlive the return value. This is not always what you want - consider the following case (note that I'm returning s now, so that the initialization order doesn't change):
fn main() {
let ss = "abc";
let mut result = "";
{
let tt = "def".to_string();
result = func(ss, &tt);
}
println!("{}", result);
}
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
s
}
(playground)
This code won't compile, although it is logically correct, since the lifetime annotations don't agree with logic: second argument, t, is in no way connected to the return value, and yet it limits its lifetime, according to function annotations. But when we change function to the following:
fn func<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
s
}
...it compiles and return the desired result (although with some warnings), since now the lifetime 'b isn't connected with 'a and, in fact, can be removed at all - lifetime elision will do its work well.

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.

Unconstrained lifetime error when implementing Index trait

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

Resources