Data in a buffer is not `static lifetime (no_std, serde_json_core)

Howdy, everyone! I am building a simple client-server tcp communication library. The trick is that the client must be a no_std library. We can ignore the socket communication issues with no_std for now.

So, I am using serde_json_core.

My issue is that I can't seem to pass any data buffer into the deserialize function because it requires that data buffer to be static. But, my data can't be static. It's ultimately going to come out of a socket.

This is the simplest case I have:

pub fn main() {
    // In this example, we'll just use an empty buffer
    let mut data = [0 as u8; 100];

    /// Now, I deserialize it
    let result = serde_json_core::from_slice::<NetworkMessage>(&data);
    let message = result.unwrap();

    println!("{:?}", message); // Should be an instance of NetworkMessage
}

That gives me the error:

error[E0597]: `data` does not live long enough
   --> src/main.rs:40:60
    |
40  |     let result = serde_json_core::from_slice::<NetworkMessage>(&data);
    |                                                            ^^^^^
    |                                                            |
    |                                                            borrowed value does not live long enough
    |                                                            cast requires that `data` is borrowed for `'static`
...
109 | }
    | - `data` dropped here while still borrowed

I am sure I am missing something obvious. I'm more sure (and open to the idea that) my code needs to be reorganized.

Thank you for any suggestions.

Well, I just discovered my real problem:

pub struct NetworkMessage {
    message: &'static str,
}

That is where the static lifetime is being brought in. So, I'm looking for alternatives. I cannot use a standard library there, so String is out of the question.

It can be, but not in the way you'd normally do in Rust. What you effectively need is a static mut variable, something that is known in Rust to be unsafe. So what you can do is as follows:

  • Use a Mutex made from a spin-lock. Write your own or use the spin crate.
  • Use the lazy_static macro from a crate of the same name.

Then you can do something in the line of:

lazy_static! {
  static buffer: Mutex<[u8;100]> = Mutex::new([0_u8;100]);
}

Now, use this buffer to store the data - it will have static lifetime.

Edit: Previously I had incorrectly specified the buffer to be static mut. It doesn't need to be mut. Mutex provides interior mutability.

Well, @RedDocMD that is super helpful.

I think I'm almost there. The issue I've hit is that the lazy_static! macro doesn't have a rule for the mut keyword. And I can't seem to get the static variable to be mutable in order for me to use it.

#[macro_use]
extern crate lazy_static;
use std::borrow::{Borrow, BorrowMut};
use spin::Mutex;

lazy_static! {
    static ref DATA: Mutex<[u8; 100]> = Mutex::new([0_u8;100]);
}

pub fn main() {
    let result = serde_json_core::from_slice::<NetworkMessage>(DATA.borrow_mut().get_mut());
}

produces

error[E0596]: cannot borrow immutable static item `DATA` as mutable
  --> src/main.rs:54:64
   |
54 |     let result = serde_json_core::from_slice::<NetworkMessage>(DATA.borrow_mut().get_mut());
   |                                                                ^^^^ cannot borrow as mutable

I have tried borrow() and borrow_mut(). In one instance, the compiler suggested:

= help: trait DerefMut is required to modify through a dereference, but it is not implemented for DATA

EDIT:
And when I try to lock the mutex, I get the original problem. It says that the value is not static:

let mut a = DATA.lock();
let result = serde_json_core::from_slice::<NetworkMessage>(&*a);

Well, yes I was quite wrong about my fix.
I'm sorry but I'm out of ideas. Perhaps someone with more knowledge can help out.

I appreciate helping me learn new things. Thanks for the attempt.

The issue is the &'static itself. Having &'static T at some point means the T, in this case the string content and its buffer, should exist and accessible for the entire lifetime of the process. That's why &'static str practically means some string literal or substring of it.

To borrow from the non-permanent content, you need to use struct with lifetime parameter instead.

1 Like

Thank you @Hyeonu that was just the solution I needed. And what's worse, I knew that, lol.

For future googlers, this was the final solution:

#[derive(Serialize, Deserialize, Debug)]
pub struct NetworkMessage<'a> {
    pub message: &'a str,
}

pub fn main() {
    let mut data = [0 as u8; 100];

    // A function that fills data from a TcpStream

     let result = serde_json_core::from_slice::<NetworkMessage>(&data);
     let nm = result.unwrap();
     println!("{:?}", nm.message); // Whatever came off the line
}

Of course it doesn't have one. A static can't be mutable just like that! Since it is accessible globally, then one could create multiple simultaneous mutable borrows to it, creating unsoundness. (Worse yet, it could even be mutated from multiple threads at the same time.)

This is exactly why you shouldn't ignore the first part of @RedDocMD 's advice: you will need a Mutex. You wrap your object in a Mutex, you put the mutex in an apparently non-mutable lazy static, and then the mutex will allow exclusive mutation through its own interface only, which ensures locking (i.e. exclusive borrowing).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.