I created an API that is effectively a glorified vector that only takes String
s and hands out a &'static mut String
to its elements:
struct MemManager {
ptr: *mut String,
len: usize,
cap: usize,
marker: std::marker::PhantomData<String>,
}
impl MemManager {
fn new(cap: usize) -> Self {
unsafe {
let layout = std::alloc::Layout::array::<String>(cap).unwrap();
let ptr = std::alloc::alloc(layout) as *mut String;
Self {
ptr,
cap,
len: 0,
marker: std::marker::PhantomData,
}
}
}
pub fn push(&mut self, string: String) -> usize {
unsafe {
let index = self.len;
std::ptr::write(self.ptr.add(index), string);
self.len += 1;
index
}
}
pub fn get(&mut self, index: usize) -> &'static mut String {
unsafe { &mut *self.ptr.add(index) }
}
}
Very straight forward. Then I created a Wrapper
that wraps MemManager
:
struct Wrapper(MemManager);
impl Wrapper {
fn new() -> Self {
Self(MemManager::new(10))
}
fn get_data(&mut self, index: usize) -> StaticData {
let data = self.0.get(index);
StaticData { data }
}
fn push(&mut self, string: String) -> usize {
self.0.push(string)
}
fn other_mutable(&mut self) {}
}
Notice that get_data()
doesn't hadn't out the String
references directly but hands out another wrapper that looks like this:
#[derive(Debug)]
struct StaticData<'d> {
data: &'d mut String,
}
Note that while the API that the string ref comes from (MemManager
's API) was &'static mut String
this wrapper stores the String
ref as a generic lifetime. This leads to an interesting result: Wrapper
is considered borrowed while the StaticData
instance is still alive.
fn main() {
let mut wrapper = Wrapper::new();
let index1 = wrapper.push("Jumbo".to_string());
let data = wrapper.get_data(index1);
wrapper.other_mutable();
println!("{data:?}");
}
This code will NOT compile and rustc
gives the following error:
error[E0499]: cannot borrow `wrapper` as mutable more than once at a time
--> src/main.rs:80:5
|
79 | let data = wrapper.get_data(index1);
| ------- first mutable borrow occurs here
80 | wrapper.other_mutable();
| ^^^^^^^ second mutable borrow occurs here
81 | println!("{data:?}");
| -------- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` (bin "playground") due to 1 previous error
However, if you change the generic lifetime on StaticData
to use a 'static
lifetime:
#[derive(Debug)]
struct StaticData {
data: &'static mut String,
}
all of a sudden Wrapper
is no longer mutable borrowed while the StaticData
instance is still around and you get the following output:
Standard Error
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/playground`
Standard Output
StaticData { data: "Jumbo" }
Part of me thinks this might be something to do with elided lifetimes and the generic lifetime is inheriting the &mut self
lifetime but I cannot find anything in The Book or the Rust Reference that talks about how the compiler determines generice lifetimes stored in a struct. They all talk about the lifetimes of references returned directly. Also, my understanding was that lifetimes stipulate how long a borrow lives NOT what is being borrowed (Although, yes, what is being borrowed is necessarily intertwined with lifetimes, a lifetime being generice vs. static should change that from my understanding) .
What is going on here??