Skip to content

Commit d445318

Browse files
authored
Merge pull request #177 from andersk/filter_map
Add `GcCellRef::filter_map`, `GcCellRefMut::filter_map`
2 parents 8dd9cf5 + 2bbea85 commit d445318

File tree

1 file changed

+107
-21
lines changed

1 file changed

+107
-21
lines changed

gc/src/lib.rs

+107-21
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::cmp::Ordering;
1616
use std::fmt::{self, Debug, Display};
1717
use std::hash::{Hash, Hasher};
1818
use std::marker::PhantomData;
19-
use std::mem;
19+
use std::mem::{self, ManuallyDrop};
2020
use std::ops::{Deref, DerefMut};
2121
use std::ptr::{self, NonNull};
2222
use std::rc::Rc;
@@ -194,9 +194,7 @@ impl<T: ?Sized> Gc<T> {
194194
/// unsafe { Gc::from_raw(x_ptr) };
195195
/// ```
196196
pub fn into_raw(this: Self) -> *const T {
197-
let ptr: *const T = GcBox::value_ptr(this.inner_ptr());
198-
mem::forget(this);
199-
ptr
197+
GcBox::value_ptr(ManuallyDrop::new(this).inner_ptr())
200198
}
201199

202200
/// Constructs an `Gc` from a raw pointer.
@@ -747,7 +745,7 @@ impl<'a, T: ?Sized> GcCellRef<'a, T> {
747745
}
748746
}
749747

750-
/// Makes a new `GcCellRef` from a component of the borrowed data.
748+
/// Makes a new `GcCellRef` for a component of the borrowed data.
751749
///
752750
/// The `GcCell` is already immutably borrowed, so this cannot fail.
753751
///
@@ -763,24 +761,65 @@ impl<'a, T: ?Sized> GcCellRef<'a, T> {
763761
/// let c = GcCell::new((5, 'b'));
764762
/// let b1: GcCellRef<(u32, char)> = c.borrow();
765763
/// let b2: GcCellRef<u32> = GcCellRef::map(b1, |t| &t.0);
766-
/// //assert_eq!(b2, 5);
764+
/// assert_eq!(*b2, 5);
767765
/// ```
768766
#[inline]
769767
pub fn map<U, F>(orig: Self, f: F) -> GcCellRef<'a, U>
770768
where
771769
U: ?Sized,
772770
F: FnOnce(&T) -> &U,
773771
{
774-
let ret = GcCellRef {
775-
flags: orig.flags,
776-
value: f(orig.value),
777-
};
772+
let value = f(orig.value);
778773

779774
// We have to tell the compiler not to call the destructor of GcCellRef,
780775
// because it will update the borrow flags.
781-
std::mem::forget(orig);
776+
let orig = ManuallyDrop::new(orig);
782777

783-
ret
778+
GcCellRef {
779+
flags: orig.flags,
780+
value,
781+
}
782+
}
783+
784+
/// Makes a new `GcCellRef` for an optional component of the borrowed data.
785+
/// The original guard is returned as an `Err(..)` if the closure returns
786+
/// `None`.
787+
///
788+
/// The `GcCell` is already immutably borrowed, so this cannot fail.
789+
///
790+
/// This is an associated function that needs to be used as
791+
/// `GcCellRef::filter_map(...)`. A method would interfere with methods of
792+
/// the same name on the contents of a `GcCellRef` used through `Deref`.
793+
///
794+
/// # Examples
795+
///
796+
/// ```
797+
/// use gc::{GcCell, GcCellRef};
798+
///
799+
/// let c = GcCell::new(vec![1, 2, 3]);
800+
/// let b1: GcCellRef<Vec<u32>> = c.borrow();
801+
/// let b2: Result<GcCellRef<u32>, _> = GcCellRef::filter_map(b1, |v| v.get(1));
802+
/// assert_eq!(*b2.unwrap(), 2);
803+
/// ```
804+
#[inline]
805+
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<GcCellRef<'a, U>, Self>
806+
where
807+
U: ?Sized,
808+
F: FnOnce(&T) -> Option<&U>,
809+
{
810+
match f(orig.value) {
811+
None => Err(orig),
812+
Some(value) => {
813+
// We have to tell the compiler not to call the destructor of GcCellRef,
814+
// because it will update the borrow flags.
815+
let orig = ManuallyDrop::new(orig);
816+
817+
Ok(GcCellRef {
818+
flags: orig.flags,
819+
value,
820+
})
821+
}
822+
}
784823
}
785824

786825
/// Splits a `GcCellRef` into multiple `GcCellRef`s for different components of the borrowed data.
@@ -812,7 +851,11 @@ impl<'a, T: ?Sized> GcCellRef<'a, T> {
812851

813852
orig.flags.set(orig.flags.get().add_reading());
814853

815-
let ret = (
854+
// We have to tell the compiler not to call the destructor of GcCellRef,
855+
// because it will update the borrow flags.
856+
let orig = ManuallyDrop::new(orig);
857+
858+
(
816859
GcCellRef {
817860
flags: orig.flags,
818861
value: a,
@@ -821,13 +864,7 @@ impl<'a, T: ?Sized> GcCellRef<'a, T> {
821864
flags: orig.flags,
822865
value: b,
823866
},
824-
);
825-
826-
// We have to tell the compiler not to call the destructor of GcCellRef,
827-
// because it will update the borrow flags.
828-
std::mem::forget(orig);
829-
830-
ret
867+
)
831868
}
832869
}
833870

@@ -869,7 +906,7 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> {
869906
/// Makes a new `GcCellRefMut` for a component of the borrowed data, e.g., an enum
870907
/// variant.
871908
///
872-
/// The `GcCellRefMut` is already mutably borrowed, so this cannot fail.
909+
/// The `GcCell` is already mutably borrowed, so this cannot fail.
873910
///
874911
/// This is an associated function that needs to be used as
875912
/// `GcCellRefMut::map(...)`. A method would interfere with methods of the same
@@ -909,6 +946,55 @@ impl<'a, T: Trace + ?Sized, U: ?Sized> GcCellRefMut<'a, T, U> {
909946
value: f(value),
910947
}
911948
}
949+
950+
/// Makes a new `GcCellRefMut` for an optional component of the borrowed
951+
/// data. The original guard is returned as an `Err(..)` if the closure
952+
/// returns `None`.
953+
///
954+
/// The `GcCell` is already mutably borrowed, so this cannot fail.
955+
///
956+
/// This is an associated function that needs to be used as
957+
/// `GcCellRefMut::filter_map(...)`. A method would interfere with methods
958+
/// of the same name on the contents of a `GcCell` used through `Deref`.
959+
///
960+
/// # Examples
961+
///
962+
/// ```
963+
/// use gc::{GcCell, GcCellRefMut};
964+
///
965+
/// let c = GcCell::new(vec![1, 2, 3]);
966+
///
967+
/// {
968+
/// let b1: GcCellRefMut<Vec<u32>> = c.borrow_mut();
969+
/// let mut b2: Result<GcCellRefMut<Vec<u32>, u32>, _> = GcCellRefMut::filter_map(b1, |v| v.get_mut(1));
970+
///
971+
/// if let Ok(mut b2) = b2 {
972+
/// *b2 += 2;
973+
/// }
974+
/// }
975+
///
976+
/// assert_eq!(*c.borrow(), vec![1, 4, 3]);
977+
/// ```
978+
#[inline]
979+
pub fn filter_map<V, F>(orig: Self, f: F) -> Result<GcCellRefMut<'a, T, V>, Self>
980+
where
981+
V: ?Sized,
982+
F: FnOnce(&mut U) -> Option<&mut V>,
983+
{
984+
let gc_cell = orig.gc_cell;
985+
986+
// Use MaybeUninit to avoid calling the destructor of
987+
// GcCellRefMut (which would update the borrow flags) and to
988+
// avoid duplicating the mutable reference orig.value (which
989+
// would be UB).
990+
let orig = mem::MaybeUninit::new(orig);
991+
let value = unsafe { ptr::addr_of!((*orig.as_ptr()).value).read() };
992+
993+
match f(value) {
994+
None => Err(unsafe { orig.assume_init() }),
995+
Some(value) => Ok(GcCellRefMut { gc_cell, value }),
996+
}
997+
}
912998
}
913999

9141000
impl<'a, T: Trace + ?Sized, U: ?Sized> Deref for GcCellRefMut<'a, T, U> {

0 commit comments

Comments
 (0)