gpt4 book ai didi

rust - 与 Vec 相比,为什么 SmallVec 在存储具有生命周期的类型时具有不同的行为?

转载 作者:行者123 更新时间:2023-11-29 07:54:07 29 4
gpt4 key购买 nike

我有三个示例,一个使用 Vec一个使用 SmallVec ,还有一个是我自己实现的 SmallVec .那些使用 Vec 的和我自己的 SmallVec编译但使用真正的 SmallVec没有。

使用 Vec 的工作示例

use std::borrow::Cow;
use std::collections::HashMap;

pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}

/// IMPORTANT PART IS HERE: `Vec<Cow<'a, str>>`
pub struct ItemTraitReturns<'a>(Vec<Cow<'a, str>>);

/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}

impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// Works as expected: I expect that I can return `&ItemTraitReturns<'_>`
// when I have `&ItemTraitReturns<'static>` (since 'static outlives everything).
temp
// Will return `&ItemTraitReturns<'_>`
}
}

SmallVec 失败的例子

使用 SmallVec而不是 Vec没有其他变化。

use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::HashMap;

pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}

/// IMPORTANT PART IS HERE: Uses SmallVec instead of Vec
pub struct ItemTraitReturns<'a>(SmallVec<[Cow<'a, str>; 2]>);

/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}

impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
temp
}
}
error[E0308]: mismatched types
--> src/lib.rs:23:9
|
23 | temp
| ^^^^ lifetime mismatch
|
= note: expected type `&ItemTraitReturns<'_>`
found type `&ItemTraitReturns<'static>`
note: the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
19 | | let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
20 | | // Error:
21 | | // = note: expected type `&demo2::ItemTraitReturns<'_>`
22 | | // found type `&demo2::ItemTraitReturns<'static>`
23 | | temp
24 | | }
| |_____^
= note: ...does not necessarily outlive the static lifetime

我自己的工作示例 SmallVec

当我实现自己的(非常幼稚)SmallVec<[T; 2]> (称为 NaiveSmallVec2<T> )代码也编译...很奇怪!

use std::borrow::Cow;
use std::collections::HashMap;

/// This is a very naive implementation of a SmallVec<[T; 2]>
pub struct NaiveSmallVec2<T> {
item1: Option<T>,
item2: Option<T>,
more: Vec<T>,
}

impl<T> NaiveSmallVec2<T> {
pub fn push(&mut self, item: T) {
if self.item1.is_none() {
self.item1 = Some(item);
} else if self.item2.is_none() {
self.item2 = Some(item);
} else {
self.more.push(item);
}
}

pub fn element_by_index(&self, index: usize) -> Option<&T> {
match index {
0 => self.item1.as_ref(),
1 => self.item2.as_ref(),
_ => self.more.get(index - 2),
}
}
}

pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}

/// IMPORTANT PART IS HERE: Uses NaiveSmallVec2
pub struct ItemTraitReturns<'a>(NaiveSmallVec2<Cow<'a, str>>);

/// only takes items with static lifetime
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}

impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// astonishingly this works!
temp
}
}

我希望 SmallVecVec 这样编译的版本版本确实如此。我不明白为什么在某些情况下(在 Vec 的情况下)&ItemTraitReturns<'static>可以转换为&ItemTraitReturns<'_>在某些情况下( SmallVec )这是不可能的(我没有看到 Vec/SmallVec 的影响)。

我不想改变这个特征的生命周期:

pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}

...因为在使用特征时我不关心生命周期(这应该是一个实现细节)...但仍然想使用 SmallVec .

最佳答案

这种差异似乎是由 Vec 之间的方差 差异引起的。和 SmallVec .同时 Vec<T>T协变 , SmallVec似乎是不变的。结果,SmallVec<[&'a T; 1]>无法转换为 SmallVec<[&'b T; 1]>即使一生'a超过生命周期'b并且转换应该是安全的。这是触发编译器错误的最小示例:

fn foo<'a, T>(x: SmallVec<[&'static T; 1]>) -> SmallVec<[&'a T; 1]> {
x // Compiler error here: lifetime mismatch
}

fn bar<'a, T>(x: Vec<&'static T>) -> Vec<&'a T> {
x // Compiles fine
}

这似乎是 SmallVec 的缺点.变体由编译器自动确定,有时很难说服编译器假定类型是协变的是安全的。

有一个open Github issue for this problem .

不幸的是,我不知道有什么方法可以根据类型的公共(public)接口(interface)来计算类型的差异。差异不包含在文档中,并且取决于类型的私有(private)实现细节。您可以阅读更多关于方差的信息 in the language referencein the Nomicon .

关于rust - 与 Vec 相比,为什么 SmallVec 在存储具有生命周期的类型时具有不同的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56238529/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com