Skip to content

Commit

Permalink
Add new traits (MoveRef, MoveMut and Move)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuguzT committed Aug 10, 2023
1 parent d149f6c commit c2e9644
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 81 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ref_kind"
version = "0.5.0"
version = "0.5.1"
description = "Different reference kinds"
authors = ["tuguzT <timurka.tugushev@gmail.com>"]
repository = "https://github.com/toucan-games/ref_kind"
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ All of them represented in one enum `RefKind`, which allows to store immutable a
But the most importantly, this crate allows to retrieve **many** mutable references
out of the collection by creating a new collection which holds these references.

For that very case, crate defines `Many` trait which is implemented
for peekable iterators, slices and other common collections.
For that very case, crate defines some useful traits:

But nothing stops you to implement this trait for other collections as well!
- `MoveRef` and `MoveMut` for containers to retrieve corresponding kind of reference,
- `Move` as a combination of the traits above,
- `Many` for collections which is implemented for peekable iterators, slices and so on.

But nothing stops you to implement these traits for other types as well!

## Example

Expand Down
2 changes: 1 addition & 1 deletion src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloc_crate::{
vec::Vec,
};

use crate::many::{Many, Result};
use crate::{Many, Result};

/// Implementation of [`Many`] trait for [`Vec`].
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
Expand Down
2 changes: 1 addition & 1 deletion src/hashbrown.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::hash::{BuildHasher, Hash};
use hashbrown::HashMap;

use crate::many::{Many, Result};
use crate::{Many, Result};

/// Implementation of [`Many`] trait for [`hashbrown::HashMap`].
#[cfg_attr(docsrs, doc(cfg(feature = "hashbrown")))]
Expand Down
2 changes: 1 addition & 1 deletion src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::iter::Peekable;

use crate::many::{Many, Result};
use crate::{Many, Result};

/// Type of key for peekable iterator.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down
16 changes: 10 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
//! But the most importantly, this crate allows to retrieve **many** mutable references
//! out of the collection by creating a new collection which holds these references.
//!
//! For that very case, crate defines [`Many`] trait which is implemented
//! for peekable iterators, [slices] and other common collections.
//! For that very case, crate defines some useful traits:
//! - [`MoveRef`] and [`MoveMut`] for containers to retrieve corresponding kind of reference,
//! - [`Move`] as a combination of the traits above,
//! - [`Many`] for collections which is implemented for peekable iterators, [slices] and so on.
//!
//! But nothing stops you to implement this trait for other collections as well!
//! But nothing stops you to implement these traits for other types as well!
//!
//! ## Example
//!
Expand Down Expand Up @@ -95,8 +97,10 @@ extern crate alloc as alloc_crate;
extern crate std as std_crate;

pub use self::{
kind::RefKind::{self, Mut, Ref},
many::{Many, MoveError, Result},
kind::RefKind,
many::Many,
r#move::{Move, MoveError, MoveMut, MoveRef, Result},
RefKind::{Mut, Ref},
};

pub mod iter;
Expand All @@ -107,7 +111,7 @@ mod alloc;
mod hashbrown;
mod kind;
mod many;
mod option;
mod r#move;
mod slice;
#[cfg(feature = "std")]
mod std;
35 changes: 3 additions & 32 deletions src/many.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use core::fmt::{Display, Formatter};
use crate::{MoveError, Result};

/// Trait for collections which hold different kinds of reference.
///
/// This trait provides methods for retrieving references (either immutable or mutable)
/// by moving them out of the collection to preserve the lifetime of the owner.
///
/// This is useful when it is needed to get **many** mutable references
/// on different elements of the owner collection.
/// See [crate documentation](crate) for a detailed explanation and an example.
///
/// This trait is usually implemented for collections of `Option<RefKind<'a, T>>` elements
/// which allows for the implementation to replace [`Some`] with [`None`] when moving out of the collection.
/// See [crate documentation](crate) for details.
pub trait Many<'a, Key> {
/// The type of a reference which is being moved out.
type Ref: 'a;
Expand Down Expand Up @@ -57,34 +56,6 @@ pub trait Many<'a, Key> {
}
}

/// The result of [`Many`] trait method calls.
pub type Result<T> = core::result::Result<T, MoveError>;

/// Enum that defines errors which can occur when moving reference
/// out of the collection which implements [`Many`] trait.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum MoveError {
/// Reference was already moved out of the collection as immutable.
/// It is not allowed to get mutable reference again, but it is allowed to get immutable one.
BorrowedImmutably,
/// Reference was already moved out of the collection as mutable.
/// It is not allowed to get neither immutable nor mutable reference again.
BorrowedMutably,
}

impl Display for MoveError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::BorrowedImmutably => write!(f, "reference was already borrowed immutably"),
Self::BorrowedMutably => write!(f, "reference was already borrowed mutably"),
}
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std_crate::error::Error for MoveError {}

#[cold]
#[track_caller]
fn move_panic(error: MoveError) -> ! {
Expand Down
27 changes: 27 additions & 0 deletions src/move/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// The result of moving reference out of the value.
pub type Result<T> = core::result::Result<T, MoveError>;

/// Enum that defines errors which can occur when moving reference
/// out of the value.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum MoveError {
/// Reference was already moved out of the collection as immutable.
/// It is not allowed to get mutable reference again, but it is allowed to get immutable one.
BorrowedImmutably,
/// Reference was already moved out of the collection as mutable.
/// It is not allowed to get neither immutable nor mutable reference again.
BorrowedMutably,
}

impl core::fmt::Display for MoveError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::BorrowedImmutably => write!(f, "reference was already borrowed immutably"),
Self::BorrowedMutably => write!(f, "reference was already borrowed mutably"),
}
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std_crate::error::Error for MoveError {}
11 changes: 11 additions & 0 deletions src/move/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub use self::{
error::{MoveError, Result},
move_mut::MoveMut,
move_ref::MoveRef,
r#move::Move,
};

mod error;
mod r#move;
mod move_mut;
mod move_ref;
32 changes: 32 additions & 0 deletions src/move/move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![allow(clippy::module_inception)]

use crate::{Many, Result};

use super::{MoveMut, MoveRef};

/// Trait for containers which hold *different* kinds of reference.
///
/// Combines [`MoveRef`] and [`MoveMut`] functionality together,
/// allowing to move immutable and mutable references from the same container.
pub trait Move<'owner>: MoveRef<'owner> + MoveMut<'owner> {}

/// Technically, `Move` is a trait alias to `MoveRef + MoveMut` trait combination.
impl<'owner, T> Move<'owner> for T where T: ?Sized + MoveRef<'owner> + MoveMut<'owner> {}

/// [`Many`] trait can be implemented for any type which implements [`Move`] trait for any key.
impl<'owner, T, K> Many<'owner, K> for T
where
T: ?Sized + Move<'owner>,
{
type Ref = <Self as MoveRef<'owner>>::Ref;

fn try_move_ref(&mut self, _: K) -> Result<Self::Ref> {
MoveRef::move_ref(self)
}

type Mut = <Self as MoveMut<'owner>>::Mut;

fn try_move_mut(&mut self, _: K) -> Result<Self::Mut> {
MoveMut::move_mut(self)
}
}
55 changes: 55 additions & 0 deletions src/move/move_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::{Mut, Ref, RefKind};

use super::{MoveError, Result};

/// Trait for containers which hold *mutable* kind of reference.
///
/// This trait provides method for retrieving a mutable reference
/// by moving it out of the container to preserve the lifetime of the owner.
///
/// This is useful when it is needed to get **many** mutable references
/// on different elements of the owner collection.
///
/// See [crate documentation](crate) for details.
pub trait MoveMut<'owner> {
/// Type of a mutable reference which is being moved out.
type Mut: 'owner;

/// Tries to move a mutable reference out of the container.
fn move_mut(&mut self) -> Result<Self::Mut>;
}

/// Mutable reference should be moved out of the [`Option`].
impl<'owner, T> MoveMut<'owner> for Option<&'owner mut T>
where
T: ?Sized,
{
type Mut = &'owner mut T;

fn move_mut(&mut self) -> Result<Self::Mut> {
let unique = self.take().ok_or(MoveError::BorrowedMutably)?;
Ok(unique)
}
}

/// Mutable reference should be moved out of the optional [`RefKind`]
/// if the kind of reference is mutable.
impl<'owner, T> MoveMut<'owner> for Option<RefKind<'owner, T>>
where
T: ?Sized,
{
type Mut = &'owner mut T;

fn move_mut(&mut self) -> Result<Self::Mut> {
let kind = self.take().ok_or(MoveError::BorrowedMutably)?;

let unique = match kind {
Ref(shared) => {
*self = Some(Ref(shared));
return Err(MoveError::BorrowedImmutably);
}
Mut(unique) => unique,
};
Ok(unique)
}
}
80 changes: 80 additions & 0 deletions src/move/move_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{Ref, RefKind};

use super::{MoveError, Result};

/// Trait for containers which hold *immutable* kind of reference.
///
/// This trait provides method for retrieving an immutable reference
/// by moving it out of the container to preserve the lifetime of the owner.
///
/// This is useful when it is needed to get **many** mutable references
/// on different elements of the owner collection.
///
/// See [crate documentation](crate) for details.
pub trait MoveRef<'owner> {
/// Type of an immutable reference which is being moved out.
type Ref: 'owner;

/// Tries to move an immutable reference out of the container.
///
/// This function can copy an immutable reference or replace mutable reference with immutable one,
/// preserving an immutable reference in the container.
fn move_ref(&mut self) -> Result<Self::Ref>;
}

/// Immutable reference can be trivially copied.
impl<'owner, T> MoveRef<'owner> for &'owner T
where
T: ?Sized,
{
type Ref = &'owner T;

fn move_ref(&mut self) -> Result<Self::Ref> {
Ok(self)
}
}

/// Optional immutable reference can be trivially copied.
impl<'owner, T> MoveRef<'owner> for Option<&'owner T>
where
T: ?Sized,
{
type Ref = &'owner T;

fn move_ref(&mut self) -> Result<Self::Ref> {
let shared = self.ok_or(MoveError::BorrowedImmutably)?;
Ok(shared)
}
}

/// Mutable reference should be moved out of the [`Option`]
/// and coerced into immutable one.
impl<'owner, T> MoveRef<'owner> for Option<&'owner mut T>
where
T: ?Sized,
{
type Ref = &'owner T;

fn move_ref(&mut self) -> Result<Self::Ref> {
let unique = self.take().ok_or(MoveError::BorrowedMutably)?;
Ok(unique)
}
}

/// To move immutable reference out of the optional [RefKind],
/// it should copy an immutable reference or replace mutable reference with immutable one,
/// preserving an immutable reference in the container.
impl<'owner, T> MoveRef<'owner> for Option<RefKind<'owner, T>>
where
T: ?Sized,
{
type Ref = &'owner T;

fn move_ref(&mut self) -> Result<Self::Ref> {
let kind = self.take().ok_or(MoveError::BorrowedMutably)?;

let shared = kind.into_ref();
*self = Some(Ref(shared));
Ok(shared)
}
}
34 changes: 0 additions & 34 deletions src/option.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/slice.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::many::{Many, Result};
use crate::{Many, Result};

/// Implementation of [`Many`] trait for [slice](prim@slice).
impl<'a, T> Many<'a, usize> for [T]
Expand Down
Loading

0 comments on commit c2e9644

Please sign in to comment.