如何在迭代器、if/else块中使用字符串(String)而不是&str?

3

请问为什么在这个脚本中使用String不起作用,但使用&str可以?另外,我该如何修改它,让它可以适用于String?[版本 1.2]

use std::collections::{HashMap};

fn main() { 

    let mut hash = HashMap::<&str, &str>::new();
    hash.insert("this", "value");
    let l: &str = "this is a borrowed string reference";
    // If the above line was defined as:
    //let l: String = "this is a string".to_string();

    let mut all = l.split(" ");   
    let name: &str = all.next().unwrap();

    if hash.contains_key(name) == true {
        hash.remove(name);
    } else {
        hash.insert(name, "stuff");
    }
}

3
“String” 在哪里?有四个不同的地方可以用“String”替换“&str”,每个地方都有不同的原因,可能会起作用或不起作用。您是否只是指更改“l”的类型? - DK.
3个回答

5

好的,让我们把重点放在必要的事情上:

use std::collections::HashMap;

fn main() { 
    let mut hash = HashMap::<&str, &str>::new();
    hash.insert("this", "value");
    let l: String = "this is a borrowed string reference".to_string();
    hash.insert(&l, "stuff");
}

编译这个代码会得到以下结果:
<anon>:7:18: 7:19 error: `l` does not live long enough
<anon>:7     hash.insert(&l, "stuff");
                          ^
<anon>:4:49: 8:2 note: reference must be valid for the block suffix following statement 0 at 4:48...
<anon>:4     let mut hash = HashMap::<&str, &str>::new();
<anon>:5     hash.insert("this", "value");
<anon>:6     let l: String = "this is a borrowed string reference".to_string();
<anon>:7     hash.insert(&l, "stuff");
<anon>:8 }
<anon>:6:71: 8:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 6:70
<anon>:6     let l: String = "this is a borrowed string reference".to_string();
<anon>:7     hash.insert(&l, "stuff");
<anon>:8 }

这段话大致意思是:当你试图将一个借用的指向字符串 l 的指针插入哈希表时,会发现这个字符串并没有活得足够久。具体来说,Rust 按照反向词法顺序销毁值。因此,在函数执行到末尾时,Rust 首先释放 l,然后是 hash。这是一个问题:这意味着在 hash 包含对已销毁数据的指针的窗口期间,Rust 绝对不允许这种情况发生。使用 &str 的原因是字符串字面量 "like this" 不仅仅是 &str 类型;它实际上是 &'static str 类型。这意味着字符串字面量在整个程序的生命周期内都存在:它永远不会被销毁,因此哈希表可以安全地持有指向它的指针。解决方法是确保 String 活得比 HashMap 更长。
use std::collections::HashMap;

fn main() { 
    // Declare `l` here ...
    let l;
    let mut hash = HashMap::<&str, &str>::new();
    hash.insert("this", "value");

    // ... but initialise it *here*.
    l = "this is a borrowed string reference".to_string();
    hash.insert(&l, "stuff");
}

现在,首先销毁hash,然后是l。变量未初始化也没关系,只要在读取或使用之前进行初始化即可。

谢谢,我之前真的被卡住了一段时间,现在非常清楚了! - kezzos
"Rust按照声明的相反顺序销毁值" - 不,不是按照词法相反顺序销毁:http://is.gd/LEBmOi - emlai
"lexical" 指按照字典顺序出现的顺序;即在文本中。 - DK.

2
如果将l 改为 String,你会得到如下错误信息:

错误: l 的生命周期不够长

这是正确的: name 是对 l 子字符串之一的引用。你可能正在将 name 引用插入到 hash 中,但是 hash 的生命周期比 l 的生命周期更长。因此,当 l 生命周期结束时,hash 将包含无效的引用。这是不允许的。例如,如果删除 insert 行,则 Rust 可以正常运行。
有多种方法可以解决这个问题,具体取决于你的需求。其中一种方法是通过在实例化 hash 之后来缩短 hash 的生命周期,使其比 l 的生命周期更短。
use std::collections::{HashMap};

fn main() { 
    // let l: &str = "this is a borrowed string reference";
    // If the above line was defined as:
    let l: String = "this is a string".to_string();

    let mut hash = HashMap::<&str, &str>::new();
    hash.insert("this", "value");

    let mut all = l.split(" ");   
    let name: &str = all.next().unwrap();

    if hash.contains_key(name) == true {
        hash.remove(name);
    } else {
        hash.insert(name, "stuff");
    }
}

或者,您可以将字符串的副本存储在映射中。

1

你有一个终身问题。

任何插入到hash中的对象必须只引用超过hash的生存期的东西(或根本不引用任何东西)。

然而,在这里,你在hash之后定义了l,因此l的寿命更短。反过来,这意味着引用l缓冲区的name的寿命比hash短,因此name不适合插入到hash中(尽管可以用于搜索/删除)。

改变定义lhash的顺序使其工作:

use std::collections::{HashMap};

fn main() { 
    let l: String = "this is a string".to_string();

    let mut hash = HashMap::<&str, &str>::new();
    hash.insert("this", "value");

    let mut all = l.split(" ");   
    let name: &str = all.next().unwrap();

    if hash.contains_key(name) == true {
        hash.remove(name);
    } else {
        hash.insert(name, "stuff");
    }
}

如果不可能实现这一点,那么使用一个 HashMap<String, String> 来避免生命周期问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接