Hi, I'm working on some networking code where I need to manually manage buffers, and I find myself creating small helper functions to help with bookkeeping and other minutiae, but I keep running into two similar issues around lifetimes and mutability.
The first one is around shared mutable access to a variable, idx in this example. read_u32 and read_u16 both require mutable access to idx, but only one function is ever called at a time. Is there a way to let the compiler know that the two functions wont ever execute concurrently?
fn read_buf() -> (u32, u16) {
let buf = vec![0u8; 32];
let mut idx = 0;
let mut read_u32 = || {
idx += 4;
u32::from_be_bytes(buf[idx - 4..idx].try_into().unwrap())
};
let mut read_u16 = || {
idx += 2;
u16::from_be_bytes(buf[idx - 2..idx].try_into().unwrap())
};
// error[E0499]: cannot borrow `idx` as mutable more than once at a time
let (a, _b, c) = (read_u32(), read_u32(), read_u16());
(a, c)
}
The second example involes owning a buffer and handing out immutable references to that buffer. I'm not sure if I'm approaching this the right way with Generators, but I'm not sure how to say the lifetime of the yielded &str should be shorter than the next call to Generator::resume.
#![feature(generators, generator_trait)]
use std::{fmt::Write, ops::Generator};
fn buf_generator<'a, 'gen>() -> impl Generator<&'a str, Yield = &'gen str> + 'gen {
let mut buf = String::new();
let gen = move |mut url: &str| loop {
buf.clear();
write!(&mut buf, "{url}?some=params");
// error[E0626]: borrow may still be in use when generator yields
url = yield &buf[..];
};
gen
}
Same thing as buf_generator, but as a regular re-entrant function
fn buf_builder
use std::fmt::Write;
fn buf_builder<'gen>() -> impl FnMut(&'_ str) -> &'gen str {
let mut buf = String::new();
let builder = move |url: &str| {
buf.clear();
write!(&mut buf, "{url}?some=params");
// error: captured variable cannot escape `FnMut` closure body
return &buf[..];
};
builder
}