为什么我可以 return 引用一个函数的拥有值?
Why can I return a reference to an owned value of a function?
在The Rust Programming Language的第19.2章中,下面的例子编译没有任何错误。我从 issue #1834 中发现有一个新的生命周期省略规则隐含地使 's
比 'c
长。
虽然我找不到这个新省略规则的详细解释,但我猜它只不过是 更长、更明确的约束的隐式版本: <'c, 's: 'c>
。不过,我认为我的困惑可能与这个新的省略规则无关,但当然我对此可能是错误的。
我的理解是,parse_context
取得了 context
的所有权,因为它没有被借用,而是实际上移到了函数中。仅此一项就意味着 context
的生命周期应该与它所拥有的函数的生命周期相匹配,而不管我们在 Context
和 Parser
.[=27 中定义的生命周期和约束如何=]
根据这些定义,context
比临时 Parser
长的部分对我来说非常有意义(毕竟,我们定义了更长的生命周期),但 [=22] 的部分=] 当 context
在 parse_context
末尾超出范围时,引用不会被删除,我仍然可以安全地 return 它——让我感到困惑。
我错过了什么?编译器如何推断 returned &str
的生命周期?
更新示例
struct Context<'s>(&'s str);
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
fn main()
{
let mut s = String::new();
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
{
println!("{}", text);
}
}
您不是返回对函数拥有值的引用。您正在返回传入的引用的副本。
您的函数是恒等函数的精巧版本:
fn parse_context(s: &str) -> &str {
s
}
在您的实际代码中,您引用一个包含字符串切片的结构,然后引用另一个字符串切片,但所有这些引用都被丢弃。
例如,parse
中有一个不需要的引用:
fn parse(&self) -> Result<(), &'s str> {
Err( self.context.0)
// ^ no & needed
}
此外,如果您启用更多的 lints,您将被迫为您的函数签名添加更多的生命周期,这可能会让事情变得更清楚:
#![deny(rust_2018_idioms)]
fn parse_context(context: Context<'_>) -> Result<(), &'_ str> {
Parser { context: &context }.parse()
}
另请参阅:
Although I couldn't find a detailed explanation of this new elision rule,
T: 'a
inference in structs 在版本指南中。
感谢 Jmb 的评论和 Shepmaster 的一些回答,现在我确实很清楚我对 RAII 规则和所有权的困惑。
也就是说,字符串是在 main
范围内创建的,匿名 Context
实例 而不是 取得它只是借用的字符串的所有权一个引用,因此即使实例在 parse_context
的末尾与借用的引用一起被删除,复制到 Err
对象的引用仍然存在并指向现有的字符串——因此我们正在使用受约束的生命周期变量和编译器能够推断出内部字符串引用的生命周期。
struct Context<'s>(&'s str);
→ 类型 Context
的值包含一个具有生命周期 's
的字符串。此生命周期至少与上下文的生命周期一样长,但可能更长。
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
→ 类型 Parser
的值持有对具有某个生命周期 'c
的上下文的引用。此上下文包含一个具有 other 生命周期 's
.
的字符串
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
→ 函数 parse
returns 生命周期为 's
的值,即。 与存储在上下文中的字符串具有相同的生命周期,这与上下文本身的生命周期不同。
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
→ 我不确定具体在哪里指定,但显然编译器推断返回字符串的生命周期与用于上下文的 's
参数相同。请注意,即使上下文本身被移动到 parse_context
,这只会影响上下文本身,而不影响它包含的字符串。
fn main()
{
let mut s = String::new();
→创建一个新字符串,有效期至main
结束
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
→ 创建一个新的上下文并将其移动到parse_context
。它将在 parse_context
结束时自动删除。它持有对字符串 s
的引用,该引用在 main
和 parse_context
结束之前有效 returns 与 s
具有相同生命周期的字符串 ⇒ text
有效期至main
.
结束
{
println!("{}", text);
}
}
→没问题:text
有效期至main
结束。
在The Rust Programming Language的第19.2章中,下面的例子编译没有任何错误。我从 issue #1834 中发现有一个新的生命周期省略规则隐含地使 's
比 'c
长。
虽然我找不到这个新省略规则的详细解释,但我猜它只不过是 更长、更明确的约束的隐式版本: <'c, 's: 'c>
。不过,我认为我的困惑可能与这个新的省略规则无关,但当然我对此可能是错误的。
我的理解是,parse_context
取得了 context
的所有权,因为它没有被借用,而是实际上移到了函数中。仅此一项就意味着 context
的生命周期应该与它所拥有的函数的生命周期相匹配,而不管我们在 Context
和 Parser
.[=27 中定义的生命周期和约束如何=]
根据这些定义,context
比临时 Parser
长的部分对我来说非常有意义(毕竟,我们定义了更长的生命周期),但 [=22] 的部分=] 当 context
在 parse_context
末尾超出范围时,引用不会被删除,我仍然可以安全地 return 它——让我感到困惑。
我错过了什么?编译器如何推断 returned &str
的生命周期?
更新示例
struct Context<'s>(&'s str);
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
fn main()
{
let mut s = String::new();
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
{
println!("{}", text);
}
}
您不是返回对函数拥有值的引用。您正在返回传入的引用的副本。
您的函数是恒等函数的精巧版本:
fn parse_context(s: &str) -> &str {
s
}
在您的实际代码中,您引用一个包含字符串切片的结构,然后引用另一个字符串切片,但所有这些引用都被丢弃。
例如,parse
中有一个不需要的引用:
fn parse(&self) -> Result<(), &'s str> {
Err( self.context.0)
// ^ no & needed
}
此外,如果您启用更多的 lints,您将被迫为您的函数签名添加更多的生命周期,这可能会让事情变得更清楚:
#![deny(rust_2018_idioms)]
fn parse_context(context: Context<'_>) -> Result<(), &'_ str> {
Parser { context: &context }.parse()
}
另请参阅:
Although I couldn't find a detailed explanation of this new elision rule,
T: 'a
inference in structs 在版本指南中。
感谢 Jmb 的评论和 Shepmaster 的一些回答,现在我确实很清楚我对 RAII 规则和所有权的困惑。
也就是说,字符串是在 main
范围内创建的,匿名 Context
实例 而不是 取得它只是借用的字符串的所有权一个引用,因此即使实例在 parse_context
的末尾与借用的引用一起被删除,复制到 Err
对象的引用仍然存在并指向现有的字符串——因此我们正在使用受约束的生命周期变量和编译器能够推断出内部字符串引用的生命周期。
struct Context<'s>(&'s str);
→ 类型 Context
的值包含一个具有生命周期 's
的字符串。此生命周期至少与上下文的生命周期一样长,但可能更长。
struct Parser<'c, 's>
{
context: &'c Context<'s>,
}
→ 类型 Parser
的值持有对具有某个生命周期 'c
的上下文的引用。此上下文包含一个具有 other 生命周期 's
.
impl<'c, 's> Parser<'c, 's>
{
fn parse(&self) -> Result<(), &'s str>
{
Err(self.context.0)
}
}
→ 函数 parse
returns 生命周期为 's
的值,即。 与存储在上下文中的字符串具有相同的生命周期,这与上下文本身的生命周期不同。
fn parse_context(context: Context) -> Result<(), &str>
{
Parser { context: &context }.parse()
}
→ 我不确定具体在哪里指定,但显然编译器推断返回字符串的生命周期与用于上下文的 's
参数相同。请注意,即使上下文本身被移动到 parse_context
,这只会影响上下文本身,而不影响它包含的字符串。
fn main()
{
let mut s = String::new();
→创建一个新字符串,有效期至main
s += "Avail";
s += "able?";
if let Err(text) = parse_context(Context(&s))
→ 创建一个新的上下文并将其移动到parse_context
。它将在 parse_context
结束时自动删除。它持有对字符串 s
的引用,该引用在 main
和 parse_context
结束之前有效 returns 与 s
具有相同生命周期的字符串 ⇒ text
有效期至main
.
{
println!("{}", text);
}
}
→没问题:text
有效期至main
结束。