繁体   English   中英

为什么 Rust 中的 &str 数组作为参数传递有不同的生命周期?

[英]Why do &str arrays in Rust passed as parameters have different lifetimes?

我正在学习 Rust 并且正在测试通过函数进行的一些数组复制。 我确信有内置的 Rust 函数来复制/克隆数组信息,但是我认为个人实现是一个好主意,可以帮助我理解通过函数传递引用。

fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]) {
    // copy 1 into 2
    for i in 0..60 {
        a2[i] = a1[i];
    } // change is reflected in a2 as it is passed as &mut
}

但是,这引发了错误, these two types are declared with different lifetimes...对于&str类型本身。 经过一些进一步的学习,我尝试声明自己的一生并将它们分配给它,然后就解决了!

fn copy_str_arr_fix<'a> (a1: [&'a str; 60], a2: &mut [&'a str; 60]) {
    // copy 1 into 2
    for i in 0..60 {
        a2[i] = a1[i];
    } // change is reflected in a2 as it is passed as &mut
}

但是,为什么会这样呢? 为什么数组中的值类型需要分配生命周期而不是参数本身? 换句话说,为什么这根本不起作用?

fn copy_str_arr_bad<'a> (a1: &'a [&str; 60], a2: &'a mut [&str; 60]) {
    // does not work...           ^-----------------------^-------- different lifetimes
    for i in 0..60 {
        a2[i] = a1[i]; 
    } 
}

我仍在努力了解生命周期在更复杂的对象(如数组和结构)的上下文中是如何工作的,因此将不胜感激任何解释!

错误消息有点令人困惑,因为它指的是根据生命周期省略规则生成的生命周期 在您的情况下,终身省略意味着:

fn copy_str_arr_original(a1: [&str; 60], a2: &mut [&str; 60])

是语法糖:

fn copy_str_arr_original<'a1, 'a2_mut, 'a2>(a1: [&'a1 str; 60], a2: &'a2_mut mut [&'a2 str; 60])

换句话说,我们有三个完全不相关的生命。 “不相关”意味着调用者可以选择与他们相关联的对象存活多长时间。 例如, a2的字符串可能是静态的并且一直存在到程序结束,而a1的字符串可能会在copy_str_arr_original()返回后立即删除。 或者反过来。 如果这对您来说是个问题,那么您就在正确的轨道上,因为借用检查员同意您的意见。

请注意,有点违反直觉, 'a2_mut生命周期的长度完全无关,它可以根据调用者的喜好长或短。 我们的函数已经收到引用,因此可以在函数的作用域内使用它。 'a2_mut生命周期告诉我们它将在函数范围之外存活多久,我们只是不关心这个。

'a1'a2是另一回事。 由于我们将引用从a1复制到a2 ,我们有效地a1的引用(类型为&'a1 str )转换为存储在a2的引用类型(即&'a2 str ):

a2[i] = a1[i];  // cast &'a1 str to &'a2 str

为了使其有效, &'a1 str必须是&'a2 str子类型 虽然 Rust 没有 C++ 意义上的类和子类,但它确实有涉及生命周期的子类型。 从这个意义上说,如果 A 值的值保证至少与 B 的值一样长,则 A 是 B 的子类型。换句话说, 'a1必须至少离开'a2 ,这表示为'a1: a2 所以这编译:

fn copy_str_arr<'a1: 'a2, 'a2, 'a2_mut>(a1: [&'a1 str; 60], a2: &'a2_mut mut [&'a2 str; 60]) {
    for i in 0..60 {
        a2[i] = a1[i];
    }
}

强制转换成功的另一种方法是只要求生命周期相同,您在代码中有效地做到了这一点。 (您还省略了'a2_mut生命周期,编译器正确地将其解释为对无关匿名生命周期的请求。)

假设您可以使用两个不同的、不相关的生命周期定义copy_str_arr ,如下所示:

fn copy_str_arr<'a, 'b>(a1: [&'a str; 60], a2: &mut [&'b str; 60]) {
    // ...
}

然后考虑这个例子:

let mut outer: [&str; 60] = [""; 60];

{
    let temp_string = String::from("temporary string");
    
    let inner: [&str; 60] = [&temp_string; 60];

    // this compiles because our bad `copy_str_arr` function allows
    // `inner` and `outer` to have unrelated lifetimes
    copy_str_array(&inner, &mut outer); 

}   // <-- `temp_string` destroyed here

// now `outer` contains references to `temp_string` here, which is invalid
// because it has already been destroyed!

println!("{:?}", outer); // undefined behavior! may print garbage, crash your
                         // program, make your computer catch fire or anything else

如您所见,如果允许a1a2具有完全不相关的生命周期,那么我们最终可能会遇到这样一种情况,其中一个数组包含对无效数据的引用,这是非常糟糕的

但是,寿命不必相同。 您可以改为要求您正在复制的生命周期比您复制到的生命周期更长(从而确保您不会非法延长引用的生命周期):

fn copy_str_arr<'a, 'b>(a1: &[&'a str; 60], a2: &mut [&'b str; 60])
where
    'a: 'b, // 'a (source) outlives 'b (destination)
{
    for i in 0..60 {
        a2[i] = a1[i];
    }
}

简单的答案是编译器不是很聪明。

您不必在每次定义处理引用的函数时都指定一堆生命周期,这仅仅是因为编译器在可能的情况下进行了一些有根据的猜测 所以它有点聪明,但不是很聪明。

假设您正在编写一个函数,该函数接受对结构的引用并返回对该结构中字段的引用:

struct Book {
  pages: u16,
  title: String,
}

fn borrow_title(book: &Book) -> &str {
  &book.title
}

十分之九它确实是对您传递的参数的引用。 但有时不是:

fn borrow_title(book: &Book) -> &'static str {
  if book.pages > 10 {
    "Too long..."
  } else {
    "Not long enough"
  }
}

如您所见,您需要指定返回的&str具有不同的生命周期(在这种情况下,特殊的'static .

所以既然你说fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]) ,编译器实际上并没有推理你的实现,也不知道a1中引用的生命周期应该至少是只要a2中任何引用的生命周期。

至于第二部分,您需要考虑引用只是指向某些数据的指针。 该数据可以包含其他引用。 在这种情况下,重要的是这些其他参考。

这里有 2 个字符串引用数组。 假设您将引用从第一个复制到第二个。 是否通过引用将这些数组传递给函数并不重要。 重要的是,如果第一个数组的所有权被删除,字符串也会被删除。 如果第二个数组仍然持有任何引用,这将导致不安全的内存处理。

为简化起见,让我们考虑只有一个字符串,我们要将值借用到一个数组中,然后将这些借用值复制到另一个数组中,删除第一个数组,然后删除该字符串。 你希望发生什么?

编译器将抛出一个合适的问题,以确保没有对字符串的引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM