How can I make something like this compile?
struct MyStruct<'a> {
v: String,
r: &'a String
}
fn main() {
let s = String::from("test");
MyStruct{v:s, r:&s};
}
How can I make something like this compile?
struct MyStruct<'a> {
v: String,
r: &'a String
}
fn main() {
let s = String::from("test");
MyStruct{v:s, r:&s};
}
You can't in safe Rust, and getting around the restriction with unsafe code is tricky and discouraged. You'll need to restructure your code so that the owned String
and the borrowing &String
don't live in the same struct, or replace the reference with a usize
index.
"Self-referential struct" is the usual term for this construction, and you can search this forum for that phrase to find more discussion of the issues they cause from past threads. For example, if you could safely construct a value of your type MyStruct<'a>
, you could (safely) cause that value to be moved from one location in memory to another (by putting it in a Box
, say), after which the r
field would be a dangling pointer because it would still point into the previous location, and that's undefined behavior.
What are you trying to achieve? Storing a reference to a String
next to that same String
doesn't give you anything useful to work with. Do you want to save a sub-string, that will be shrunk, later? The correct type for that use-case would be &str
.
However, as @cole-miller already pointed out, that doesn't work. &str
boils down to the type { pointer: *const u8, length: usize }
. If you store an offset instead of a pointer, you can reconstruct the pointer whenever you need to work with r
.
MyStruct {
v: String,
r_offset: usize,
r_length: usize,
}
Constructing MyStruct:
let v: String = "Hello, World!".to_owned();
let r_offset: isize = 0;
let r_length: usize = v.len();
let my_struct = MyStruct { v, r_offset, r_length };
Obtaining a &str
/&mut str
from MyStruct
:
impl MyStruct {
fn get(&self) -> &str {
let data: *const u8 = self.v.as_ptr().offset(self.r_offset);
let slice: &[u8] = unsafe { std::slice::from_raw_parts(data, self.r_length) };
unsafe { std::str::from_utf8_unchecked(slice) }
}
fn get_mut(&mut self) -> &mut str {
let data: *mut u8 = self.v.as_mut_ptr().offset(self.r_offset);
let slice: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(data, self.r_length) };
unsafe { std::str::from_utf8_unchecked_mut(slice) }
}
}
Now, what's missing is an example to update the sub-string and depending on your use-case, you may even want to update the String
, which would get quite messy.
At this point, I'd probably just scrap the internally unsafe
API. I'd store r_begin: usize
and r_end: usize
instead of r_offset
and r_length
respectively and use the indexing operation v[r_begin..r_end]
, which guarantees memory-safety by sacrificing a bit of runtime performance, as it includes boundary checks, which are unlikely to be optimized away.
Luckily, most of the time, you don't need to squeeze out every bit of performance, i.e. having to use unsafe
is rarely necessary. Rust is plenty fast as it is.
The Ouroboros crate is one of the more popular ways of creating self-referential structs.
https://crates.io/crates/ouroboros
These examples show alternative ways to use it.
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.