Skip to content

Commit 41ce8ad

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

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

library/alloc/src/rc.rs

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

645738
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)