Return 在堆栈上分配的东西
Return something that's allocated on the stack
我有以下简化代码,其中结构 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);
}
以上代码产生
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| | }
| |_^
这个问题之前肯定有人回答过,但我不会将其作为重复项关闭,因为这里的代码有些不同,我认为这很重要。
注意您是如何定义函数的:
fn combine(orig: &'a str) -> A<'a>
它说它将 return 类型 A
的值,其内部与提供的字符串一样长。然而,函数体违反了这个声明:
let attr = &*(orig.to_string() + "suffix");
A {
some_attr: attr
}
这里构造一个从[=22=得到的newString
,取一片然后尝试return里面A
。但是,为 orig.to_string() + "suffix"
创建的隐式变量的生命周期严格小于输入参数的生命周期。因此,你的程序被拒绝了。
另一种更实用的方法是考虑由 to_string()
和连接创建的字符串必须存在于某个地方。但是,您只是 return 借来的一部分。因此,当函数退出时,字符串被销毁,returned 切片变得无效。这正是 Rust 防止的情况。
为了克服这个问题,您可以在 A
:
中存储一个 String
pub struct A {
some_attr: String
}
或者您可以使用 std::borrow::Cow
来存储切片或拥有的字符串:
pub struct A<'a> {
some_attr: Cow<'a, str>
}
在最后一种情况下,您的函数可能如下所示:
fn combine(orig: &str) -> A<'static> {
let attr = orig.to_owned() + "suffix";
A {
some_attr: attr.into()
}
}
请注意,因为您在函数内部构造字符串,所以它表示为 Cow
的拥有变体,因此您可以使用 'static
生命周期参数作为结果值。将它绑定到 orig
也是可能的,但没有理由这样做。
使用 Cow
也可以直接从切片中创建 A
的值而无需分配:
fn new(orig: &str) -> A {
A { some_attr: orig.into() }
}
此处 A
的生命周期参数将(通过生命周期省略)与输入字符串切片的生命周期相关联。在这种情况下,使用了 Cow
的借用变体,并且没有进行任何分配。
另请注意,最好使用to_owned()
或into()
将字符串切片转换为String
s,因为这些方法不需要将代码格式化为运行和所以他们更有效率。
how can you return an A
of lifetime 'static
when you're creating it on the fly? Not sure what "owned variant of Cow
" means and why that makes 'static
possible.
这里是Cow
的定义:
pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized {
Borrowed(&'a B),
Owned(B::Owned),
}
看似复杂,其实很简单。 Cow
的实例可能包含对某种类型 B
的引用,或者包含可以通过 ToOwned
特征从 B
派生的拥有值。因为 str
实现 ToOwned
,其中 Owned
关联类型等于 String
(写为 ToOwned<Owned = String>
,当此枚举专门用于 str
时,它看起来像这样:
pub enum Cow<'a, str> {
Borrowed(&'a str),
Owned(String)
}
因此,Cow<str>
可能表示字符串切片或拥有的字符串 - 虽然 Cow
确实提供了写时克隆功能的方法,但它也经常用于保存可以借用或拥有以避免额外分配的值。因为 Cow<'a, B>
实现了 Deref<Target = B>
,你可以通过简单的重新借用从 Cow<'a, B>
得到 &B
:如果 x
是 Cow<str>
,那么 &*x
是 &str
,无论 x
中包含什么 - 自然地,您可以从 Cow
.
的两种变体中分出一部分
您可以看到 Cow::Owned
变体内部不包含任何引用,仅包含 String
。因此,当使用 Owned
变体创建 Cow
的值时,you 可以选择任何你想要的生命周期(记住,生命周期参数很像泛型类型参数;特别是,是来电者可以选择它们) - 没有任何限制。所以选择 'static
作为最大可能的生命周期是有意义的。
Does orig.to_owned
remove ownership from whoever's calling this function? That sounds like it would be inconvenient.
to_owned()
方法属于ToOwned
特征:
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
此特征由 str
实现,Owned
等于 String
。 to_owned()
方法 return 是调用它的任何值的自有变体。在这种特殊情况下,它从 &str
中创建了一个 String
,有效地将字符串切片的内容复制到新的分配中。因此,不,to_owned()
并不意味着所有权转移,它更像是意味着 "smart" 克隆。
As far as I can tell String implements Into<Vec<u8>>
but not str
, so how can we call into()
in the 2nd example?
Into
特性非常通用,它在标准库中为许多类型实现。 Into
通常通过 From
特性实现:如果 T: From<U>
,则 U: Into<T>
。 From
在标准库中有两个重要的实现:
impl<'a> From<&'a str> for Cow<'a, str>
impl<'a> From<String> for Cow<'a, str>
这些实现非常简单 - 它们只是 return Cow::Borrowed(value)
如果 value
是 &str
和 Cow::Owned(value)
如果 value
是 String
.
这意味着&'a str
和String
实现了Into<Cow<'a, str>>
,因此它们可以通过into()
方法转换为Cow
。这正是我的示例中发生的情况 - 我正在使用 into()
将 String
或 &str
转换为 Cow<str>
。如果没有这种显式转换,您将收到有关类型不匹配的错误。
我有以下简化代码,其中结构 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);
}
以上代码产生
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| | }
| |_^
这个问题之前肯定有人回答过,但我不会将其作为重复项关闭,因为这里的代码有些不同,我认为这很重要。
注意您是如何定义函数的:
fn combine(orig: &'a str) -> A<'a>
它说它将 return 类型 A
的值,其内部与提供的字符串一样长。然而,函数体违反了这个声明:
let attr = &*(orig.to_string() + "suffix");
A {
some_attr: attr
}
这里构造一个从[=22=得到的newString
,取一片然后尝试return里面A
。但是,为 orig.to_string() + "suffix"
创建的隐式变量的生命周期严格小于输入参数的生命周期。因此,你的程序被拒绝了。
另一种更实用的方法是考虑由 to_string()
和连接创建的字符串必须存在于某个地方。但是,您只是 return 借来的一部分。因此,当函数退出时,字符串被销毁,returned 切片变得无效。这正是 Rust 防止的情况。
为了克服这个问题,您可以在 A
:
String
pub struct A {
some_attr: String
}
或者您可以使用 std::borrow::Cow
来存储切片或拥有的字符串:
pub struct A<'a> {
some_attr: Cow<'a, str>
}
在最后一种情况下,您的函数可能如下所示:
fn combine(orig: &str) -> A<'static> {
let attr = orig.to_owned() + "suffix";
A {
some_attr: attr.into()
}
}
请注意,因为您在函数内部构造字符串,所以它表示为 Cow
的拥有变体,因此您可以使用 'static
生命周期参数作为结果值。将它绑定到 orig
也是可能的,但没有理由这样做。
使用 Cow
也可以直接从切片中创建 A
的值而无需分配:
fn new(orig: &str) -> A {
A { some_attr: orig.into() }
}
此处 A
的生命周期参数将(通过生命周期省略)与输入字符串切片的生命周期相关联。在这种情况下,使用了 Cow
的借用变体,并且没有进行任何分配。
另请注意,最好使用to_owned()
或into()
将字符串切片转换为String
s,因为这些方法不需要将代码格式化为运行和所以他们更有效率。
how can you return an
A
of lifetime'static
when you're creating it on the fly? Not sure what "owned variant ofCow
" means and why that makes'static
possible.
这里是Cow
的定义:
pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized {
Borrowed(&'a B),
Owned(B::Owned),
}
看似复杂,其实很简单。 Cow
的实例可能包含对某种类型 B
的引用,或者包含可以通过 ToOwned
特征从 B
派生的拥有值。因为 str
实现 ToOwned
,其中 Owned
关联类型等于 String
(写为 ToOwned<Owned = String>
,当此枚举专门用于 str
时,它看起来像这样:
pub enum Cow<'a, str> {
Borrowed(&'a str),
Owned(String)
}
因此,Cow<str>
可能表示字符串切片或拥有的字符串 - 虽然 Cow
确实提供了写时克隆功能的方法,但它也经常用于保存可以借用或拥有以避免额外分配的值。因为 Cow<'a, B>
实现了 Deref<Target = B>
,你可以通过简单的重新借用从 Cow<'a, B>
得到 &B
:如果 x
是 Cow<str>
,那么 &*x
是 &str
,无论 x
中包含什么 - 自然地,您可以从 Cow
.
您可以看到 Cow::Owned
变体内部不包含任何引用,仅包含 String
。因此,当使用 Owned
变体创建 Cow
的值时,you 可以选择任何你想要的生命周期(记住,生命周期参数很像泛型类型参数;特别是,是来电者可以选择它们) - 没有任何限制。所以选择 'static
作为最大可能的生命周期是有意义的。
Does
orig.to_owned
remove ownership from whoever's calling this function? That sounds like it would be inconvenient.
to_owned()
方法属于ToOwned
特征:
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
此特征由 str
实现,Owned
等于 String
。 to_owned()
方法 return 是调用它的任何值的自有变体。在这种特殊情况下,它从 &str
中创建了一个 String
,有效地将字符串切片的内容复制到新的分配中。因此,不,to_owned()
并不意味着所有权转移,它更像是意味着 "smart" 克隆。
As far as I can tell String implements
Into<Vec<u8>>
but notstr
, so how can we callinto()
in the 2nd example?
Into
特性非常通用,它在标准库中为许多类型实现。 Into
通常通过 From
特性实现:如果 T: From<U>
,则 U: Into<T>
。 From
在标准库中有两个重要的实现:
impl<'a> From<&'a str> for Cow<'a, str>
impl<'a> From<String> for Cow<'a, str>
这些实现非常简单 - 它们只是 return Cow::Borrowed(value)
如果 value
是 &str
和 Cow::Owned(value)
如果 value
是 String
.
这意味着&'a str
和String
实现了Into<Cow<'a, str>>
,因此它们可以通过into()
方法转换为Cow
。这正是我的示例中发生的情况 - 我正在使用 into()
将 String
或 &str
转换为 Cow<str>
。如果没有这种显式转换,您将收到有关类型不匹配的错误。