如何与母牛一起使用SmallVec

问题描述 投票:4回答:1

我想将SmallVecCow一起使用。我试过了:

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,但没有成功。

是否可以将SmallVecCow一起使用?

我知道的一个解决方案是使用自定义Cow枚举:

pub enum SmallCow<'a, A: Array> {
    Borrowed(&'a [A::Item]),
    Owned(SmallVec<A>),
}

还有其他方法吗?

rust copy-on-write
1个回答
0
投票

事实是,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)

© www.soinside.com 2019 - 2024. All rights reserved.