wro*_*ame 6 stack lifetime rust
我有以下简化代码,其中struct A包含某个属性.我想A从该属性的现有版本创建新实例,但是如何使该属性的新值的生命周期超过函数调用?
pub struct A<'a> {
some_attr: &'a str,
}
impl<'a> A<'a> {
fn combine(orig: &'a str) -> A<'a> {
let attr = &*(orig.to_string() + "suffix");
A { some_attr: attr }
}
}
fn main() {
println!("{}", A::combine("blah").some_attr);
}
Run Code Online (Sandbox Code Playgroud)
上面的代码产生
error[E0597]: borrowed value does not live long enough
--> src/main.rs:7:22
|
7 | let attr = &*(orig.to_string() + "suffix");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough
8 | A { some_attr: attr }
9 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 5:1...
--> src/main.rs:5:1
|
5 | / impl<'a> A<'a> {
6 | | fn combine(orig: &'a str) -> A<'a> {
7 | | let attr = &*(orig.to_string() + "suffix");
8 | | A { some_attr: attr }
9 | | }
10| | }
| |_^
Run Code Online (Sandbox Code Playgroud)
Vla*_*eev 13
这个问题大多肯定在之前得到了回答,但我并没有把它作为副本关闭,因为这里的代码有些不同,我认为这很重要.
注意你如何定义你的功能:
fn combine(orig: &'a str) -> A<'a>
Run Code Online (Sandbox Code Playgroud)
它表示它将返回一个类型A的值,其内部与提供的字符串完全一样长.但是,该函数的主体违反了此声明:
let attr = &*(orig.to_string() + "suffix");
A {
some_attr: attr
}
Run Code Online (Sandbox Code Playgroud)
在这里,您构建一个新的 ,从中String获取orig,并尝试将其返回到内部A.但是,为其创建的隐式变量orig.to_string() + "suffix"的生命周期严格小于输入参数的生命周期.因此,您的计划被拒绝.
另一种更实用的方法是考虑由to_string()连接创建的字符串必须存在于某个地方.但是,您只返回借来的一部分.因此,当函数退出时,字符串将被销毁,并且返回的切片将变为无效.这正是Rust阻止的情况.
为了克服这个问题,您可以存储String内部A:
pub struct A {
some_attr: String
}
Run Code Online (Sandbox Code Playgroud)
或者您可以std::borrow::Cow用来存储切片或拥有的字符串:
pub struct A<'a> {
some_attr: Cow<'a, str>
}
Run Code Online (Sandbox Code Playgroud)
在最后一种情况下,您的函数可能如下所示:
fn combine(orig: &str) -> A<'static> {
let attr = orig.to_owned() + "suffix";
A {
some_attr: attr.into()
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,因为您在函数内部构造了字符串,所以它被表示为拥有的变体,Cow因此您可以将'static生命周期参数用于结果值.绑定它orig也是可能的,但没有理由这样做.
使用Cow它也可以在A没有分配的情况下直接从切片中创建值:
fn new(orig: &str) -> A {
A { some_attr: orig.into() }
}
Run Code Online (Sandbox Code Playgroud)
这里的生命周期参数A将被绑定(通过生命周期省略)到输入字符串切片的生命周期.在这种情况下,使用借用的变体Cow,并且不进行分配.
另请注意,最好使用to_owned()或into()将字符串切片转换为Strings,因为这些方法不需要格式化代码来运行,因此它们更有效.
如果你在飞行中创建它,你怎么能回归
A生命'static?不确定"拥有的变体Cow"意味着什么,以及为什么这样做'static.
这是以下定义Cow:
pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized {
Borrowed(&'a B),
Owned(B::Owned),
}
Run Code Online (Sandbox Code Playgroud)
它看起来很复杂但实际上很简单.Cow可以包含对某种类型的引用B或者可以B通过该ToOwned特征派生的拥有值的实例.由于str器具ToOwned,其中Owned关联的类型等于String(写为ToOwned<Owned = String>,当此枚举是专业的str,它看起来像这样:
pub enum Cow<'a, str> {
Borrowed(&'a str),
Owned(String)
}
Run Code Online (Sandbox Code Playgroud)
因此,Cow<str>可以表示字符串切片或拥有的字符串 - 虽然Cow确实提供了写入时克隆功能的方法,但它通常用于保存可以借用或拥有的值,以避免额外的分配.因为Cow<'a, B>工具Deref<Target = B>,你可以&B从Cow<'a, B>简单的reborrowing:如果x是Cow<str>的话&*x就是&str,不管什么是包含内x-当然,你可以得到一个片出来的这两个变种Cow.
您可以看到该Cow::Owned变体仅包含其中的任何引用String.因此,当Cow使用Ownedvariant 创建值时,您可以选择所需的任何生命周期(请记住,生命周期参数与泛型类型参数非常相似;特别是,调用者可以选择它们) - 对它没有任何限制.因此选择'static尽可能最长的生命是有意义的.
是否
orig.to_owned从任何调用此函数的人中删除所有权?这听起来很不方便.
该to_owned()方法属于ToOwned特质:
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
Run Code Online (Sandbox Code Playgroud)
这个特点是通过实现str与Owned相等String.to_owned()method返回调用它的任何值的拥有变量.在这种特殊情况下,它会创建一个String出来的&str,有效地复制串片的内容到一个新的分配.因此,to_owned()不,并不意味着所有权转移,它更像是暗示一个"聪明"的克隆.
至于我可以告诉String实现
Into<Vec<u8>>但不是str,那么我们如何into()在第二个例子中调用?
该Into特征非常通用,并且在标准库中实现了许多类型.Into通常通过From特征来实现:if T: From<U>,then U: Into<T>.From标准库中有两个重要的实现:
impl<'a> From<&'a str> for Cow<'a, str>
impl<'a> From<String> for Cow<'a, str>
Run Code Online (Sandbox Code Playgroud)
这些实现非常简单-他们只是返回Cow::Borrowed(value),如果value是&str和Cow::Owned(value),如果value是String.
这意味着&'a str并String实现Into<Cow<'a, str>>,因此可以将它们转换为Cowwith into()方法.这是在我的例子究竟发生了什么-我使用into()转换String或&str到Cow<str>.如果没有这种显式转换,您将收到有关不匹配类型的错误.
| 归档时间: |
|
| 查看次数: |
623 次 |
| 最近记录: |