How can I make my data live as long as String::as_bytes()?

I'm still learning Rust. I couldn't figure out how to make my bytes live long enough, so please help.

// This code works fine,
for i in 0..n {
  let bytes = "test".as_bytes();
  builder.add(&bytes);
}
builder.build();

// But, this code doesn't work
struct Content {}
impl Content {
  fn to_data(&self) -> Result<Vec<u8>, ()> {
    Ok("test".as_bytes().to_vec())
  }
}
for i in 0..n {
  let bytes = Content {}.to_data().unwrap();
  builder.add(&bytes);
}
builder.build();

The second code above tells me the error,

borrowed value does not live long enough.
}
- bytes dropped here while still borrowed
builder.build();
----- borrow later used here

I think the first code "let bytes = "test".as_bytes();" also drops at the same position, but Rust compiler says nothing about it.
If the first one is okay, I want the second bytes "let bytes = Content {}.to_data().unwrap();" to live as long as the first code does.

"builder" is a struct created by somebody else, so I don't want to change the function's parameters.

Thank you in advance!

In the first part of the code, you're working with a string literal. Literals are a bit special, so you don't run into lifetime issues. "test".as_bytes() lives forever, so it has 'static lifetime and you can pass it around without restrictions.

In the second part of the code, the bytes from the string literal are copied to a vector (Vec<u8>), and then you try to borrow the vector's data and pass it to the builder. But the vector's data only lives for as long as the vector lives. The vector is dropped at the end of each iteration of the loop, so you can't access its data later.

If you want the bytes inside the vector to live longer, you need to store the vector somewhere (like in another vector in the outer scope) and then borrow from that storage. You can also solve this issue by copying data inside builder.add implementation instead of borrowing it, so that its argument only needs to live for the duration of the call. Alternatively, you can change the API to move the vector inside the builder, so that it and its data live for as long as the builder lives.

4 Likes

Thank you for your suggestions.

you need to store the vector somewhere (like in another vector in the outer scope) and then borrow from that storage.

I tried to store the vector outside scope, but it was also difficult because I have to convert the Content into data in the for statement. That means I have to push the bytes to some vec that lives longer and lend that vec to builder. And then, in the next loop step of the for statement, I have to make mutable borrow of the vec because I have to push some bytes again, which will cause an error.

You can also solve this issue by copying data inside builder.add implementation instead of borrowing it

This is not allowed since the builder code is some protocol codex code auto-generated by some API to serialize data across some foreign languages. The Content struct is the only struct I can modify in this case.

So my question is "is there a way to generate 'static bytes from my Content::to_data function like String::as_bytes() just by modifying fn Content::to_data? If it's impossible, how can String::as_bytes make it happen?"

To be specific it's not String::as_bytes but str::as_bytes. There's nothing special about the function itself, it takes a &self and returns a &[u8]. The difference is that string literals are valid for the lifetime 'static (the entire lifetime of the program, this is because they are hardcoded in the binary so they are never dropped), and so the returned &[u8] is also valid for 'static and can be stored like that. There are ways to do it with Content but they're probably not good ideas (for example, you could leak memory to make it so the bytes are never dropped).

I'm not sure why you have to convert the Content into data inside the for statement, can't you do it before it and make a Vec<Vec> which is then used to get the bytes in the for loop?

1 Like

Oh, sorry.
I could convert Content into bytes in advance!

let mut bytes_vec = Vec::new();
for i in 0..n {
  bytes_vec.push(Content {}.to_data().unwrap());
}
for i in 0..n {
  builder.add(&bytes_vec[i]);
}
builder.build();

I didn't notice the way to write two "for" statements separately because I usually don't.
This can be a solution for this case. Thank you!

But it doesn't look very idiomatic coding to me, so if somebody has a way to write only a "for" statement or even better idea, please let me know. I'm glad to know it as well!
Until that, I'll write like above. Anyway, thank you @Heliozoa and @Riateche ! That helps me a lot!

The idiomatic way to write the first loop depends on your actual code I think. It's probably fine to do in a way like that. I would definitely rewrite the second loop as for bytes in bytes_vec { ... } though

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.