0

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 | |     }
   | |_____^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Dominik Dosoudil
  • 899
  • 8
  • 15
  • *Playground link so I don't have to paste error msg* — it's not polite to force **every** person who reads your question to click a link in exchange for **one** person avoiding the time to copy-and-paste some text. – Shepmaster Feb 09 '18 at 20:16
  • @Shepmaster, it wasn't meant like that. I thought it's better to see the code 'in action' and it seemed to me more DRY when the error can be generated. – Dominik Dosoudil Feb 13 '18 at 17:00

1 Answers1

1

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.

Dan Hulme
  • 14,779
  • 3
  • 46
  • 95
  • 2
    note that `String` has the `into_bytes` method that creates a `Vec` without any copies, simply by taking ownership of the `String`, so you can skip the `as_bytes()`, `Vec::from` dance. But your `to_vec` solution is even more elegant! – oli_obk Feb 09 '18 at 18:01
  • 2
    @oli_obk-ker Good point. I just wanted to first show the "obvious" solution to get a `Vec` without changing the code too much, and then develop it into the solution I actually recommend. – Dan Hulme Feb 09 '18 at 18:14