В том-то и дело, что Cow<'a, T>
требует T
для реализации ToOwned
, а владельцем версии Cow<'a, T>
является связанный тип Owned
из ToOwned
. Более того, Owned
, должен реализовать Borrow<T>
. В его нынешнем виде Cow<'a, [u8]>
может только использовать Vec<u8>
в качестве собственного варианта, поскольку [T]
реализует ToOwned
с Vec<T>
в качестве Owned
связанного типа.
Iувидеть два варианта для вас. Либо вы можете сделать свою собственную реализацию Cow
, которая использует различные границы признаков (или, как вы предложили, просто специализироваться на вашем конкретном случае использования), либо вы можете использовать новые типы для переноса [u8]
и SmallVec<A>
и реализации ToOwned
на обертке для [u8]
и Borrow<SliceWrapper<u8>>
на обертке на 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>>
. То есть Borrow
использует в качестве параметра типа нестандартный тип (я полагаю, что может обойтись без этого, но у вас будет дополнительный уровень косвенности, и вы не сможетеиспользуйте методы мутации на срезе).
Одна из основных проблем, с которой я столкнулся при таком подходе, заключается в том, что, похоже, нет способа превратить &[u8]
в &SliceWrap<u8>
без небезопасного блока. Это имеет определенный смысл, поскольку без какой-либо дополнительной информации эти два типа могут быть семантически различными. Например, NonZeroU8
находится в аналогичной ситуации, но нет смысла конвертировать u8
в NonZeroU8
без проверки, равен ли он нулю. RFC # 1909, несвязанные значения, может помочь с этим, но я не мог заставить его работать. Я отмечу, что MIRI не обнаружил никаких проблем при запуске на вашем тестовом примере.
Другая проблема с этим подходом состоит в том, что вы должны либо всегда откладывать использование упакованного типа (например, 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' ');
}
}
}
(детская площадка)
Для вас существует третий вариант: не используйте SmallVec<A>
, если только вы не определили и не определили, что эти небольшие ассигнования значительно замедляют вашу программу.