3 个不稳定版本
0.2.0 | 2023 年 1 月 9 日 |
---|---|
0.1.1 | 2023 年 1 月 3 日 |
0.1.0 | 2023 年 1 月 2 日 |
#2758 在 Rust 模式
在 im-rope 中使用
29KB
393 行
static-cow
此 Rust 包提供了一种特性框架,用于编写泛型内容所有权的类型,通过将 Cow
升级到类型级别,可以通过泛型类型参数指定特定对象是借用还是拥有。
文档
请参阅 docs.rs 上的 API 文档。
许可证
本项目根据 Apache License 2.0 以及 LLVM 异常 许可。除非你明确说明,否则你提交给 static-cow
的任何有意贡献,都应按照 Apache 2.0 以及 LLVM 异常许可,不附加任何额外条款或条件。
lib.rs
:
此包提供了一种特性框架,用于编写泛型内容所有权的类型。
API 概述
ToOwning
和 IntoOwning
ToOwning
和 IntoOwning
是本包提供的最通用特性,是你将在自己的类型上实现的特性。ToOwning
是 std::borrow::ToOwned
的一般化。
pub trait ToOwning {
type Owning;
fn to_owning(&self) -> Self::Owning;
}
与 ToOwned
不同,它不需要满足 Owning: Borrow<Self>
的条件。因此,ToOwning
代表了一种可以转换为自己拥有内容的版本,但不一定允许你从拥有者中获取原始借用类型的引用的类型。
ToOwning
对 T where T : ToOwned + ?Sized
提供了一个泛型实现。这个泛型实现执行了显然的操作,让 Owning = Owned
和 to_owning = to_owned
。
IntoOwning
的声明很直观
pub trait IntoOwning ToOwning + Sized {
fn into_owning(self) -> Self::Owning;
}
IntoOwning
对 T where T : Clone
提供了一个泛型实现,使得 into_owning
成为一个恒等函数。因此,如果你的类型已经实现了 Clone
,你将自动获得 IntoOwning
的实现。如果你手动实现 IntoOwning
,则不能实现 Clone
。
实现了 ToOwning
和 IntoOwning
的用户定义类型通常只需在其每个字段上调用 .to_owning()
和 .into_owning()
即可。最终会有衍生宏来实现这一点,但我还没有写。
StaticCow
StaticCow
,这个包的名字,是将 std::borrow::Cow
提升到类型级别。虽然 Cow
是一个枚举,但 StaticCow
是一个特质。虽然 Cow::Borrowed
和 Cow::Owned
是枚举的变体,但这个包的 Borrowed
和 Owned
是实现了 StaticCow
的元组结构(因此 Cow
也不例外)。因此,你不需要有字段 field: Cow<'a, B>
的结构体,你可以声明该字段为 field: S
并让 S
成为泛型参数 S: StaticCow<B>
。然后,当在编译时已知 S
的所有权时,编译器可以生成适当特化的函数版本。
与 Cow
类似,StaticCow
需要满足 B : ToOwned
,这使得它可以拥有 Deref<Target=B>
的超级特性。 IntoOwning
是 StaticCow
的另一个超级特性。
幂等性
将 Idempotent
作为约束,可以让你对实现了 IntoOwning
但没有实现 ToOwned
的类型进行泛型编程。
StaticCow
<B>
将 Deref
<Target=B>
作为超级特性,因此你可以对 StaticCow<B>
做任何你可以对 &B
做的事情。然而,为了提供这个超级特性,它的实现需要满足 B : ToOwned
,这样它们就可以依赖 B::Owned : Borrow<B>
。
Idempotent
的要求较弱,因此它的功能也相应较弱,并且它不继承自 Deref
。 ToOwning
对 Owning 没有任何约束,这意味着从类型系统的角度来看,as far as the type system is concerned,
.into_owning()is just a completely arbitrary conversion. 因此,你无法对可能是 T 或 T::Owning 但你不知道是哪一个的类型做任何有用的操作,因为它们不保证有任何特性是共同的。
Idempotent
提供了足够的信息,使其成为一个有用的约束
-
它可以提供
T
或T::Owning
,并且会告诉你哪一个是哪个。 -
它对
T
进行了约束,使其满足T::Owning::Owning = T::Owning
。这意味着你可以多次调用into_owning()
并始终得到一个T
或T::Owning
。
Idempotent<T>
通过 Change
<T>
实现,它包含一个 T
;Keep
<T>
,它包含一个 T::Owning
;以及通过 ChangeOrKeep
<T>
实现,它可能包含其中之一,运行时决定。在 Idempotent<T>
上调用 .to_owning()
或 .into_owning()
总是返回一个 Keep<T>
。
示例
在这个示例中,我们将实现一个切片迭代器,它以相反的顺序返回切片的元素。最初,它会借用切片并在返回时克隆其元素。但它将实现 IntoOwning
,这样在迭代过程中的任何时候,都可以将其转换为拥有 Vec
的迭代器。然后,它将从 Vec
的末尾弹出它返回的元素,而不进行克隆。
首先,我们声明我们的灵活迭代器
struct FlexIter<S, E> {
inner: S,
index: usize,
_phantom: PhantomData<[E]>,
}
E
是切片元素的类型。尽管约束没有出现在结构声明中,但 S
将是 StaticCow<[E]>
的实现。具体来说,S
将是 Borrowed<'b, [E]>
,它封装了一个 &'b [E]
,或者它将是 Owned<[E]>
,它封装了一个 Vec<E>
。 index
比我们将返回的下一个元素的索引大一个,而 _phantom
是一个零大小对象,它必须存在以满足类型检查器,参数 E
必须出现在结构体字段中。
现在我们为 FlexIter
创建 ToOwning
和 IntoOwning
实例。
impl<S, E> ToOwning for FlexIter<S, E>
where
S: ToOwning,
{
type Owning = FlexIter<S::Owning, E>;
fn to_owning(&self) -> Self::Owning {
FlexIter {
inner: self.inner.to_owning(),
index: self.index.to_owning(),
_phantom: self._phantom.to_owning()
}
}
}
impl<S, E> IntoOwning for FlexIter<S, E>
where
S: IntoOwning,
{
fn into_owning(self) -> Self::Owning {
FlexIter {
inner: self.inner.into_owning(),
index: self.index.into_owning(),
_phantom: self._phantom.into_owning()
}
}
}
您可以看到,这些实现完全是照搬的:我们提供了一个与 Self
相同但用 S::Owning
代替 S
的 Owning
类型,以及将相同的操作应用于每个字段的 to_owning
和 into_owning
方法。
现在我们提供了一个借用迭代器的构造函数,该构造函数实现了 StaticCow<[E]>
,使用 Borrowed<'b, [E]>
。
impl<'b, E> FlexIter<'b, Borrowed<'b, [E]>, E> {
fn new(slice: &'b [E]) -> FlexIter<'b, Borrowed<'b, [E]>, E> {
FlexIter {
inner: Borrowed(slice),
index: slice.len(),
_phantom: CowPhantom::default(),
}
}
}
现在我们可以实现 Iterator
impl<S, E> Iterator for FlexIter<S, E>
where
E: Clone,
S: StaticCow<[E]>,
{
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
// This is here to show that we can also access `inner` generically
// through its `Deref<Target=[E]>` implementation, without having to
// match on `mut_if_owned()`.
assert!(self.index <= self.inner.len());
match self.inner.mut_if_owned() {
// We're borrowing the slice, so we have to work inefficiently
// by cloning its elements before we return them.
MutIfOwned::Const(slice) => {
if self.index == 0 {
None
} else {
self.index -= 1;
Some(slice[self.index].clone())
}
}
// We own the slice as a `Vec`, so we can pop elements off of it
// without cloning.
MutIfOwned::Mut(vec) => {
// It's necessary to make sure we first truncate the vector
// to `index`, because we may have already started iterating
// before `.into_owned()` was called, and this may be our
// first time calling `.next()` since we took ownership. Of
// course we could have had our `into_owned` implementation
// do this instead of doing it here.
vec.truncate(self.index);
let ret = vec.pop()?;
self.index -= 1;
Some(ret)
}
}
}
}
现在让我们看看它的实际应用
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let mut borrowing_iter = FlexIter::new(numbers.borrow());
println!("Borrowing:");
println!("{}", borrowing_iter.next().unwrap());
println!("{}", borrowing_iter.next().unwrap());
let owning_iter = borrowing_iter.into_owning();
std::mem::drop(numbers);
println!("Owning:");
for item in owning_iter {
println!("{}", item);
}
}
运行此代码,我们得到预期的结果
Borrowing:
5
4
Owning:
3
2
1
此示例也可在 crate 源代码的 examples/flex_iter.rs
中找到。