borrow-or-share-0.2.4/.cargo_vcs_info.json0000644000000001360000000000100140670ustar { "git": { "sha1": "fa67a43ce36367757ace1824434b9edaa53f5a09" }, "path_in_vcs": "" }borrow-or-share-0.2.4/.github/workflows/ci.yml000064400000000000000000000006211046102023000173710ustar 00000000000000on: push: branches: ["main"] name: CI jobs: test: name: Test runs-on: ubuntu-latest env: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v4 - name: Install Rust nightly uses: dtolnay/rust-toolchain@nightly - name: Test with no features run: cargo test - name: Test with all features run: cargo test --all-features borrow-or-share-0.2.4/.gitignore000064400000000000000000000000231046102023000146420ustar 00000000000000/target Cargo.lock borrow-or-share-0.2.4/Cargo.lock0000644000000002370000000000100120440ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "borrow-or-share" version = "0.2.4" borrow-or-share-0.2.4/Cargo.toml0000644000000025150000000000100120700ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.65" name = "borrow-or-share" version = "0.2.4" authors = ["Scallop Ye "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Traits for either borrowing or sharing data." documentation = "https://docs.rs/borrow-or-share" readme = "README.md" keywords = [ "borrow", "share", ] categories = ["rust-patterns"] license = "MIT-0" repository = "https://github.com/yescallop/borrow-or-share" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [features] alloc = [] default = ["alloc"] std = ["alloc"] [lib] name = "borrow_or_share" path = "src/lib.rs" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(no_rc, no_sync, no_global_oom_handling)"] borrow-or-share-0.2.4/Cargo.toml.orig000064400000000000000000000012531046102023000155470ustar 00000000000000[package] name = "borrow-or-share" version = "0.2.4" authors = ["Scallop Ye "] edition = "2021" rust-version = "1.65" description = "Traits for either borrowing or sharing data." documentation = "https://docs.rs/borrow-or-share" repository = "https://github.com/yescallop/borrow-or-share" license = "MIT-0" keywords = ["borrow", "share"] categories = ["rust-patterns"] [features] default = ["alloc"] std = ["alloc"] alloc = [] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(no_rc, no_sync, no_global_oom_handling)'] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] all-features = true rustdoc-args = ["--cfg", "docsrs"] borrow-or-share-0.2.4/LICENSE000064400000000000000000000016101046102023000136620ustar 00000000000000MIT No Attribution Copyright 2024 Scallop Ye Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.borrow-or-share-0.2.4/README.md000064400000000000000000000026621046102023000141440ustar 00000000000000# borrow-or-share Traits for either borrowing or sharing data. [![crates.io](https://img.shields.io/crates/v/borrow-or-share.svg)](https://crates.io/crates/borrow-or-share) [![build](https://img.shields.io/github/actions/workflow/status/yescallop/borrow-or-share/ci.yml )](https://github.com/yescallop/borrow-or-share/actions/workflows/ci.yml) [![license](https://img.shields.io/crates/l/borrow-or-share)](/LICENSE) See below for a basic usage of the crate. See the [documentation](https://docs.rs/borrow-or-share) for a detailed walkthrough. ## Basic usage Suppose that you have a generic type that either owns some data or holds a reference to them. You can use this crate to implement on this type a method taking `&self` that either borrows from `*self` or from behind a reference it holds: ```rust use borrow_or_share::BorrowOrShare; struct Text(T); impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text { fn as_str(&'i self) -> &'o str { self.0.borrow_or_share() } } // The returned reference is borrowed from `*text` // and lives as long as `text`. fn borrow(text: &Text) -> &str { text.as_str() } // The returned reference is borrowed from `*text.0`, lives // longer than `text` and is said to be shared with `*text`. fn share<'a>(text: &Text<&'a str>) -> &'a str { text.as_str() } ``` ## Credit Credit goes to [@beepster4096](https://github.com/beepster4096) for figuring out a safe version of the code. borrow-or-share-0.2.4/src/lib.rs000064400000000000000000000220561046102023000145670ustar 00000000000000#![warn(missing_docs, rust_2018_idioms)] #![forbid(unsafe_code)] #![cfg_attr(docsrs, feature(doc_cfg))] #![no_std] //! Traits for either borrowing or sharing data. //! //! # Walkthrough //! //! Suppose that you have a generic type that either owns some data or holds a reference to them. //! You want to implement on this type a method taking `&self` that either borrows from `*self` //! or from behind a reference it holds. A naive way to do this would be //! to duplicate the method declaration: //! //! ``` //! struct Text(T); //! //! impl Text { //! // The returned reference is borrowed from `*self` //! // and lives as long as `self`. //! fn as_str(&self) -> &str { //! &self.0 //! } //! } //! //! impl<'a> Text<&'a str> { //! // The returned reference is borrowed from `*self.0`, lives //! // longer than `self` and is said to be shared with `*self`. //! fn as_str(&self) -> &'a str { //! self.0 //! } //! } //! ``` //! //! However, if you add more methods to `Text`, the code would become //! intolerably verbose. This crate thus provides a [`BorrowOrShare`] trait //! you can use to simplify the above code by making the `as_str` method //! generic over `T`: //! //! ``` //! use borrow_or_share::BorrowOrShare; //! //! struct Text(T); //! //! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text { //! fn as_str(&'i self) -> &'o str { //! self.0.borrow_or_share() //! } //! } //! //! // The returned reference is borrowed from `*text` //! // and lives as long as `text`. //! fn borrow(text: &Text) -> &str { //! text.as_str() //! } //! //! // The returned reference is borrowed from `*text.0`, lives //! // longer than `text` and is said to be shared with `*text`. //! fn share<'a>(text: &Text<&'a str>) -> &'a str { //! text.as_str() //! } //! ``` //! //! The [`BorrowOrShare`] trait takes two lifetime parameters `'i`, `'o`, //! and a type parameter `T`. For `T = str` it is implemented on `String` //! wherever `'i: 'o`, while on `&'a str` wherever `'a: 'i + 'o`. //! The trait is also implemented on other types, which we'll cover later. //! //! On the trait is a [`borrow_or_share`] method that takes `&'i self` //! and returns `&'o T`. You can use it to write your own //! "data borrowing or sharing" functions. A typical usage would be //! to put a `BorrowOrShare<'i, 'o, str>` bound on a type parameter `T` //! taken by an `impl` block of your type. Within the block, you implement //! a method that takes `&'i self` and returns something with lifetime `'o`, //! by calling the [`borrow_or_share`] method on some `T` //! contained in `self` and further processing the returned `&'o str`. //! //! [`borrow_or_share`]: BorrowOrShare::borrow_or_share //! //! While you're happy with the different behavior of the `as_str` method //! on `Text` (borrowing) and on `Text<&str>` (sharing), you still //! have to fall back on borrowing when dealing with generic `Text`. //! For example, you may want to implement [`AsRef`] on `Text`, //! which requires an `as_ref` method that always borrows from `*self`. //! The code won't compile, however, if you put the same [`BorrowOrShare`] //! bound and write `self.as_str()` in the [`AsRef`] impl: //! //! ```compile_fail //! use borrow_or_share::BorrowOrShare; //! //! struct Text(T); //! //! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text { //! fn as_str(&'i self) -> &'o str { //! self.0.borrow_or_share() //! } //! } //! //! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> AsRef for Text { //! fn as_ref(&self) -> &str { //! self.as_str() //! } //! } //! ``` //! //! The problem is that in the [`AsRef`] impl, the anonymous lifetime //! `'1` of `self` does not satisfy the bounds `'1: 'i` and `'o: '1`. //! The idiomatic solution is to put a [`Bos`] bound instead: //! //! ``` //! use borrow_or_share::{BorrowOrShare, Bos}; //! //! struct Text(T); //! //! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text { //! fn as_str(&'i self) -> &'o str { //! self.0.borrow_or_share() //! } //! } //! //! impl> AsRef for Text { //! fn as_ref(&self) -> &str { //! self.as_str() //! } //! } //! ``` //! //! In the above example, the `as_str` method is also available on `Text` //! where `T: Bos`, because [`BorrowOrShare`] is implemented on //! all types that implement [`Bos`]. It also works the other way round //! because [`Bos`] is a supertrait of [`BorrowOrShare`]. //! //! This crate provides [`Bos`] (and [`BorrowOrShare`]) implementations //! on [`&T`](reference), [`&mut T`](reference), [`[T; N]`](array), //! [`Vec`], [`String`], [`CString`], [`OsString`], [`PathBuf`], //! [`Box`], [`Cow<'_, B>`], [`Rc`], and [`Arc`]. If some of //! these are out of scope, consider putting extra trait bounds in your //! code, preferably on a function that constructs your type. //! //! [`CString`]: alloc::ffi::CString //! [`Cow<'_, B>`]: Cow //! [`Rc`]: alloc::rc::Rc //! [`Arc`]: alloc::sync::Arc //! //! You can also implement [`Bos`] on your own type, for example: //! //! ``` //! use borrow_or_share::Bos; //! //! struct Text<'a>(&'a str); //! //! impl<'a> Bos for Text<'a> { //! type Ref<'this> = &'a str where Self: 'this; //! //! fn borrow_or_share(this: &Self) -> Self::Ref<'_> { //! this.0 //! } //! } //! ``` //! //! # Limitations //! //! This crate only provides implementations of [`Bos`] on types that //! currently implement [`Borrow`] in the standard library, not including //! the blanket implementation. If this is too restrictive, feel free //! to copy the code pattern from this crate as you wish. //! //! [`Borrow`]: core::borrow::Borrow //! //! # Crate features //! //! - `std` (disabled by default): Implies `alloc`. Required for //! [`Bos`] implementations on [`OsString`] and [`PathBuf`]. //! //! - `alloc`: Required for [`Bos`] implementations on [`alloc`] types. #[cfg(any(feature = "alloc", doc))] extern crate alloc; #[cfg(any(feature = "std", doc))] extern crate std; mod internal { pub trait Ref { fn cast<'a>(self) -> &'a T where Self: 'a; } impl Ref for &T { #[inline] fn cast<'a>(self) -> &'a T where Self: 'a, { self } } } use internal::Ref; #[cfg(any(feature = "alloc", doc))] use alloc::{ borrow::{Cow, ToOwned}, boxed::Box, string::String, vec::Vec, }; #[cfg(any(feature = "std", doc))] use std::{ ffi::{OsStr, OsString}, path::{Path, PathBuf}, }; /// A trait for either borrowing or sharing data. /// /// See the [crate-level documentation](crate) for more details. pub trait Bos { /// The resulting reference type. May only be `&T`. type Ref<'this>: Ref where Self: 'this; /// Borrows from `*this` or from behind a reference it holds, /// returning a reference of type [`Self::Ref`]. /// /// In the latter case, the returned reference is said to be *shared* with `*this`. fn borrow_or_share(this: &Self) -> Self::Ref<'_>; } /// A helper trait for writing "data borrowing or sharing" functions. /// /// See the [crate-level documentation](crate) for more details. pub trait BorrowOrShare<'i, 'o, T: ?Sized>: Bos { /// Borrows from `*self` or from behind a reference it holds. /// /// In the latter case, the returned reference is said to be *shared* with `*self`. fn borrow_or_share(&'i self) -> &'o T; } impl<'i, 'o, T: ?Sized, B> BorrowOrShare<'i, 'o, T> for B where B: Bos + ?Sized + 'i, B::Ref<'i>: 'o, { #[inline] fn borrow_or_share(&'i self) -> &'o T { (B::borrow_or_share(self) as B::Ref<'i>).cast() } } impl<'a, T: ?Sized> Bos for &'a T { type Ref<'this> = &'a T where Self: 'this; #[inline] fn borrow_or_share(this: &Self) -> Self::Ref<'_> { this } } macro_rules! impl_bos { ($($(#[$attr:meta])? $({$($params:tt)*})? $ty:ty => $target:ty)*) => { $( $(#[$attr])? impl $(<$($params)*>)? Bos<$target> for $ty { type Ref<'this> = &'this $target where Self: 'this; #[inline] fn borrow_or_share(this: &Self) -> Self::Ref<'_> { this } } )* }; } impl_bos! { // A blanket impl would show up everywhere in the // documentation of a dependent crate, which is noisy. // So we're omitting it for the moment. // {T: ?Sized} T => T {T: ?Sized} &mut T => T {T, const N: usize} [T; N] => [T] #[cfg(feature = "std")] OsString => OsStr #[cfg(feature = "std")] PathBuf => Path } #[cfg(feature = "alloc")] impl_bos! { {T} Vec => [T] String => str #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] alloc::ffi::CString => core::ffi::CStr {T: ?Sized} Box => T {B: ?Sized + ToOwned} Cow<'_, B> => B #[cfg(not(no_rc))] {T: ?Sized} alloc::rc::Rc => T #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] {T: ?Sized} alloc::sync::Arc => T }