Crate borrow_or_share
source ·Expand description
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>(T);
impl Text<String> {
// 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>(T);
impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
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<String>) -> &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
.
While you’re happy with the different behavior of the as_str
method
on Text<String>
(borrowing) and on Text<&str>
(sharing), you still
have to fall back on borrowing when dealing with generic Text<T>
.
For example, you may want to implement AsRef
on Text<T>
,
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:
use borrow_or_share::BorrowOrShare;
struct Text<T>(T);
impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
fn as_str(&'i self) -> &'o str {
self.0.borrow_or_share()
}
}
impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> AsRef<str> for Text<T> {
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>(T);
impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> Text<T> {
fn as_str(&'i self) -> &'o str {
self.0.borrow_or_share()
}
}
impl<T: Bos<str>> AsRef<str> for Text<T> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
In the above example, the as_str
method is also available on Text<T>
where T: Bos<str>
, 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
, &mut T
, [T; N]
,
Vec<T>
, String
, CString
, OsString
, PathBuf
,
Box<T>
, Cow<'_, B>
, Rc<T>
, and Arc<T>
. If some of
these are out of scope, consider putting extra trait bounds in your
code, preferably on a function that constructs your type.
You can also implement Bos
on your own type, for example:
use borrow_or_share::Bos;
struct Text<'a>(&'a str);
impl<'a> Bos<str> 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.
§Crate features
Traits§
- A helper trait for writing “data borrowing or sharing” functions.
- A trait for either borrowing or sharing data.