我想将SmallVec
与Cow
一起使用。我试过了:
use smallvec::SmallVec;
use std::borrow::Cow;
fn main() {
let s = "hello world".to_owned();
let mut s = Cow::Borrowed(s.as_bytes());
clear_subslice(&mut s, 2, 6);
}
fn clear_subslice(text: &mut Cow<'_, [u8]>, start: usize, end: usize) {
match text {
Cow::Borrowed(v) => {
if !v[start..end].iter().all(|&c| c == b' ') {
let mut v = SmallVec::from_slice(v);
v[start..end].iter_mut().for_each(|c| *c = b' ');
*text = Cow::Owned(v);
}
}
Cow::Owned(v) => {
v[start..end].iter_mut().for_each(|c| *c = b' ');
}
}
}
error[E0271]: type mismatch resolving `<[u8] as std::borrow::ToOwned>::Owned == smallvec::SmallVec<_>`
--> src/main.rs:16:25
|
16 | *text = Cow::Owned(v);
| ^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found struct `smallvec::SmallVec`
|
= note: expected type `std::vec::Vec<u8>`
found type `smallvec::SmallVec<_>`
仅适用于已将ToOwned
实现为特定类型的类型。在这种情况下,&[u8]
具有使用目标ToOwned
实施的Vec
。
我尝试以目标为ToOwned
的方式实现SmallVec
,但没有成功。
是否可以将SmallVec
和Cow
一起使用?
我知道的一个解决方案是使用自定义Cow
枚举:
pub enum SmallCow<'a, A: Array> {
Borrowed(&'a [A::Item]),
Owned(SmallVec<A>),
}
还有其他方法吗?
事实是,Cow<'a, T>
要求T
实现ToOwned
,并且Cow<'a, T>
的拥有版本是Owned
的关联类型ToOwned
。此外,Owned
必须实现Borrow<T>
。就目前而言,Cow<'a, [u8]>
可以only使用Vec<u8>
作为其拥有的变体,因为[T]
实现了ToOwned
,而Vec<T>
是Owned
关联类型。
我为您提供了两种选择。您可以使用不同的特征范围来实现自己的Cow
实现(或者按照您的建议,只需专门针对您的确切用例),或者可以使用新类型包装[u8]
和SmallVec<A>
并实现[C0 ToOwned
的包装上的[]和[u8]
的包装上的Borrow<SliceWrapper<u8>>
。因为您似乎已经涵盖了前者,所以我将集中讨论后者。
newtype是一个包装器,从本质上讲,它声明了一个与原始类型等效的新类型,但没有任何特征或方法。通常的方法是使用元组结构。
SmallVec<A>
请注意,由于use small_vec::{Array, SmallVec};
struct SmallVecWrap<A: Array>(SmallVec<A>);
struct SliceWrap<T>([T]);
是SliceWrap<T>
,因此它是未调整大小的类型,因此我们将始终在指针后面使用它。这样做很重要,因为当我们在[T]
上实现Borrow
时,它将是SmallVecWrap<A>
,而不是Borrow<SliceWrap<T>>
。也就是说,Borrow<&SliceWrap<T>>
使用未调整大小的类型作为它的类型参数(我想might可以做到这一点,但是您将拥有额外的间接层,并且您将无法使用变异切片中的方法)。
我遇到这种方法的一个主要问题是,似乎没有一种方法可以在没有不安全的阻止的情况下将Borrow
转换为&[u8]
。这确实有一定意义,因为在没有任何额外信息的情况下,这两种类型在语义上可能会有所不同。例如,&SliceWrap<u8>
处于类似情况,但是在不检查其是否为零的情况下将NonZeroU8
转换为u8
没有意义。 RFC#1909,未设置大小的右值可能对此有所帮助,但我无法使其正常工作。我会注意到MIRI在测试用例上运行时没有发现任何问题。
此方法的另一个问题是,您必须始终遵循包装的类型(例如,示例代码中的NonZeroU8
),然后可能重新包装返回的值,或者重新实现所需的所有特征和方法。同样的问题也适用于v.0
方法,但是您只需要实现SmallCow<'a, A>
的特征和方法,而没有那么多。
如果您决定始终遵循包装类型的方法,则可能需要公开新类型的字段(例如Cow<'a, T>
),以便可以在此模块之外使用它们。
此方法的最终问题还是SliceWrap<T>(pub [T])
。 ToOwned
需要单个类型进行转换,但是即使ToOwned
元素的类型是固定的,SmallVecWrap<A>
不是单个类型。例如,可以将A
有效地转换为&[u8]
,SmallVecWrap<[u8, 1]>
等。一种可能的解决方法是将SmallVecWrap<[u8, 2]>
类型附加到A
:
SliceWrap<T>
然后您可以将struct SliceWrap<T, A: Array> {
array: std::marker::PhantomData<A>,
slice: [T],
}
设置为ToOwned
来实现SliceWrap<T, A>
的Owned
。>>
无论如何,这是完整的示例。
SmallVecWrap<A>
use smallvec::{Array, SmallVec}; // 0.6.10 use std::borrow::{Borrow, Cow, ToOwned}; struct SmallVecWrap<A: Array>(SmallVec<A>); #[repr(transparent)] struct SliceWrap<T>([T]); impl<T> SliceWrap<T> { // for convenience fn from_slice(slice: &[T]) -> &Self { // As far as I can tell, there's no way to do this without unsafe. // This should be safe since SliceWrap<T> is transparently a [T]. // All we're doing is changing a (fat) pointer to a [T] // into a (fat) pointer to SliceWrap<T>. // I won't claim expertise on this, though. unsafe { &*((slice as *const [T]) as *const SliceWrap<T>) } // ^ ^ // These parentheses aren't needed, but it's clearer this way } // I guess we didn't need this #[allow(dead_code)] fn from_mut_slice(slice: &mut [T]) -> &mut Self { // Same caveats apply unsafe { &mut *((slice as *mut [T]) as *mut SliceWrap<T>) } } } impl<A: Array> Borrow<SliceWrap<A::Item>> for SmallVecWrap<A> { fn borrow(&self) -> &SliceWrap<A::Item> { SliceWrap::from_slice(self.0.borrow()) } } // Note: We have to choose a particular array size // to use for the owned SmallVec<A>. const OWNED_ARRAY_SIZE: usize = 4; impl<T: Clone> ToOwned for SliceWrap<T> { type Owned = SmallVecWrap<[T; OWNED_ARRAY_SIZE]>; fn to_owned(&self) -> SmallVecWrap<[T; OWNED_ARRAY_SIZE]> { SmallVecWrap(self.0.into()) } } fn main() { let s = "hello world".to_owned(); let mut s = Cow::Borrowed(SliceWrap::from_slice(s.as_bytes())); clear_subslice(&mut s, 2, 6); } fn clear_subslice(text: &mut Cow<'_, SliceWrap<u8>>, start: usize, end: usize) { match text { Cow::Borrowed(v) => { if !v.0[start..end].iter().all(|&c| c == b' ') { let mut v = SmallVec::from_slice(&v.0); v[start..end].iter_mut().for_each(|c| *c = b' '); *text = Cow::Owned(SmallVecWrap(v)); } } Cow::Owned(v) => { v.0[start..end].iter_mut().for_each(|c| *c = b' '); } } }
存在第三个选择:除非您已进行基准测试并确定这些小的分配会显着降低程序速度,否则请不要使用(playground)。