Outlive struct method [duplicate] - struct

This question already has answers here:
Is there any way to return a reference to a variable created in a function?
(5 answers)
Return local String as a slice (&str)
(7 answers)
Closed 5 years ago.
I want some data object to serialize itself and make a version of itself that is possible to send via UDP. The problem is that the String created by serialization (serde_json::to_string) lives only until end of scope (which makes sense to me) so the byte version (a &[u8] from as_bytes) cannot reference it out of scope. I've tried adding some lifetime parameters but without success; I don't actually understand lifetime parameters that much yet.
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use std::str;
#[derive(Debug, Serialize, Deserialize)]
struct Test {
text: String,
}
impl Test {
pub fn new(input: &str) -> Self {
Test {
text: String::from(input),
}
}
pub fn to_utf8(&self) -> &[u8] {
// serde_json::to_string returns Result<String, ...>
// String.as_bytes() returns &[u8]
serde_json::to_string(&self).unwrap().as_bytes()
}
}
fn main() {
let a = Test::new("abc");
println!("{:?}", a.to_utf8());
}
Playground
error[E0597]: borrowed value does not live long enough
--> src/main.rs:22:9
|
22 | serde_json::to_string(&self).unwrap().as_bytes()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough
23 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 19:5...
--> src/main.rs:19:5
|
19 | / pub fn to_utf8(&self) -> &[u8] {
20 | | // serde_json::to_string returns Result<String, ...>
21 | | // String.as_bytes() returns &[u8]
22 | | serde_json::to_string(&self).unwrap().as_bytes()
23 | | }
| |_____^

As you've worked out, the slice can't outlive the scope. I don't think you can fix this with lifetime annotations: explicit lifetimes can't actually make objects live for longer, they only stop the compiler making too-strict assumptions about which lifetimes are the same.
The way to do this is to give ownership of these bytes back to the caller, instead of borrowing them in a slice. The obvious candidate for that is a Vec:
pub fn to_utf8(&self) -> Vec<u8> {
Vec::from(serde_json::to_string(&self).unwrap().as_bytes())
}
This is an extra copy, which is unfortunate, but luckily Serde provides a function that gives you this output directly!
pub fn to_utf8(&self) -> Vec<u8> {
serde_json::to_vec(&self).unwrap()
}
Now you can return a Vec<u8> which will be owned by the caller and last as long as the caller needs. You can use the Vec<u8> exactly the same way you would use the slice, and you can even borrow the underlying slice from it if you need to.

Related

Rust, serde Deserialize and Higher Rank Trait Bounds For<`a>

I am trying to have a deeper understanding of how rust works. I am trying to do some serializing and deserializing to save and load a struct with a generic type. I got it to work, but I don't understand the HRTB and why they made the code work.
Initially I have this
use serde::Deserialize;
use bincode;
use std::fs;
#[derive(Deserialize)]
pub struct Construct<T> {
data: Vec<T>
}
impl <'a, T: Deserialize<'a>> Construct<T> {
pub fn load() -> Self {
match fs::read("data.sav") {
Ok(d) => {
let c: Construct<T> = bincode::deserialize(&d).unwrap();
c
},
Err(e) => {
println!("{e}, passing empty Construct");
Self { data: Vec::new() }
}
}
}
}
whihc produces this error
error[E0597]: `d` does not live long enough
--> src/main.rs:14:49
|
10 | impl <'a, T: Deserialize<'a>> Construct<T> {
| -- lifetime `'a` defined here
...
14 | let c: Construct<T> = bincode::deserialize(&d).unwrap();
| ---------------------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `d` is borrowed for `'a`
15 | c
16 | },
| - `d` dropped here while still borrowed
I have fixed the impl block to take a higher ranked trait bound. And it works just fine.
...
impl <T: for<'a> Deserialize<'a>> Construct<T> {
pub fn load() -> Self {
...
As I understand it Deserialize needs to make sure that the input reference lives as long as the out structure(https://serde.rs/lifetimes.html), and the difference between declaring the trait in the first example and using for<'a>. Is that the 1st example the lifetime is being provided by the caller and the for<'a> is getting the lifetime from the impl itself. (How does "for<>" syntax differ from a regular lifetime bound?)
Am I right in thinking that with the for<'a> syntax we are getting the lifetime from the implementation block and that gives us a longer lifetime than from calling the function? Is there another way to code this load function without using HRTBs?
Am I right in thinking that with the for<'a> syntax we are getting the lifetime from the implementation block
Yes, from the call bincode::deserialize(&d). Specifically, the lifetime of d.
and that gives us a longer lifetime than from calling the function
Nope, a shorter: instead of a caller-decided lifetime, that will always be longer than d's lifetime (because it is declared inside our function), we get a lifetime for only d.
Is there another way to code this load function without using HRTBs?
Yes, by bounding T to DeserializeOwned. But this just hides the HRTB: DeserializeOwned uses them behind the scene.

Lifetime in mutable structure with HashSet

I'm having trouble understanding why rust doesn't like my remove_str method in there:
use std::cell::RefCell;
use std::collections::HashSet;
#[derive(Hash, Eq, PartialEq)]
struct StringWrap<'a>{
s: &'a String,
}
struct Container<'a>{
m: HashSet<StringWrap<'a>>
}
impl<'a> Container<'a>{
fn remove_str(&mut self, s: &str){
let string = String::from(s);
let to_remove = StringWrap{s: &string};
self.m.remove(&to_remove);
}
}
It chokes with:
error[E0597]: `string` does not live long enough
--> tests/worksheet.rs:17:39
|
14 | impl<'a> Container<'a>{
| -- lifetime `'a` defined here
...
17 | let to_remove = StringWrap{s: &string};
| ^^^^^^^ borrowed value does not live long enough
18 | self.m.remove(&to_remove);
| ------------------------- argument requires that `string` is borrowed for `'a`
19 | }
| - `string` dropped here while still borrowed
As far as I can see, my string and to_remove live long enough to allow the .remove call to do its job. Is it because remove is potentially asynchronous or something like that?
Thanks for any help or insight!
As far as I can see, my string and to_remove live long enough to allow the .remove call to do its job. Is it because remove is potentially asynchronous or something like that?
No, it's because HashSet::remove must be called with something that the item becomes when borrowed:
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: Hash + Eq,
However, unless you manually implement Borrow for StringWrap, only the blanket reflexive implementation will apply—and thus remove can only be called with value of type &StringWrap<'a>. Note the lifetime requirement.
What you need to do to make this work is to implement Borrow for StringWrap. You could, for example, do the following:
impl Borrow<str> for StringWrap<'_> {
fn borrow(&self) -> &str {
self.s
}
}
and then Container::remove_str can merely forward its argument to HashMap::remove:
impl Container<'_> {
fn remove_str(&mut self, s: &str) {
self.m.remove(s);
}
}
See it on the playground.
All that said, it's rather unusual to store references in a HashSet: typically one would move ownership of the stored Strings into the set, which would render this problem moot as no lifetimes would be at play.

Cannot infer an appropriate lifetime for borrow expression due to conflicting requirement(s) [duplicate]

This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 1 year ago.
I'm having a hard time resolving a lifetime issue:
pub struct A<'a> {
pub a: &'a str,
pub b: &'a u8,
}
pub enum Executable<'a> {
ExecutableA(A<'a>),
}
pub struct Config {
pub a: String,
pub b: u8, // some more config values to follow
}
impl Config {
pub fn new() -> Config {
// Implementation details omitted due to irrelevancy
unimplemented!();
}
}
pub struct Cli<'a> {
pub config: Config,
pub execution_list: Vec<Executable<'a>>,
}
impl<'a> Cli<'a> {
pub fn new() -> Cli<'a> {
Cli {
config: Config::new(),
execution_list: vec![],
}
}
pub fn prepare(&mut self) {
self.execution_list.push(
// Compilation error occurs for following line
Executable::ExecutableA(A {
a: &self.config.a,
b: &self.config.b,
}),
)
}
}
The compilation error is:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/lib.rs:39:20
|
39 | a: &self.config.a,
| ^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 35:20...
--> src/lib.rs:35:20
|
35 | pub fn prepare(&mut self) {
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:39:20
|
39 | a: &self.config.a,
| ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 27:6...
--> src/lib.rs:27:6
|
27 | impl<'a> Cli<'a> {
| ^^
note: ...so that the expression is assignable
--> src/lib.rs:38:13
|
38 | / Executable::ExecutableA(A {
39 | | a: &self.config.a,
40 | | b: &self.config.b,
41 | | }),
| |______________^
= note: expected `Executable<'a>`
found `Executable<'_>`
(Playground)
After doing a lot of reading and looking at other questions, I still can't convince myself that I'm understanding the error correctly, mainly because I can't reason why the compiler is attaching the "anonymous" lifetime to the &mut self reference in the prepare function.
My general design idea is that I'll have my Cli struct contain a config, and a list of executables such that I can add all relevant executables to that list in the Cli's prepare function (where I'd like those executables to be able to reference values owned by the Config). I'll then iterate over that execution list to start/stop executables.
I think one answer to this question is to not have executables maintain references to config values and instead copy those values, but I feel like that shouldn't be necessary, and would like to use this issue as a learning opportunity.
I'm totally open to suggestions around "Maybe you should rethink your design and do X, instead."
Anonymous lifetimes are inferred by the compiler, most of them will point to 'static,
If you add this (&'a mut self):
pub fn prepare (&'a mut self) /* ... */
It compiles just fine, since the impl lifetime requirement is not conflicting with the Executable one.
Edit:
As pointed out, i was wrong (probably because the rust style did reach into my mind but lifetimes did not), i though about generic lifetimes but i looked at the already existing lifetime 'a and found that it compiled, at least mutably borrowing the class for eternity makes sense.
About your design:
I found it good for what you are trying to do, also i am not the expert with designs, specially in rust, so i found nothing wrong about it.
Viable solution:
Like pointed out, you could add the generic lifetime:
pub fn prepare <'b> (&'b mut self) { ... }
Now whenever the function is called the mutable borrow will last the needed lifetime, and when you want to immutably borrow it, it will work.

Lifetimes when returning a reference to the contents of Rc<RefCell<_>> [duplicate]

This question already has answers here:
How do I return a reference to something inside a RefCell without breaking encapsulation?
(3 answers)
How do I borrow a RefCell<HashMap>, find a key, and return a reference to the result? [duplicate]
(1 answer)
Closed 2 years ago.
How can I return a reference to something from inside a shared pointer (in this case Rc<RefCell<_>>)? In the example below, I show how it can be done with just a regular mutable reference to self, but if it becomes a shared pointer instead, the compiler gets angry that the return type has a missing lifetime specifier.
error[E0106]: missing lifetime specifier
--> src/main.rs:19:60
|
19 | fn add_to_shared(me: Rc<RefCell<Thing>>, item: i32) -> &i32 {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
19 | fn add_to_shared(me: Rc<RefCell<Thing>>, item: i32) -> &'static i32 {
| ^^^^^^^^
use std::cell::RefCell;
use std::rc::Rc;
struct Thing {
items: Vec<i32>,
}
impl Thing {
fn new() -> Self {
Thing { items: Vec::new() }
}
fn add_to_self(&mut self, item: i32) -> &i32 {
self.items.push(item);
self.items.last().unwrap()
}
// does not compile
fn add_to_shared(me: Rc<RefCell<Thing>>, item: i32) -> &i32 {
me.borrow().items.push(item);
me.borrow().items.last().unwrap()
}
}
fn main() {
let mut thing = Thing::new();
println!("{}", thing.add_to_self(10));
let mut rc = Rc::new(RefCell::new(Thing::new()));
println!("{}", rc.add_to_shared(20));
}
Why do I want to do this? I have a program that builds a tree-like structure with multiple ownership. One of the associated methods takes two nodes of the tree (each shared pointers) and bundles them together into another part of the tree. Each method returns a reference to the newly created node so that it can be conveniently logged out (see the example).
I was thinking I'd need to use lifetime annotations to get this to work, but I have not been able to find out how do apply this concept to the interior of a Rc<RefCell<_>> type.
I think the problem here is that Rust knows how long self lives, but is not able to figure out how long Rc<RefCell<_>> exists. Do you need to return i32 references? If you would return just i32, the value would be copied and you would not have a reference into a struct that might not exists long enough.

Cannot assign because it is borrowed in a method of a structure that returns a Result containing a reference [duplicate]

This question already has answers here:
Cannot borrow `*self` as mutable more than once at a time when returning a Result containing a reference
(2 answers)
"Variable does not live long enough" when returning a Result containing a reference but it does live long enough
(1 answer)
Why do I get a borrow error when returning result of a child structure's method? [duplicate]
(2 answers)
Closed 2 years ago.
It seems I don't get the borrowing concept in Rust. I have this simple example:
pub struct User {
name: Option<String>,
}
impl User {
pub fn new() -> Self {
Self {
name: Some("".to_string()),
}
}
pub fn write_name(&mut self) -> Result<(), &str> {
let name = self.generate_name()?;
self.name = Some(name);
Ok(())
}
pub fn generate_name(&self) -> Result<String, &str> {
Ok("John".to_string())
}
}
error[E0506]: cannot assign to `self.name` because it is borrowed
--> src/lib.rs:14:9
|
12 | pub fn write_name(&mut self) -> Result<(), &str> {
| - let's call the lifetime of this reference `'1`
13 | let name = self.generate_name()?;
| ---- - returning this value requires that `*self` is borrowed for `'1`
| |
| borrow of `self.name` occurs here
14 | self.name = Some(name);
| ^^^^^^^^^ assignment to borrowed `self.name` occurs here
I need to return Result and I need to split name generation from setting name. How can I release borrowing so I can set the self.name variable?
The function header
pub fn generate_name(&self) -> Result<String, &str>
should probably be changed to
pub fn generate_name(&self) -> Result<String, &'static str>
The former makes the compiler assume that the lifetime of the error type &str is tied to the lifetime of self, so self is considered borrowed on the call site until the result goes out of scope. It's unlikely that your error type is supposed to borrow self – you much more likely want to return a static string as an error instead.
Your defintion of generate_name() has an implicit lifetime parameter in the argument list – the lifetime of the self reference – and an implicit lifetime parameter in the return type – the lifetime of the error type &str. If there is exactly one free lifetime parameter in both the argument list and the return type, the compiler applies a lifetime elision rule that identifies the free lifetime parameters, since this is most likely what you want. If it isn't what you want, like in this case, you need to explicitly state the desired lifetimes.

Resources