Skip to content

Commit

Permalink
0.3.0
Browse files Browse the repository at this point in the history
- Change the API of `try_*` functions on `Parc` and `Prc` to take
  functions returning `Result` instead of `Option` and make them return
  `Result` instead of `Option`.
  • Loading branch information
radekvit committed Apr 21, 2024
1 parent 266d73f commit 65bbc62
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 44 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.3.0
- Change the API of `try_*` functions on `Parc` and `Prc` to take functions returning `Result` instead of `Option` and make them return `Result` instead of `Option`.

## 0.2.3
- Factor out the vtable to be shared between the `sync` and `prc` modules, as the type definition is identical
- Fix tests that should fail and check that we're failing for the right reason on nightly
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pared"
version = "0.2.3"
version = "0.3.0"
authors = ["Radek Vít <radekvitr@gmail.com>"]
edition = "2021"
rust-version = "1.56"
Expand Down
24 changes: 12 additions & 12 deletions src/prc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,24 @@ impl<T: ?Sized> Prc<T> {
///
/// let rc = Rc::new(Enum::Int(5));
/// let prc = Prc::try_from_rc(&rc, |x| match x {
/// Enum::Str(s) => None,
/// Enum::Int(i) => Some(i),
/// Enum::Str(s) => Err(()),
/// Enum::Int(i) => Ok(i),
/// });
///
/// assert!(matches!(prc, Some(prc) if *prc == 5 ));
/// assert!(matches!(prc, Ok(prc) if *prc == 5 ));
/// ```
#[inline]
pub fn try_from_rc<U, F>(rc: &Rc<U>, project: F) -> Option<Self>
pub fn try_from_rc<U, E, F>(rc: &Rc<U>, project: F) -> Result<Self, E>
where
U: ?Sized,
T: 'static,
F: FnOnce(&U) -> Option<&T>,
F: FnOnce(&U) -> Result<&T, E>,
{
let projected = project(rc)?;
// SAFETY: fn shouldn't be able to capture any local references
// which should mean that the projection done by f is safe
let projected = unsafe { NonNull::new_unchecked(projected as *const T as *mut T) };
Some(Self {
Ok(Self {
rc: TypeErasedRc::new(rc.clone()),
projected,
})
Expand Down Expand Up @@ -271,23 +271,23 @@ impl<T: ?Sized> Prc<T> {
///
/// let prc = Prc::new(Enum::Int(5));
/// let projected = prc.try_project(|x| match x {
/// Enum::Str(s) => None,
/// Enum::Int(i) => Some(i),
/// Enum::Str(s) => Err(()),
/// Enum::Int(i) => Ok(i),
/// });
///
/// assert!(matches!(projected, Some(p) if *p == 5 ));
/// assert!(matches!(projected, Ok(p) if *p == 5 ));
/// ```
#[inline]
pub fn try_project<U, F>(&self, project: F) -> Option<Prc<U>>
pub fn try_project<U, E, F>(&self, project: F) -> Result<Prc<U>, E>
where
U: ?Sized + 'static,
F: FnOnce(&T) -> Option<&U>,
F: FnOnce(&T) -> Result<&U, E>,
{
let projected = project(self)?;
// SAFETY: fn shouldn't be able to capture any local references
// which should mean that the projection done by f is safe
let projected = unsafe { NonNull::new_unchecked(projected as *const U as *mut U) };
Some(Prc::<U> {
Ok(Prc::<U> {
rc: self.rc.clone(),
projected,
})
Expand Down
33 changes: 17 additions & 16 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ impl<T: ?Sized> Parc<T> {
}
}

/// Constructs a new `Option<Parc<T>>` from an existing `Arc<T>` by trying to project a field.
/// Constructs a new `Result<Parc<T>, E>` from an existing `Arc<T>`
/// by trying to project a field.
///
/// If the function passed into this returns `None`, this method will also return `None`.
/// If the function passed into this returns `Err(x)`, this method will also return `Err(x)`.
///
/// # Panics
/// If `f` panics, the panic is propagated to the caller and the rc won't be cloned.
Expand All @@ -242,24 +243,24 @@ impl<T: ?Sized> Parc<T> {
///
/// let arc = Arc::new(Enum::Int(5));
/// let parc = Parc::try_from_arc(&arc, |x| match x {
/// Enum::Str(s) => None,
/// Enum::Int(i) => Some(i),
/// Enum::Str(s) => Err(()),
/// Enum::Int(i) => Ok(i),
/// });
///
/// assert!(matches!(parc, Some(parc) if *parc == 5 ));
/// assert!(matches!(parc, Ok(parc) if *parc == 5 ));
/// ```
#[inline]
pub fn try_from_arc<U, F>(arc: &Arc<U>, project: F) -> Option<Self>
pub fn try_from_arc<U, E, F>(arc: &Arc<U>, project: F) -> Result<Self, E>
where
U: ?Sized + Sync + Send,
T: 'static,
F: FnOnce(&U) -> Option<&T>,
F: FnOnce(&U) -> Result<&T, E>,
{
let projected = project(arc)?;
// SAFETY: fn shouldn't be able to capture any local references
// which should mean that the projection done by f is safe
let projected = unsafe { NonNull::new_unchecked(projected as *const T as *mut T) };
Some(Self {
Ok(Self {
arc: TypeErasedArc::new(arc.clone()),
projected,
})
Expand Down Expand Up @@ -303,10 +304,10 @@ impl<T: ?Sized> Parc<T> {
}
}

/// Constructs a new `Option<Parc<T>>` from an existing `Parc<T>`
/// Constructs a new `Result<Parc<T>, E>` from an existing `Parc<T>`
/// by trying to projecting a field.
///
/// If the function passed into this returns `None`, this method will also return `None`.
/// If the function passed into this returns `Err(x)`, this method will also return `Err(x)`.
///
/// # Panics
/// If `f` panics, the panic is propagated to the caller and the underlying rc won't be cloned.
Expand All @@ -322,23 +323,23 @@ impl<T: ?Sized> Parc<T> {
///
/// let prc = Parc::new(Enum::Int(5));
/// let projected = prc.try_project(|x| match x {
/// Enum::Str(s) => None,
/// Enum::Int(i) => Some(i),
/// Enum::Str(s) => Err(()),
/// Enum::Int(i) => Ok(i),
/// });
///
/// assert!(matches!(projected, Some(p) if *p == 5 ));
/// assert!(matches!(projected, Ok(p) if *p == 5 ));
/// ```
pub fn try_project<U, F>(&self, project: F) -> Option<Parc<U>>
pub fn try_project<U, E, F>(&self, project: F) -> Result<Parc<U>, E>
where
T: Send + Sync,
U: ?Sized + 'static,
F: for<'x> FnOnce(&'x T) -> Option<&'x U>,
F: for<'x> FnOnce(&'x T) -> Result<&'x U, E>,
{
let projected = project(self)?;
// SAFETY: fn shouldn't be able to capture any local references
// which should mean that the projection done by f is safe
let projected = unsafe { NonNull::new_unchecked(projected as *const U as *mut U) };
Some(Parc::<U> {
Ok(Parc::<U> {
arc: self.arc.clone(),
projected,
})
Expand Down
14 changes: 7 additions & 7 deletions tests/parc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,28 +130,28 @@ fn fallible_projections() {
B,
}

fn try_project(t: &Test) -> Option<&str> {
fn try_project(t: &Test) -> Result<&str, ()> {
match t {
Test::A(s) => Some(s),
Test::B => None,
Test::A(s) => Ok(s),
Test::B => Err(()),
}
}

let arc = Arc::new(Test::B);
let parc = Parc::try_from_arc(&arc, try_project);
assert!(parc.is_none());
assert!(parc.is_err());

let parc = Parc::new(Test::B);
let parc = parc.try_project(try_project);
assert!(parc.is_none());
assert!(parc.is_err());

let arc = Arc::new(Test::A("Hi!".to_owned()));
let parc = Parc::try_from_arc(&arc, try_project);
assert!(matches!(parc, Some(p) if &*p == "Hi!"));
assert!(matches!(parc, Ok(p) if &*p == "Hi!"));

let parc = Parc::new(Test::A("Hi!".to_owned()));
let parc = parc.try_project(try_project);
assert!(matches!(parc, Some(p) if &*p == "Hi!"));
assert!(matches!(parc, Ok(p) if &*p == "Hi!"));
}

#[test]
Expand Down
14 changes: 7 additions & 7 deletions tests/prc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,28 +131,28 @@ fn fallible_projections() {
B,
}

fn try_project(t: &Test) -> Option<&str> {
fn try_project(t: &Test) -> Result<&str, ()> {
match t {
Test::A(s) => Some(s),
Test::B => None,
Test::A(s) => Ok(s),
Test::B => Err(()),
}
}

let rc = Rc::new(Test::B);
let prc = Prc::try_from_rc(&rc, try_project);
assert!(prc.is_none());
assert!(prc.is_err());

let prc = Prc::new(Test::B);
let prc = prc.try_project(try_project);
assert!(prc.is_none());
assert!(prc.is_err());

let rc = Rc::new(Test::A("Hi!".to_owned()));
let prc = Prc::try_from_rc(&rc, try_project);
assert!(matches!(prc, Some(p) if &*p == "Hi!"));
assert!(matches!(prc, Ok(p) if &*p == "Hi!"));

let prc = Prc::new(Test::A("Hi!".to_owned()));
let prc = prc.try_project(try_project);
assert!(matches!(prc, Some(p) if &*p == "Hi!"));
assert!(matches!(prc, Ok(p) if &*p == "Hi!"));
}

#[test]
Expand Down

0 comments on commit 65bbc62

Please sign in to comment.