Restricting the usage of a mutable reference
Hi,
I often run into the same problem: I want to pass around a mutable reference, but I also want to restrict the usage (e.g. because there is some invariant). My preferred solution is to create a wrapper type (very similar to std::fmt::Formatter). For example:
// Cannot implement `Copy` or `Clone` because of `&'a mut u64`
pub struct IdGen<'a> {
// Only incrementing is allowed
state: &'a mut u64,
}
impl<'a> IdGen<'a> {
pub fn new(state: &'a mut u64) -> Self {
Self { state }
}
pub fn next_id(&mut self) -> u64 {
let id = self.state.checked_add(1).unwrap();
*self.state = id;
id
}
}
pub trait ValueGen {
// I don't trust the implementors, so I use `IdGen` instead of `&mut u64`
fn gen(id_gen: IdGen) -> Self;
}
pub struct Foo { id: u64 }
impl ValueGen for Foo {
fn gen(mut id_gen: IdGen) -> Self {
Foo { id: id_gen.next_id() }
}
}
This works, but it's very awkward to use because IdGen cannot be Copy or Clone:
fn main() {
let mut state = 0;
let id_gen = IdGen::new(&mut state);
let foo1 = Foo::gen(id_gen);
//let foo2 = Foo::gen(id_gen); // Does not compile because not `Copy`
}
The only solution I found so far is implementing an Clone-like function:
impl<'a> IdGen<'a> {
// Note: It does not return `Self`.
// It took me hours to realize why `Self` would not work...
pub fn copy(&mut self) -> IdGen {
IdGen { state: self.state }
}
}
fn main() {
let mut state = 0;
let mut id_gen = IdGen::new(&mut state);
let foo1 = Foo::gen(id_gen.copy());
let foo2 = Foo::gen(id_gen); // Its works!
}
This is better than "does not compile", but it's verbose.
So, questions:
- Is there a better solution for the initial problem (restricting the usage of a mutable reference)?
- If not, is there a better solution for the non-
Copy-problem?
PS:
A solution that I tried but could not uphold the invariant was to take &mut IdGen instead of IdGen:
pub trait BadValueGen {
// `&mut IdGen` instead of `IdGen`
fn gen(id_gen: &mut IdGen) -> Self;
}
pub struct BadFoo { id: u64 }
impl BadValueGen for BadFoo {
fn gen(id_gen: &mut IdGen) -> Self {
let value = BadFoo { id: id_gen.next_id() };
// Burn invariant! Burn!
let counter = Box::leak(Box::new(0));
*id_gen = IdGen::new(counter);
value
}
}
Edit: Formatting
Posted Feb 3, 2021, 11:54 AM UTC · edited
612