borrow_or_share/
lib.rs

1#![warn(missing_docs, rust_2018_idioms)]
2#![forbid(unsafe_code)]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4#![no_std]
5
6//! Traits for either borrowing or sharing data.
7//!
8//! # Walkthrough
9//!
10//! Suppose that you have a generic type that either owns some data or holds a reference to them.
11//! You want to implement on this type a method taking `&self` that either borrows from `*self`
12//! or from behind a reference it holds. A naive way to do this would be
13//! to duplicate the method declaration:
14//!
15//! ```
16//! struct Text<T>(T);
17//!
18//! impl Text<String> {
19//!     // The returned reference is borrowed from `*self`
20//!     // and lives as long as `self`.
21//!     fn as_str(&self) -> &str {
22//!         &self.0
23//!     }
24//! }
25//!
26//! impl<'a> Text<&'a str> {
27//!     // The returned reference is borrowed from `*self.0`, lives
28//!     // longer than `self` and is said to be shared with `*self`.
29//!     fn as_str(&self) -> &'a str {
30//!         self.0
31//!     }
32//! }
33//! ```
34//!
35//! However, if you add more methods to `Text`, the code would become
36//! intolerably verbose. This crate thus provides a [`BorrowOrShare`] trait
37//! you can use to simplify the above code by making the `as_str` method
38//! generic over `T`:
39//!
40//! ```
41//! use borrow_or_share::BorrowOrShare;
42//!
43//! struct Text<T>(T);
44//!
45//! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
46//!     fn as_str(&'i self) -> &'o str {
47//!         self.0.borrow_or_share()
48//!     }
49//! }
50//!
51//! // The returned reference is borrowed from `*text`
52//! // and lives as long as `text`.
53//! fn borrow(text: &Text<String>) -> &str {
54//!     text.as_str()
55//! }
56//!
57//! // The returned reference is borrowed from `*text.0`, lives
58//! // longer than `text` and is said to be shared with `*text`.
59//! fn share<'a>(text: &Text<&'a str>) -> &'a str {
60//!     text.as_str()
61//! }
62//! ```
63//!
64//! The [`BorrowOrShare`] trait takes two lifetime parameters `'i`, `'o`,
65//! and a type parameter `T`. For `T = str` it is implemented on `String`
66//! wherever `'i: 'o`, while on `&'a str` wherever `'a: 'i + 'o`.
67//! The trait is also implemented on other types, which we'll cover later.
68//!
69//! On the trait is a [`borrow_or_share`] method that takes `&'i self`
70//! and returns `&'o T`. You can use it to write your own
71//! "data borrowing or sharing" functions. A typical usage would be
72//! to put a `BorrowOrShare<'i, 'o, str>` bound on a type parameter `T`
73//! taken by an `impl` block of your type. Within the block, you implement
74//! a method that takes `&'i self` and returns something with lifetime `'o`,
75//! by calling the [`borrow_or_share`] method on some `T`
76//! contained in `self` and further processing the returned `&'o str`.
77//!
78//! [`borrow_or_share`]: BorrowOrShare::borrow_or_share
79//!
80//! While you're happy with the different behavior of the `as_str` method
81//! on `Text<String>` (borrowing) and on `Text<&str>` (sharing), you still
82//! have to fall back on borrowing when dealing with generic `Text<T>`.
83//! For example, you may want to implement [`AsRef`] on `Text<T>`,
84//! which requires an `as_ref` method that always borrows from `*self`.
85//! The code won't compile, however, if you put the same [`BorrowOrShare`]
86//! bound and write `self.as_str()` in the [`AsRef`] impl:
87//!
88//! ```compile_fail
89//! use borrow_or_share::BorrowOrShare;
90//!
91//! struct Text<T>(T);
92//!
93//! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
94//!     fn as_str(&'i self) -> &'o str {
95//!         self.0.borrow_or_share()
96//!     }
97//! }
98//!
99//! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> AsRef<str> for Text<T> {
100//!     fn as_ref(&self) -> &str {
101//!         self.as_str()
102//!     }
103//! }
104//! ```
105//!
106//! The problem is that in the [`AsRef`] impl, the anonymous lifetime
107//! `'1` of `self` does not satisfy the bounds `'1: 'i` and `'o: '1`.
108//! The idiomatic solution is to put a [`Bos`] bound instead:
109//!
110//! ```
111//! use borrow_or_share::{BorrowOrShare, Bos};
112//!
113//! struct Text<T>(T);
114//!
115//! impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
116//!     fn as_str(&'i self) -> &'o str {
117//!         self.0.borrow_or_share()
118//!     }
119//! }
120//!
121//! impl<T: Bos<str>> AsRef<str> for Text<T> {
122//!     fn as_ref(&self) -> &str {
123//!         self.as_str()
124//!     }
125//! }
126//! ```
127//!
128//! In the above example, the `as_str` method is also available on `Text<T>`
129//! where `T: Bos<str>`, because [`BorrowOrShare`] is implemented on
130//! all types that implement [`Bos`]. It also works the other way round
131//! because [`Bos`] is a supertrait of [`BorrowOrShare`].
132//!
133//! This crate provides [`Bos`] (and [`BorrowOrShare`]) implementations
134//! on [`&T`](reference), [`&mut T`](reference), [`[T; N]`](array),
135//! [`Vec<T>`], [`String`], [`CString`], [`OsString`], [`PathBuf`],
136//! [`Box<T>`], [`Cow<'_, B>`], [`Rc<T>`], and [`Arc<T>`]. If some of
137//! these are out of scope, consider putting extra trait bounds in your
138//! code, preferably on a function that constructs your type.
139//!
140//! [`Cow<'_, B>`]: Cow
141//!
142//! You can also implement [`Bos`] on your own type, for example:
143//!
144//! ```
145//! use borrow_or_share::Bos;
146//!
147//! struct Text<'a>(&'a str);
148//!
149//! impl<'a> Bos<str> for Text<'a> {
150//!     type Ref<'this> = &'a str where Self: 'this;
151//!     
152//!     fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
153//!         this.0
154//!     }
155//! }
156//! ```
157//!
158//! # Limitations
159//!
160//! This crate only provides implementations of [`Bos`] on types that
161//! currently implement [`Borrow`] in the standard library, not including
162//! the blanket implementation. If this is too restrictive, feel free
163//! to copy the code pattern from this crate as you wish.
164//!
165//! [`Borrow`]: core::borrow::Borrow
166//!
167//! # Crate features
168//!
169//! - `std` (disabled by default): Enables [`Bos`] implementations on
170//!   [`OsString`] and [`PathBuf`].
171
172extern crate alloc;
173#[cfg(any(feature = "std", doc))]
174extern crate std;
175
176mod internal {
177    pub trait Ref<T: ?Sized> {
178        fn cast<'a>(self) -> &'a T
179        where
180            Self: 'a;
181    }
182
183    impl<T: ?Sized> Ref<T> for &T {
184        #[inline]
185        fn cast<'a>(self) -> &'a T
186        where
187            Self: 'a,
188        {
189            self
190        }
191    }
192}
193
194use alloc::{
195    borrow::{Cow, ToOwned},
196    boxed::Box,
197    ffi::CString,
198    rc::Rc,
199    string::String,
200    sync::Arc,
201    vec::Vec,
202};
203use core::ffi::CStr;
204use internal::Ref;
205
206#[cfg(any(feature = "std", doc))]
207use std::{
208    ffi::{OsStr, OsString},
209    path::{Path, PathBuf},
210};
211
212/// A trait for either borrowing or sharing data.
213///
214/// See the [crate-level documentation](crate) for more details.
215pub trait Bos<T: ?Sized> {
216    /// The resulting reference type. May only be `&T`.
217    type Ref<'this>: Ref<T>
218    where
219        Self: 'this;
220
221    /// Borrows from `*this` or from behind a reference it holds,
222    /// returning a reference of type [`Self::Ref`].
223    ///
224    /// In the latter case, the returned reference is said to be *shared* with `*this`.
225    fn borrow_or_share(this: &Self) -> Self::Ref<'_>;
226}
227
228/// A helper trait for writing "data borrowing or sharing" functions.
229///
230/// See the [crate-level documentation](crate) for more details.
231pub trait BorrowOrShare<'i, 'o, T: ?Sized>: Bos<T> {
232    /// Borrows from `*self` or from behind a reference it holds.
233    ///
234    /// In the latter case, the returned reference is said to be *shared* with `*self`.
235    fn borrow_or_share(&'i self) -> &'o T;
236}
237
238impl<'i, 'o, T: ?Sized, B> BorrowOrShare<'i, 'o, T> for B
239where
240    B: Bos<T> + ?Sized + 'i,
241    B::Ref<'i>: 'o,
242{
243    #[inline]
244    fn borrow_or_share(&'i self) -> &'o T {
245        (B::borrow_or_share(self) as B::Ref<'i>).cast()
246    }
247}
248
249impl<'a, T: ?Sized> Bos<T> for &'a T {
250    type Ref<'this> = &'a T where Self: 'this;
251
252    #[inline]
253    fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
254        this
255    }
256}
257
258macro_rules! impl_bos {
259    ($($(#[$attr:meta])? $({$($params:tt)*})? $ty:ty => $target:ty)*) => {
260        $(
261            $(#[$attr])?
262            impl $(<$($params)*>)? Bos<$target> for $ty {
263                type Ref<'this> = &'this $target where Self: 'this;
264
265                #[inline]
266                fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
267                    this
268                }
269            }
270        )*
271    };
272}
273
274impl_bos! {
275    // A blanket impl would show up everywhere in the
276    // documentation of a dependent crate, which is noisy.
277    // So we're omitting it for the moment.
278    // {T: ?Sized} T => T
279
280    {T: ?Sized} &mut T => T
281
282    {T, const N: usize} [T; N] => [T]
283    {T} Vec<T> => [T]
284
285    String => str
286    CString => CStr
287
288    #[cfg(feature = "std")]
289    OsString => OsStr
290    #[cfg(feature = "std")]
291    PathBuf => Path
292
293    {T: ?Sized} Box<T> => T
294    {B: ?Sized + ToOwned} Cow<'_, B> => B
295
296    {T: ?Sized} Rc<T> => T
297    {T: ?Sized} Arc<T> => T
298}