Skip to content

Commit a70e279

Browse files
committed
Add Ptr::try_with_{as_ref,as_mut_unchecked}
A common operation is to try a fallible `Ptr` conversion and then dereference the `Ptr` to a `&` or `&mut` in both the success and error cases. These functions handle the dereferencing boilerplate. gherrit-pr-id: G7ef173122e7ebd6e3ce2659d432363d56972eb88
1 parent 34f572d commit a70e279

File tree

6 files changed

+116
-56
lines changed

6 files changed

+116
-56
lines changed

src/error.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ use core::{
125125
#[cfg(all(no_zerocopy_core_error_1_81_0, any(feature = "std", test)))]
126126
use std::error::Error;
127127

128-
use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
128+
use crate::{
129+
pointer::TryWithError, util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned,
130+
};
129131
#[cfg(doc)]
130132
use crate::{FromBytes, Ref};
131133

@@ -355,6 +357,16 @@ impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> {
355357

356358
impl<Src: Eq, Dst: ?Sized> Eq for AlignmentError<Src, Dst> {}
357359

360+
// SAFETY: `AlignmentError` contains a single `Self::Inner = Src`, and no other
361+
// non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`.
362+
unsafe impl<Src, NewSrc, Dst: ?Sized> TryWithError<NewSrc> for AlignmentError<Src, Dst> {
363+
type Inner = Src;
364+
type Mapped = AlignmentError<NewSrc, Dst>;
365+
fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
366+
self.map_src(f)
367+
}
368+
}
369+
358370
impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
359371
#[inline(always)]
360372
fn from(_: AlignmentError<Src, Dst>) -> Infallible {
@@ -655,13 +667,12 @@ impl<Src: Clone, Dst: ?Sized + TryFromBytes> Clone for ValidityError<Src, Dst> {
655667

656668
// SAFETY: `ValidityError` contains a single `Self::Inner = Src`, and no other
657669
// non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`.
658-
unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc>
659-
for crate::ValidityError<Src, Dst>
670+
unsafe impl<Src, NewSrc, Dst> TryWithError<NewSrc> for ValidityError<Src, Dst>
660671
where
661672
Dst: TryFromBytes + ?Sized,
662673
{
663674
type Inner = Src;
664-
type Mapped = crate::ValidityError<NewSrc, Dst>;
675+
type Mapped = ValidityError<NewSrc, Dst>;
665676
fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
666677
self.map_src(f)
667678
}
@@ -793,12 +804,12 @@ impl<Src, Dst: ?Sized> CastError<Src, Dst> {
793804
// `SizeError`. In either case, it contains a single `Self::Inner = Src`, and no
794805
// other non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner`
795806
// to `f`.
796-
unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc> for crate::CastError<Src, Dst>
807+
unsafe impl<Src, NewSrc, Dst> TryWithError<NewSrc> for CastError<Src, Dst>
797808
where
798809
Dst: ?Sized,
799810
{
800811
type Inner = Src;
801-
type Mapped = crate::CastError<NewSrc, Dst>;
812+
type Mapped = CastError<NewSrc, Dst>;
802813

803814
fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped {
804815
self.map_src(f)

src/lib.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3775,10 +3775,9 @@ pub unsafe trait FromBytes: FromZeros {
37753775
Self: KnownLayout + Immutable,
37763776
{
37773777
static_assert_dst_is_not_zst!(Self);
3778-
match Ptr::from_ref(source).try_cast_into_no_leftover::<_, BecauseImmutable>(None) {
3779-
Ok(ptr) => Ok(ptr.recall_validity().as_ref()),
3780-
Err(err) => Err(err.map_src(|src| src.as_ref())),
3781-
}
3778+
Ptr::from_ref(source).try_with_as_ref(|ptr| {
3779+
ptr.try_cast_into_no_leftover::<_, BecauseImmutable>(None).map(Ptr::recall_validity)
3780+
})
37823781
}
37833782

37843783
/// Interprets the prefix of the given `source` as a `&Self` without
@@ -4011,9 +4010,12 @@ pub unsafe trait FromBytes: FromZeros {
40114010
Self: IntoBytes + KnownLayout,
40124011
{
40134012
static_assert_dst_is_not_zst!(Self);
4014-
match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) {
4015-
Ok(ptr) => Ok(ptr.recall_validity::<_, (_, (_, _))>().as_mut()),
4016-
Err(err) => Err(err.map_src(|src| src.as_mut())),
4013+
// SAFETY: TODO
4014+
unsafe {
4015+
Ptr::from_mut(source).try_with_as_mut_unchecked(|ptr| {
4016+
ptr.try_cast_into_no_leftover::<_, BecauseExclusive>(None)
4017+
.map(Ptr::recall_validity::<_, (_, (_, _))>)
4018+
})
40174019
}
40184020
}
40194021

@@ -4248,12 +4250,10 @@ pub unsafe trait FromBytes: FromZeros {
42484250
where
42494251
Self: KnownLayout<PointerMetadata = usize> + Immutable,
42504252
{
4251-
let source = Ptr::from_ref(source);
4252-
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
4253-
match maybe_slf {
4254-
Ok(slf) => Ok(slf.recall_validity().as_ref()),
4255-
Err(err) => Err(err.map_src(|s| s.as_ref())),
4256-
}
4253+
Ptr::from_ref(source).try_with_as_ref(|ptr| {
4254+
ptr.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count))
4255+
.map(Ptr::recall_validity)
4256+
})
42574257
}
42584258

42594259
/// Interprets the prefix of the given `source` as a DST `&Self` with length
@@ -4479,11 +4479,12 @@ pub unsafe trait FromBytes: FromZeros {
44794479
where
44804480
Self: IntoBytes + KnownLayout<PointerMetadata = usize> + Immutable,
44814481
{
4482-
let source = Ptr::from_mut(source);
4483-
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
4484-
match maybe_slf {
4485-
Ok(slf) => Ok(slf.recall_validity::<_, (_, (_, BecauseExclusive))>().as_mut()),
4486-
Err(err) => Err(err.map_src(|s| s.as_mut())),
4482+
// SAFETY: TODO
4483+
unsafe {
4484+
Ptr::from_mut(source).try_with_as_mut_unchecked(|ptr| {
4485+
ptr.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count))
4486+
.map(Ptr::recall_validity::<_, (_, (_, BecauseExclusive))>)
4487+
})
44874488
}
44884489
}
44894490

src/pointer/ptr.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ mod _external {
160160
/// Methods for converting to and from `Ptr` and Rust's safe reference types.
161161
mod _conversions {
162162
use super::*;
163-
use crate::pointer::cast::{CastExact, CastSized, IdCast};
163+
use crate::{
164+
pointer::cast::{CastExact, CastSized, IdCast},
165+
Unalign,
166+
};
164167

165168
/// `&'a T` → `Ptr<'a, T>`
166169
impl<'a, T> Ptr<'a, T, (Shared, Aligned, Valid)>
@@ -371,9 +374,10 @@ mod _conversions {
371374
}
372375

373376
/// `Ptr<'a, T>` → `&'a mut T`
374-
impl<'a, T> Ptr<'a, T, (Exclusive, Aligned, Valid)>
377+
impl<'a, T, I> Ptr<'a, T, I>
375378
where
376379
T: 'a + ?Sized,
380+
I: Invariants<Aliasing = Exclusive, Alignment = Aligned, Validity = Valid>,
377381
{
378382
/// Converts `self` to a mutable reference.
379383
#[allow(clippy::wrong_self_convention)]
@@ -548,7 +552,7 @@ mod _conversions {
548552
/// `Unalign<T>`.
549553
pub(crate) fn into_unalign(
550554
self,
551-
) -> Ptr<'a, crate::Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
555+
) -> Ptr<'a, Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
552556
// FIXME(#1359): This should be a `transmute_with` call.
553557
// Unfortunately, to avoid blanket impl conflicts, we only implement
554558
// `TransmuteFrom<T>` for `Unalign<T>` (and vice versa) specifically
@@ -985,6 +989,63 @@ mod _casts {
985989
}
986990
}
987991

992+
impl<'a, T, I> Ptr<'a, T, I>
993+
where
994+
T: 'a + ?Sized,
995+
I: Invariants<Aliasing = Shared, Alignment = Aligned, Validity = Valid>,
996+
{
997+
/// Like [`try_with`], but returns a reference instead of a pointer
998+
/// in both the success and error cases.
999+
pub(crate) fn try_with_as_ref<U, J, E, F>(
1000+
self,
1001+
f: F,
1002+
) -> Result<&'a U, <E::Mapped as TryWithError<&'a T>>::Mapped>
1003+
where
1004+
U: 'a + ?Sized,
1005+
J: Invariants<Aliasing = Shared, Alignment = Aligned, Validity = Valid>,
1006+
E: TryWithError<Self>,
1007+
E::Mapped: TryWithError<&'a T, Inner = Self>,
1008+
F: FnOnce(Ptr<'a, T, I>) -> Result<Ptr<'a, U, J>, E>,
1009+
{
1010+
match self.try_with(f) {
1011+
Ok(ptr) => Ok(ptr.as_ref()),
1012+
Err(err) => Err(TryWithError::map(err, Ptr::as_ref)),
1013+
}
1014+
}
1015+
}
1016+
1017+
impl<'a, T, I> Ptr<'a, T, I>
1018+
where
1019+
T: 'a + ?Sized,
1020+
I: Invariants<Aliasing = Exclusive, Alignment = Aligned, Validity = Valid>,
1021+
{
1022+
/// Like [`try_with_unchecked`], but returns a mutable reference instead
1023+
/// of a pointer in both the success and error cases.
1024+
///
1025+
/// # Safety
1026+
///
1027+
/// If `f` returns `Err(err)`, no copy of `f`'s argument must exist
1028+
/// outside of `err`.
1029+
pub(crate) unsafe fn try_with_as_mut_unchecked<U, J, E, F>(
1030+
self,
1031+
f: F,
1032+
) -> Result<&'a mut U, <E::Mapped as TryWithError<&'a mut T>>::Mapped>
1033+
where
1034+
U: 'a + ?Sized,
1035+
J: Invariants<Aliasing = Exclusive, Alignment = Aligned, Validity = Valid>,
1036+
E: TryWithError<Self>,
1037+
E::Mapped: TryWithError<&'a mut T, Inner = Self>,
1038+
F: FnOnce(Ptr<'a, T, I>) -> Result<Ptr<'a, U, J>, E>,
1039+
{
1040+
// SAFETY: The caller promises that, if `f` returns `Err(err)`, no
1041+
// copy of `f`'s argument exists outside of `err`.
1042+
match unsafe { self.try_with_unchecked(f) } {
1043+
Ok(ptr) => Ok(ptr.as_mut()),
1044+
Err(err) => Err(TryWithError::map(err, Ptr::as_mut)),
1045+
}
1046+
}
1047+
}
1048+
9881049
/// # Safety
9891050
///
9901051
/// `Self` only contains a single `Self::Inner`, and `Self::Mapped` only

src/split_at.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ where
700700
// SAFETY: `self.source.as_mut()` points to exactly the same referent as
701701
// `self.source` and thus maintains the invariants of `self` with
702702
// respect to `l_len`.
703-
unsafe { Split::new(self.source.unify_invariants().as_mut(), self.l_len) }
703+
unsafe { Split::new(self.source.as_mut(), self.l_len) }
704704
}
705705

706706
/// Produces the length of `self`'s left part.

src/util/macro_util.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -665,21 +665,16 @@ where
665665
{
666666
let ptr = Ptr::from_ref(src);
667667
#[rustfmt::skip]
668-
let res = ptr.try_with(#[inline(always)] |ptr| {
668+
return ptr.try_with_as_ref(#[inline(always)] |ptr| {
669669
let ptr = ptr.recall_validity::<Initialized, _>();
670670
let ptr = ptr.cast::<_, CastSized, _>();
671-
ptr.try_into_valid()
672-
});
673-
match res {
674-
Ok(ptr) => {
671+
ptr.try_into_valid().map(#[inline(always)] |ptr| {
675672
static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>());
676673
// SAFETY: We have checked that `Dst` does not have a stricter
677674
// alignment requirement than `Src`.
678-
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
679-
Ok(ptr.as_ref())
680-
}
681-
Err(err) => Err(err.map_src(Ptr::as_ref)),
682-
}
675+
unsafe { ptr.assume_alignment::<Aligned>() }
676+
})
677+
});
683678
}
684679

685680
/// Attempts to transmute `&mut Src` into `&mut Dst`.
@@ -703,23 +698,18 @@ where
703698
let ptr = Ptr::from_mut(src);
704699
// SAFETY: The provided closure returns the only copy of `ptr`.
705700
#[rustfmt::skip]
706-
let res = unsafe {
707-
ptr.try_with_unchecked(#[inline(always)] |ptr| {
701+
return unsafe {
702+
ptr.try_with_as_mut_unchecked(#[inline(always)] |ptr| {
708703
let ptr = ptr.recall_validity::<Initialized, (_, (_, _))>();
709704
let ptr = ptr.cast::<_, CastSized, _>();
710-
ptr.try_into_valid()
705+
ptr.try_into_valid().map(#[inline(always)] |ptr| {
706+
static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>());
707+
// SAFETY: We have checked that `Dst` does not have a stricter
708+
// alignment requirement than `Src`.
709+
unsafe { ptr.assume_alignment::<Aligned>() }
710+
})
711711
})
712712
};
713-
match res {
714-
Ok(ptr) => {
715-
static_assert!(Src, Dst => mem::align_of::<Dst>() <= mem::align_of::<Src>());
716-
// SAFETY: We have checked that `Dst` does not have a stricter
717-
// alignment requirement than `Src`.
718-
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
719-
Ok(ptr.as_mut())
720-
}
721-
Err(err) => Err(err.map_src(Ptr::as_mut)),
722-
}
723713
}
724714

725715
// Used in `transmute_ref!` and friends.

src/wrappers.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,8 @@ impl<T> Unalign<T> {
198198
/// may prefer [`Deref::deref`], which is infallible.
199199
#[inline(always)]
200200
pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> {
201-
let inner = Ptr::from_ref(self).transmute();
202-
match inner.try_into_aligned() {
203-
Ok(aligned) => Ok(aligned.as_ref()),
204-
Err(err) => Err(err.map_src(|src| src.into_unalign().as_ref())),
205-
}
201+
let ptr = Ptr::from_ref(self);
202+
ptr.try_with_as_ref(|ptr| ptr.transmute().try_into_aligned())
206203
}
207204

208205
/// Attempts to return a mutable reference to the wrapped `T`, failing if

0 commit comments

Comments
 (0)