From 0bca92fe5b2cb2d60b0cfac9b4a79b410c393623 Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 9 Jan 2026 14:28:10 +0100 Subject: [PATCH 1/4] osi: maybe-owned types Add `osi::mown` including `osi::mown::Mown`, which is a reduced variant of `core::borrow::Cow`, but does not require `ToOwned` or `Clone` to be implemented. Signed-off-by: David Rheinsberg --- lib/osi/src/lib.rs | 1 + lib/osi/src/mown.rs | 213 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 lib/osi/src/mown.rs diff --git a/lib/osi/src/lib.rs b/lib/osi/src/lib.rs index a5ab9ed..b9888a7 100644 --- a/lib/osi/src/lib.rs +++ b/lib/osi/src/lib.rs @@ -34,6 +34,7 @@ pub mod json; pub mod marker; pub mod mem; pub mod meta; +pub mod mown; pub mod never; pub mod pin; pub mod ptr; diff --git a/lib/osi/src/mown.rs b/lib/osi/src/mown.rs new file mode 100644 index 0000000..184a786 --- /dev/null +++ b/lib/osi/src/mown.rs @@ -0,0 +1,213 @@ +//! # Maybe-Owned Type +//! +//! This module provides the `Mown` type. This is a generic type that +//! represents data that is either owned or borrowed. + +/// A *Maybe-Owned* type represents values that are either owned or borrowed. +/// +/// The main motivation of the `Mown` type is to generalize whether an object +/// stores borrowed or owned data. When an object stores a reference to +/// borrowed data, the caller must ensure this data lives long enough. This is +/// often cumbersome since Rust lacks support for self-referential data types. +/// But if the object instead stores a [`Mown`] object, the caller can decide +/// whether to pass in borrowed or owned data. +/// +/// ## Similarity to Cow +/// +/// This type is almost identical to [`Cow`](alloc::borrow::Cow), but does not +/// require [`Clone`](core::clone::Clone) or any kind of support for +/// mutability. +pub enum Mown<'a, B: ?Sized, O = &'a B> { + Borrowed(&'a B), + Owned(O), +} + +impl<'a, B, O> Mown<'a, B, O> +where + B: 'a + ?Sized, +{ + /// Create a new borrowed `Mown`. + pub const fn new_borrowed(v: &'a B) -> Self { + Self::Borrowed(v) + } + + /// Create a new owned `Mown`. + pub const fn new_owned(v: O) -> Self { + Self::Owned(v) + } + + /// Check whether the `Mown` is borrowed. + pub const fn is_borrowed(&self) -> bool { + match *self { + Self::Borrowed(_) => true, + Self::Owned(_) => false, + } + } + + /// Check whether the `Mown` is owned. + pub const fn is_owned(&self) -> bool { + !self.is_borrowed() + } +} + +impl<'a, B, O> Mown<'a, B, O> +where + B: ?Sized, + O: core::borrow::Borrow, +{ + /// Dereference the `Mown` to the borrowed type. + pub fn deref(&self) -> &B { + match *self { + Self::Borrowed(v) => v, + Self::Owned(ref v) => v.borrow(), + } + } +} + +impl<'a, B, O, T> core::convert::AsRef for Mown<'a, B, O> +where + B: ?Sized + core::convert::AsRef, + O: core::borrow::Borrow, + T: ?Sized, +{ + fn as_ref(&self) -> &T { + self.deref().as_ref() + } +} + +impl<'a, B, O> core::clone::Clone for Mown<'a, B, O> +where + B: ?Sized, + O: core::clone::Clone, +{ + fn clone(&self) -> Self { + match *self { + Self::Borrowed(v) => Self::Borrowed(v), + Self::Owned(ref v) => Self::Owned(v.clone()), + } + } +} + +impl<'a, B, O> core::fmt::Debug for Mown<'a, B, O> +where + B: ?Sized + core::fmt::Debug, + O: core::borrow::Borrow, +{ + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + match *self { + Self::Borrowed(v) => fmt.debug_tuple("Mown::Borrowed").field(&v).finish(), + Self::Owned(ref v) => fmt.debug_tuple("Mown::Owned").field(&v.borrow()).finish(), + } + } +} + +impl<'a, B, O> core::default::Default for Mown<'a, B, O> +where + B: ?Sized, + O: core::default::Default, +{ + fn default() -> Self { + Self::new_owned(O::default()) + } +} + +impl<'a, B, O> core::ops::Deref for Mown<'a, B, O> +where + B: ?Sized, + O: core::borrow::Borrow, +{ + type Target = B; + + fn deref(&self) -> &B { + Mown::deref(self) + } +} + +impl<'a, B, O> core::cmp::Eq for Mown<'a, B, O> +where + B: ?Sized + core::cmp::Eq, + O: core::borrow::Borrow, +{ +} + +impl<'a, B, O> core::convert::From<&'a B> for Mown<'a, B, O> +where + B: ?Sized, +{ + fn from(v: &'a B) -> Self { + Self::new_borrowed(v) + } +} + +impl<'a, B, O> core::hash::Hash for Mown<'a, B, O> +where + B: ?Sized + core::hash::Hash, + O: core::borrow::Borrow, +{ + fn hash(&self, state: &mut Op) + where + Op: core::hash::Hasher, + { + (**self).hash(state) + } +} + +impl<'a, B, O> core::cmp::Ord for Mown<'a, B, O> +where + B: ?Sized + core::cmp::Ord, + O: core::borrow::Borrow, +{ + fn cmp(&self, v: &Self) -> core::cmp::Ordering { + (**self).cmp(v) + } +} + +impl<'a, B, O> core::cmp::PartialEq for Mown<'a, B, O> +where + B: ?Sized + core::cmp::PartialEq, + O: core::borrow::Borrow, +{ + fn eq(&self, v: &Self) -> bool { + (**self).eq(v) + } +} + +impl<'a, B, O> core::cmp::PartialOrd for Mown<'a, B, O> +where + B: ?Sized + core::cmp::PartialOrd, + O: core::borrow::Borrow, +{ + fn partial_cmp(&self, v: &Self) -> Option { + (**self).partial_cmp(v) + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::string::String; + + // Verify basic behavior of `Mown`. + #[test] + fn basic() { + // Verify that niches are used by `Mown`. Since `String` simply wraps + // `Vec`, we know that it has a non-zero-annotation. Thus the `Mown` + // enum must be able to re-use the nieche bits. + assert_eq!( + core::mem::size_of::>(), + core::mem::size_of::(), + ); + + // Create owned and borrowed variants and compare them. + let b = Mown::::new_borrowed("foobar"); + let o = Mown::::new_owned("foobar".into()); + assert_eq!(b, o); + assert_eq!(&*b, &*o); + assert_eq!(&*b, "foobar"); + assert_eq!(&*o, "foobar"); + assert!(b.is_borrowed()); + assert!(!o.is_borrowed()); + assert!(!b.is_owned()); + assert!(o.is_owned()); + } +} From cc4283c12a059bdf3b3c0b1070826a92e88af1bc Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 9 Jan 2026 14:29:37 +0100 Subject: [PATCH 2/4] osi: document rust wishlist Document the features we would like to see in upstream Rust. Signed-off-by: David Rheinsberg --- lib/osi/src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/osi/src/lib.rs b/lib/osi/src/lib.rs index b9888a7..c3a9eca 100644 --- a/lib/osi/src/lib.rs +++ b/lib/osi/src/lib.rs @@ -5,6 +5,36 @@ //! particular runtime, but can optionally be combined with the Rust Standard //! Library. +// MSRV(unknown): This is a wish-list for Rust features that either have no +// clear path to stabilization, or just do not exist. Only features +// relevant to the development of this project are listed. +// +// - rustdoc self-type: Jumping to the documentation of a method in rustdoc +// html renderings does not show the self-type. It is often cumbersome to +// scroll up to the exact place where the `impl` is shown. It would be nice +// if the self-type was shown, or there was another way to quickly jump to +// it via an anchor. +// +// - rustfmt optout: There is no global opt-out for rustfmt. While individual +// items can be annotated with `#[rustfmt::skip]`, the root module of a +// crate cannot be annotated like this (NB: inner attributes like +// `#![rustfmt::skip]` are not stable). +// While projects can decide to not run `rustfmt`, it would be nice to +// annotate the code-base so IDEs will also not format the code +// automatically. +// +// - maybe-owned types: When objects borrow data, the data cannot live in the +// same object, since Rust does not allow self-referential types. A common +// workaround is to make such objects own the data instead, but this is +// sub-optimal and leads to unneeded copies. It would be nice to have some +// official support to represent data that can be either owned or borrowed, +// similar to `core::borrow::Cow`, but without the requirement for `Clone`. +// +// - memchr(), memmem(): While strings have two-way search support in the Rust +// standard library, no such features are exposed for searching u8. Given +// that these can be greatly optimized by the compiler, they seem a worthy +// fit for the standard library. + #![allow(clippy::assertions_on_constants)] #![allow(clippy::identity_op)] #![allow(clippy::manual_range_contains)] From bceb3cfd96ad67734bf03e70fbcfa7e2450c4988 Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 9 Jan 2026 14:43:57 +0100 Subject: [PATCH 3/4] ci: do not pass `--all-features` We use features sparely, so avoid passing `all-features`. This can break the builds if features require extra setups. Signed-off-by: David Rheinsberg --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03642eb..774265c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: uses: actions/checkout@v4 - name: "Run Analyzer" - run: cargo check --all-features --all-targets --verbose --workspace + run: cargo check --all-targets --features libc --verbose --workspace # # A complete but basic build of the project, running on common x86-64 ubuntu @@ -89,7 +89,7 @@ jobs: uses: actions/checkout@v4 - name: "Build and Test" - run: cargo test --all-features --all-targets --verbose --workspace + run: cargo test --all-targets --features libc --verbose --workspace # # A simple no-op job that serves as guard. All extended jobs depend on this @@ -124,7 +124,7 @@ jobs: uses: actions/checkout@v4 - name: "Build and Test" - run: cargo test --all-features --all-targets --verbose --workspace + run: cargo test --all-targets --features libc --verbose --workspace # # A matrix of project builds with different settings. All builds run the @@ -146,8 +146,8 @@ jobs: # Explicitly set all options here to document them. cargoargs: >- - --all-features --all-targets + --features libc --profile=release image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest" toolchain: stable @@ -157,8 +157,8 @@ jobs: name: "Ubuntu-x86_64-nightly-release" cargoargs: >- - --all-features --all-targets + --features libc --profile=release image: "ghcr.io/readaheadeu/rae-ci-ubuntu:latest" toolchain: nightly @@ -204,8 +204,8 @@ jobs: run: | cargo \ test \ - --all-features \ --all-targets \ + --features libc \ --target i686-unknown-linux-gnu \ --verbose \ --workspace From 0ac57af0f3527367bca89378134f7e9657163c5e Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 9 Jan 2026 14:54:44 +0100 Subject: [PATCH 4/4] sys/ffi/linux/native: allow empty fallback Ensure that clippy does not warn about an empty fallback. Signed-off-by: David Rheinsberg --- lib/sys/src/ffi/linux/native/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sys/src/ffi/linux/native/mod.rs b/lib/sys/src/ffi/linux/native/mod.rs index 0f59261..3d6b5a5 100644 --- a/lib/sys/src/ffi/linux/native/mod.rs +++ b/lib/sys/src/ffi/linux/native/mod.rs @@ -31,4 +31,5 @@ osi::cfg::cond! { }, } +#[allow(unused)] pub use inner::*;