如何将生命周期与 AsRef 包装器分离?
How can I decouple lifetimes from the AsRef wrapper?
我正在尝试为 &[u8]
数据构建一个临时包装器 class,它允许调用者使用其原始生命周期而不是临时包装器生命周期来提取数据。
如果直接使用 &[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));
}
现在我想在抽象可变性时做同样的事情,即我需要使用 AsRef<[u8]>
和 AsMut<[u8]>
,而不是使用 &[u8]
。我试过以下方法,但它不起作用。 Playground:
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()
的 return 找到一个好的生命周期,因为它需要它比 &self
.
的生命周期更长
有没有办法像我在上面的 &[u8]
示例中所做的那样,将其与 wrapper
生命周期分离?
经过一番挖掘,我现在更了解情况了。问题是 S: AsRef<[u8]>
不仅允许 S
是像 &[u8]
或 &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]
,我们仍然需要选项 1 中的 extract
函数。
这个解决方案仍然实现了作为引入 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));
}
我正在尝试为 &[u8]
数据构建一个临时包装器 class,它允许调用者使用其原始生命周期而不是临时包装器生命周期来提取数据。
如果直接使用 &[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));
}
现在我想在抽象可变性时做同样的事情,即我需要使用 AsRef<[u8]>
和 AsMut<[u8]>
,而不是使用 &[u8]
。我试过以下方法,但它不起作用。 Playground:
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()
的 return 找到一个好的生命周期,因为它需要它比 &self
.
有没有办法像我在上面的 &[u8]
示例中所做的那样,将其与 wrapper
生命周期分离?
经过一番挖掘,我现在更了解情况了。问题是 S: AsRef<[u8]>
不仅允许 S
是像 &[u8]
或 &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]
,我们仍然需要选项 1 中的 extract
函数。
这个解决方案仍然实现了作为引入 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));
}