如果/ else阻塞,如何在迭代器中使用String而不是& str

时间:2015-08-20 13:44:10

标签: rust

有人会友好地向我解释为什么在此脚本中使用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 个答案:

答案 0 :(得分: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 }

它或多或少地告诉你完全它不起作用。您尝试将借用的指针插入到String l到hashmap中。但是,String的效果还不够长。

具体而言,Rust以反向词汇顺序销毁值。因此,当执行到达函数末尾时,Rust将首先释放l ,然后释放hash。这是一个问题:它意味着有一个窗口,其中hash包含指向已销毁数据的指针,Rust绝对将不允许

这适用于&str的原因是字符串文字"like this"不仅仅是&str;它实际上是&'static str。这意味着字符串文字在程序的整个持续时间内“存在”:它从不被破坏,因此hashmap可以安全地保存指向它的指针。

解决方案是确保StringHashMap

更长久
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。只要你在阅读或使用它之前初始化它就可以保留未初始化的变量。

答案 1 :(得分:2)

如果您将l更改为String,则会获得:

  

错误:l活得不够长

这是真的:

name是对l的一个子字符串的引用。您可能会将name引用插入hash,但hash的生命周期比l的生命周期长。因此,当l的生命周期结束时,hash将包含无效引用。这是不允许的。 F.ex.如果删除insert行,Rust很高兴。

根据您的需要,有多种方法可以解决这个问题。其中之一是hash的生命周期短于l的生命周期,通过在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");
    }
}

或者,您可以在地图中存储字符串的副本。

答案 2 :(得分:1)

您有终身问题。

插入hash的任何对象只能引用比hash更长的内容(或根本不引用任何内容)。

但是,在此处,您在l之后定义hash,因此l的生命周期更短。反过来,这意味着引用name缓冲区的l的生命周期比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>来避免生命周期问题。