rust - 如何将生命周期与 AsRef 包装器分离?
问题描述
我正在尝试为&[u8]
数据构建一个临时包装类,允许调用者提取具有原始生命周期的数据,而不是临时包装生命周期。
如果&[u8]
直接使用,这有效:
Playground:
struct Wrapper1<'a> {
data: &'a [u8],
}
impl<'a> Wrapper1<'a> {
// In the return value, we return lifetime 'a, not the lifetime of the wrapper
pub fn get(&self) -> &'a [u8] {
&self.data[2..]
}
}
fn func(data: &[u8]) -> &[u8] {
let wrapper = Wrapper1 { data };
// Note that we can return wrapper.get() even though Wrapper1 goes out of scope
wrapper.get()
}
fn main() {
let data = vec![5; 10];
println!("{:?}", func(&data));
}
现在我想在对可变性进行抽象时做同样的事情,即&[u8]
我需要使用AsRef<[u8]>
and而不是使用AsMut<[u8]>
。我尝试了以下方法,但它不起作用。游乐场:
use std::marker::PhantomData;
struct Wrapper2<'a, S: 'a + AsRef<[u8]>> {
data: S,
_p: PhantomData<&'a S>,
}
impl<'a, S: 'a + AsRef<[u8]>> Wrapper2<'a, S> {
pub fn get<'b>(&'b self) -> &'a [u8] {
&self.data.as_ref()[2..]
}
}
fn func(data: &[u8]) -> &[u8] {
let wrapper = Wrapper2 {
data,
_p: PhantomData,
};
wrapper.get()
}
fn main() {
let data = vec![5; 10];
println!("{:?}", func(&data));
}
错误信息:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:10:20
|
10 | &self.data.as_ref()[2..]
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'b` as defined on the method body at 9:16...
--> src/main.rs:9:16
|
9 | pub fn get<'b>(&'b self) -> &'a [u8] {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:10:10
|
10 | &self.data.as_ref()[2..]
| ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 8:6...
--> src/main.rs:8:6
|
8 | impl<'a, S: 'a + AsRef<[u8]>> Wrapper2<'a, S> {
| ^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:10:9
|
10 | &self.data.as_ref()[2..]
| ^^^^^^^^^^^^^^^^^^^^^^^^
它无法为返回找到一个好的生命周期,Wrapper2::get()
因为它要求它比&self
.
有没有办法wrapper
像我在&[u8]
上面的例子中那样将它与生命周期解耦?
解决方案
经过一番挖掘,我现在对情况有了更好的了解。问题是它S: AsRef<[u8]>
不仅允许S
像&[u8]
or这样的引用类型&mut [u8]
,而且还可以是像Vec<u8>
. 虽然对于引用,您可能会争辩说应该有一种方法可以将它们的寿命延长到寿命之外Wrapper
,因为Vec<u8>
,这显然没有意义,Vec<u8>
因为wrapper
.
我提出了两种可能的解决方案:
1.不要延长引用的生命周期,而是允许一种提取内部对象的方法。
这允许func
问题中提出的 return 语句。
struct Wrapper2<S: AsRef<[u8]>> {
data: S,
}
impl<S: AsRef<[u8]>> Wrapper2<S> {
pub fn get(&self) -> &[u8] {
&self.data.as_ref()[2..]
}
pub fn extract(self) -> S {
// but note that we can only return the full data,
// not a subset like get() or get_mut() would be doing
self.data
}
}
impl<S: AsRef<[u8]> + AsMut<[u8]>> Wrapper2<S> {
pub fn get_mut(&mut self) -> &mut [u8] {
&mut self.data.as_mut()[2..]
}
}
fn func(data: &[u8]) -> &[u8] {
let wrapper = Wrapper2 { data };
let _g = wrapper.get();
wrapper.extract()
}
fn func_mut(data: &mut [u8]) -> &mut [u8] {
let mut wrapper = Wrapper2 { data };
let _g = wrapper.get_mut();
wrapper.extract()
}
fn main() {
let mut data = vec![5; 10];
println!("{:?}", func(&data));
println!("{:?}", func_mut(&mut data));
}
2. 明确限制类型&[u8]
和&mut [u8]
这意味着我们不再使用AsRef
,Vec
不在图片中,我们知道我们实际上有一个参考。在这种情况下,生命周期延长适用于这种&[u8]
情况,即我们可以从包装器中获取&[u8]
引用,并且该引用将在包装器中继续存在。对于&mut [u8]
,不幸的是,我们仍然需要extract
选项 1 中的函数。
AsRef
该解决方案仍然实现了作为引入/的原因提到的问题的“抽象可变性”目标AsMut
,它只是在没有这些特征的情况下做到了。
struct Wrapper2<S> {
data: S,
}
impl <'a> Wrapper2<&'a [u8]> {
fn get(&self) -> &'a [u8] {
&self.data[2..]
}
}
impl <'a> Wrapper2<&'a mut [u8]> {
fn get_mut(&mut self) -> &mut [u8] {
&mut self.data[2..]
}
fn extract(self) -> &'a mut [u8] {
&mut self.data[2..]
}
}
fn func(data: &[u8]) -> &[u8] {
let wrapper = Wrapper2 { data };
wrapper.get()
}
fn func_mut(data: &mut [u8]) -> &mut [u8] {
let mut wrapper = Wrapper2 { data };
let _a = wrapper.get_mut();
let _b = wrapper.get_mut();
wrapper.extract()
}
fn main() {
let mut data = vec![5; 10];
println!("{:?}", func(&data));
println!("{:?}", func_mut(&mut data));
}
推荐阅读
- openrefine - OpenRefine:修复 Mojibake
- excel - 为什么选择粘贴不适用于 VBA
- haskell - 如何在 Haskell 单例库中对类型进行模式匹配?
- reactjs - 不同文件中的相同导入是否会导致 ReactJS 中的构建大小增加?
- python - 创建 Python 包的 Docker 镜像
- postgresql - 如何执行嵌套删除 gorm Golang?
- matlab - 为什么nargout返回-1?在这种情况下如何获得正确数量的函数输出?
- java - java.lang.NullPointerException:无法调用“服务”,因为“this.contentService”为空
- css - 如何在 TailwindCSS 和 Alpine.js 中构建没有重复标签内容的响应式手风琴标签?
- mysql - 最终 SQL 输出,使用 MySQL 更新到具有相同结构的基表