I'm looking for a class much like Vec<RefCell<T>>, in that it is the ultimate owner & allocator of all of its data, yet different pieces of the array can be be mutably borrowed by multiple parties indefinitely.
I emphasize indefinitely because of course pieces of Vec<T> can also be mutably borrowed by multiple parties, but doing so involves making a split which can only be resolved after the parties are done borrowing.
Vec<RefCell<T>> seems to be a world of danger and many ugly if statements checking borrow_state, which seems to be unstable. If you do something wrong, then kablammo! Panic! This is not what a lending library is like. In a lending library, if you ask for a book that isn't there, they tell you "Oh, it's checked out." Nobody dies in an explosion.
So I would like to write code something like this:
let mut a = LendingLibrary::new();
a.push(Foo{x:10});
a.push(Foo{x:11});
let b1 = a.get(0); // <-- b1 is an Option<RefMut<Foo>>
let b2 = a.get(1); // <-- b2 is an Option<RefMut<Foo>>
// the 0th element has already been borrowed, so...
let b3 = a.get(0); // <-- b3 is Option::None
Does such a thing exist? Or is there another canonical way to get this kind of behavior? A kind of "friendly RefCell"?
If the answer happens to be yes, is there also a threadsafe variant?
RefCell is not designed for long-lived borrows. The typical use case is that in a function, you'll borrow the RefCell (either mutably or immutably), work with the value, then release the borrow before returning. I'm curious to know how you're hoping to recover from a borrowed RefCell in a single-threaded context.
The thread-safe equivalent to RefCell is RwLock. It has try_read and try_write functions that do not block or panic if an incompatible lock is still acquired (on any thread, including the current thread). Contrarily to RefCell, it makes sense to just retry later if locking a RwLock fails, since another thread might just happen to have locked it at the same time.
If you end up always using write or try_write, and never read or try_read, then you should probably use the simpler Mutex instead.
#![feature(borrow_state)]
use std::cell::{RefCell, RefMut, BorrowState};
struct LendingLibrary<T> {
items: Vec<RefCell<T>>
}
impl<T> LendingLibrary<T> {
fn new(items: Vec<T>) -> LendingLibrary<T> {
LendingLibrary {
items: items.into_iter().map(|e| RefCell::new(e)).collect()
}
}
fn get(&self, item: usize) -> Option<RefMut<T>> {
self.items.get(item)
.and_then(|cell| match cell.borrow_state() {
BorrowState::Unused => Some(cell.borrow_mut()),
_ => None
})
}
}
fn main() {
let lib = LendingLibrary::new(vec![1, 2, 3]);
let a = lib.get(0); // Some
let b = lib.get(1); // Some
let a2 = lib.get(0); // None
}
This currently requires a nightly release to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With