From 2ecd147036868c4272350d2c061914f6b0d6046b Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Fri, 16 Aug 2019 12:36:27 +0200 Subject: [PATCH] tools: add borrow::Tied with example docs Signed-off-by: Wolfgang Bumiller --- proxmox-tools/src/borrow.rs | 110 ++++++++++++++++++++++++++++++++++++ proxmox-tools/src/lib.rs | 1 + 2 files changed, 111 insertions(+) create mode 100644 proxmox-tools/src/borrow.rs diff --git a/proxmox-tools/src/borrow.rs b/proxmox-tools/src/borrow.rs new file mode 100644 index 00000000..8039dae6 --- /dev/null +++ b/proxmox-tools/src/borrow.rs @@ -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 +/// //} +/// +/// // Instead we use: +/// use proxmox::tools::borrow::Tied; +/// struct Usage { +/// tied: Tied>, +/// } +/// +/// 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 { + /// The contained "value" of which we want to borrow something. + inner: Option>, + /// The thing borrowing from `inner`. This is what the `Tied` value ultimately dereferences to. + borrow: Option>, +} + +impl Drop for Tied { + fn drop(&mut self) { + // let's be explicit about order here! + std::mem::drop(self.borrow.take()); + } +} + +impl Tied { + /// 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(value: T, producer: F) -> Self + where + F: FnOnce(*mut T) -> Box, + { + 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 { + self.borrow = None; + self.inner.take().unwrap() + } + + pub fn into_inner(self) -> T { + *self.into_boxed_inner() + } +} + +impl AsRef for Tied { + fn as_ref(&self) -> &U { + self.borrow.as_ref().unwrap() + } +} + +impl AsMut for Tied { + fn as_mut(&mut self) -> &mut U { + self.borrow.as_mut().unwrap() + } +} + +impl std::ops::Deref for Tied { + type Target = U; + + fn deref(&self) -> &U { + self.as_ref() + } +} + +impl std::ops::DerefMut for Tied { + fn deref_mut(&mut self) -> &mut U { + self.as_mut() + } +} diff --git a/proxmox-tools/src/lib.rs b/proxmox-tools/src/lib.rs index cd77d975..d00d84d5 100644 --- a/proxmox-tools/src/lib.rs +++ b/proxmox-tools/src/lib.rs @@ -3,6 +3,7 @@ use failure::*; use lazy_static::lazy_static; +pub mod borrow; pub mod common_regex; pub mod fd; pub mod fs;