Skip to content

Commit 0ee6d47

Browse files
committed
add by-reference implementations for Rc and Arc
1 parent dd0846e commit 0ee6d47

File tree

2 files changed

+187
-1
lines changed

2 files changed

+187
-1
lines changed

library/alloc/src/rc.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,11 @@ use core::intrinsics::abort;
252252
#[cfg(not(no_global_oom_handling))]
253253
use core::iter;
254254
use core::marker::{PhantomData, Unsize};
255-
use core::mem::{self, ManuallyDrop, align_of_val_raw};
255+
use core::mem::{self, ManuallyDrop, MaybeUninit, align_of_val_raw};
256256
use core::num::NonZeroUsize;
257257
use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver};
258+
#[cfg(not(no_global_oom_handling))]
259+
use core::ops::{Residual, Try};
258260
use core::panic::{RefUnwindSafe, UnwindSafe};
259261
#[cfg(not(no_global_oom_handling))]
260262
use core::pin::Pin;
@@ -640,6 +642,95 @@ impl<T> Rc<T> {
640642
pub fn pin(value: T) -> Pin<Rc<T>> {
641643
unsafe { Pin::new_unchecked(Rc::new(value)) }
642644
}
645+
646+
/// Maps the value in an `Rc`, reusing the allocation if possible.
647+
///
648+
/// `f` is called on a reference to the value in the `Rc`, and the result is returned, also in
649+
/// an `Rc`.
650+
///
651+
/// Note: this is an associated function, which means that you have
652+
/// to call it as `Rc::map(&r, f)` instead of `r.map(f)`. This
653+
/// is so that there is no conflict with a method on the inner type.
654+
///
655+
/// # Examples
656+
///
657+
/// ```
658+
/// #![feature(smart_pointer_try_map)]
659+
///
660+
/// use std::rc::Rc;
661+
///
662+
/// let r = Rc::new(7);
663+
/// let new = Rc::map(r, |i| i + 7);
664+
/// assert_eq!(*new, 14);
665+
/// ```
666+
#[cfg(not(no_global_oom_handling))]
667+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
668+
pub fn map<U>(this: Self, f: impl FnOnce(&T) -> U) -> Rc<U> {
669+
if size_of::<T>() == size_of::<U>()
670+
&& align_of::<T>() == align_of::<U>()
671+
&& Rc::is_unique(&this)
672+
{
673+
unsafe {
674+
let ptr = Rc::into_raw(this);
675+
let value = ptr.read();
676+
let mut allocation = Rc::from_raw(ptr.cast::<MaybeUninit<U>>());
677+
678+
Rc::get_mut_unchecked(&mut allocation).write(f(&value));
679+
allocation.assume_init()
680+
}
681+
} else {
682+
Rc::new(f(&*this))
683+
}
684+
}
685+
686+
/// Attempts to map the value in an `Rc`, reusing the allocation if possible.
687+
///
688+
/// `f` is called on a reference to the value in the box, and if the operation succeeds, the
689+
/// result is returned, also in an `Rc`.
690+
///
691+
/// Note: this is an associated function, which means that you have
692+
/// to call it as `Rc::try_map(&r, f)` instead of `r.try_map(f)`. This
693+
/// is so that there is no conflict with a method on the inner type.
694+
///
695+
/// # Examples
696+
///
697+
/// ```
698+
/// #![feature(smart_pointer_try_map)]
699+
///
700+
/// use std::rc::Rc;
701+
///
702+
/// let b = Rc::new(7);
703+
/// let new = Rc::try_map(b, |&i| u32::try_from(i)).unwrap();
704+
/// assert_eq!(*new, 7);
705+
/// ```
706+
#[cfg(not(no_global_oom_handling))]
707+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
708+
pub fn try_map<R>(
709+
this: Self,
710+
f: impl FnOnce(&T) -> R,
711+
) -> <R::Residual as Residual<Rc<R::Output>>>::TryType
712+
where
713+
R: Try,
714+
R::Residual: Residual<Rc<R::Output>>,
715+
{
716+
if size_of::<T>() == size_of::<R::Output>()
717+
&& align_of::<T>() == align_of::<R::Output>()
718+
&& Rc::is_unique(&this)
719+
{
720+
unsafe {
721+
let ptr = Rc::into_raw(this);
722+
let value = ptr.read();
723+
let mut allocation = Rc::from_raw(ptr.cast::<MaybeUninit<R::Output>>());
724+
725+
Rc::get_mut_unchecked(&mut allocation).write(f(&value)?);
726+
<R::Residual as Residual<Rc<R::Output>>>::TryType::from_output(
727+
allocation.assume_init(),
728+
)
729+
}
730+
} else {
731+
<R::Residual as Residual<Rc<R::Output>>>::TryType::from_output(Rc::new(f(&*this)?))
732+
}
733+
}
643734
}
644735

645736
impl<T, A: Allocator> Rc<T, A> {

library/alloc/src/sync.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use core::marker::{PhantomData, Unsize};
2121
use core::mem::{self, ManuallyDrop, align_of_val_raw};
2222
use core::num::NonZeroUsize;
2323
use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver};
24+
#[cfg(not(no_global_oom_handling))]
25+
use core::ops::{Residual, Try};
2426
use core::panic::{RefUnwindSafe, UnwindSafe};
2527
use core::pin::{Pin, PinCoerceUnsized};
2628
use core::ptr::{self, NonNull};
@@ -646,6 +648,99 @@ impl<T> Arc<T> {
646648
)?))
647649
}
648650
}
651+
652+
/// Maps the value in an `Arc`, reusing the allocation if possible.
653+
///
654+
/// `f` is called on a reference to the value in the `Arc`, and the result is returned, also in
655+
/// an `Arc`.
656+
///
657+
/// Note: this is an associated function, which means that you have
658+
/// to call it as `Arc::map(&a, f)` instead of `r.map(a)`. This
659+
/// is so that there is no conflict with a method on the inner type.
660+
///
661+
/// # Examples
662+
///
663+
/// ```
664+
/// #![feature(smart_pointer_try_map)]
665+
///
666+
/// use std::sync::Arc;
667+
///
668+
/// let r = Arc::new(7);
669+
/// let new = Arc::map(r, |i| i + 7);
670+
/// assert_eq!(*new, 14);
671+
/// ```
672+
#[cfg(not(no_global_oom_handling))]
673+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
674+
pub fn map<U>(this: Self, f: impl FnOnce(&T) -> U) -> Arc<U> {
675+
if size_of::<T>() == size_of::<U>()
676+
&& align_of::<T>() == align_of::<U>()
677+
&& Arc::is_unique(&this)
678+
{
679+
unsafe {
680+
use core::mem::MaybeUninit;
681+
682+
let ptr = Arc::into_raw(this);
683+
let value = ptr.read();
684+
let mut allocation = Arc::from_raw(ptr.cast::<MaybeUninit<U>>());
685+
686+
Arc::get_mut_unchecked(&mut allocation).write(f(&value));
687+
allocation.assume_init()
688+
}
689+
} else {
690+
Arc::new(f(&*this))
691+
}
692+
}
693+
694+
/// Attempts to map the value in an `Arc`, reusing the allocation if possible.
695+
///
696+
/// `f` is called on a reference to the value in the box, and if the operation succeeds, the
697+
/// result is returned, also in an `Arc`.
698+
///
699+
/// Note: this is an associated function, which means that you have
700+
/// to call it as `Arc::try_map(&a, f)` instead of `a.try_map(f)`. This
701+
/// is so that there is no conflict with a method on the inner type.
702+
///
703+
/// # Examples
704+
///
705+
/// ```
706+
/// #![feature(smart_pointer_try_map)]
707+
///
708+
/// use std::sync::Arc;
709+
///
710+
/// let b = Arc::new(7);
711+
/// let new = Arc::try_map(b, |&i| u32::try_from(i)).unwrap();
712+
/// assert_eq!(*new, 7);
713+
/// ```
714+
#[cfg(not(no_global_oom_handling))]
715+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
716+
pub fn try_map<R>(
717+
this: Self,
718+
f: impl FnOnce(&T) -> R,
719+
) -> <R::Residual as Residual<Arc<R::Output>>>::TryType
720+
where
721+
R: Try,
722+
R::Residual: Residual<Arc<R::Output>>,
723+
{
724+
if size_of::<T>() == size_of::<R::Output>()
725+
&& align_of::<T>() == align_of::<R::Output>()
726+
&& Arc::is_unique(&this)
727+
{
728+
unsafe {
729+
use core::mem::MaybeUninit;
730+
731+
let ptr = Arc::into_raw(this);
732+
let value = ptr.read();
733+
let mut allocation = Arc::from_raw(ptr.cast::<MaybeUninit<R::Output>>());
734+
735+
Arc::get_mut_unchecked(&mut allocation).write(f(&value)?);
736+
<R::Residual as Residual<Arc<R::Output>>>::TryType::from_output(
737+
allocation.assume_init(),
738+
)
739+
}
740+
} else {
741+
<R::Residual as Residual<Arc<R::Output>>>::TryType::from_output(Arc::new(f(&*this)?))
742+
}
743+
}
649744
}
650745

651746
impl<T, A: Allocator> Arc<T, A> {

0 commit comments

Comments
 (0)