tools: add borrow::Tied with example docs

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-08-16 12:36:27 +02:00
parent 025eaf5519
commit 2ecd147036
2 changed files with 111 additions and 0 deletions

110
proxmox-tools/src/borrow.rs Normal file
View File

@ -0,0 +1,110 @@
//! Helpers for borrowing and self-borrowing values.
/// This ties two values together, so that one value can borrow from the other, while allowing the
/// resulting object to be stored in a struct. The life time of the borrow will not infect the
/// surrounding type's signature.
///
/// A `Tied` value dereferences to its produced borrowing value, and can likely be used as a
/// drop-in replacement for existing code which needs to get rid of lifetimes.
///
/// Example:
/// ```
/// // Our owner which we want to borrow from.
/// struct Owner(i64);
/// struct Borrow<'a>(&'a mut i64);
///
/// impl Owner {
/// pub fn borrow_mut(&mut self) -> Borrow {
/// Borrow(&mut self.0)
/// }
/// }
///
/// // Show that we can be used as a Borrow
/// impl<'a> Borrow<'a> {
/// pub fn i_am_a_borrow(&self) {}
/// }
///
/// // The following cannot be expressed in rust:
/// //struct Usage {
/// // owner: Owner,
/// // borrow: Borrow<??? lifetime of self.owner ???>
/// //}
///
/// // Instead we use:
/// use proxmox::tools::borrow::Tied;
/// struct Usage {
/// tied: Tied<Owner, Borrow<'static>>,
/// }
///
/// let usage = Usage {
/// tied: Tied::new(Owner(10), |owner| Box::new(unsafe { (*owner).borrow_mut() })),
/// };
///
/// // tied can be used like a Borrow:
/// usage.tied.i_am_a_borrow();
/// ```
pub struct Tied<T, U: ?Sized> {
/// The contained "value" of which we want to borrow something.
inner: Option<Box<T>>,
/// The thing borrowing from `inner`. This is what the `Tied` value ultimately dereferences to.
borrow: Option<Box<U>>,
}
impl<T, U: ?Sized> Drop for Tied<T, U> {
fn drop(&mut self) {
// let's be explicit about order here!
std::mem::drop(self.borrow.take());
}
}
impl<T, U: ?Sized> Tied<T, U> {
/// Takes a value and a function producing the borrowing value. The owning value will be
/// inaccessible until the tied value is resolved. The dependent value is only accessible by
/// reference.
pub fn new<F>(value: T, producer: F) -> Self
where
F: FnOnce(*mut T) -> Box<U>,
{
let mut value = Box::new(value);
let borrow = producer(&mut *value);
Self {
inner: Some(value),
borrow: Some(borrow),
}
}
pub fn into_boxed_inner(mut self) -> Box<T> {
self.borrow = None;
self.inner.take().unwrap()
}
pub fn into_inner(self) -> T {
*self.into_boxed_inner()
}
}
impl<T, U: ?Sized> AsRef<U> for Tied<T, U> {
fn as_ref(&self) -> &U {
self.borrow.as_ref().unwrap()
}
}
impl<T, U: ?Sized> AsMut<U> for Tied<T, U> {
fn as_mut(&mut self) -> &mut U {
self.borrow.as_mut().unwrap()
}
}
impl<T, U: ?Sized> std::ops::Deref for Tied<T, U> {
type Target = U;
fn deref(&self) -> &U {
self.as_ref()
}
}
impl<T, U: ?Sized> std::ops::DerefMut for Tied<T, U> {
fn deref_mut(&mut self) -> &mut U {
self.as_mut()
}
}

View File

@ -3,6 +3,7 @@
use failure::*;
use lazy_static::lazy_static;
pub mod borrow;
pub mod common_regex;
pub mod fd;
pub mod fs;