【问题标题】:Immutable Strings and Cloning不可变字符串和克隆
【发布时间】:2020-06-26 19:40:37
【问题描述】:

我的心态是让我的Strings 不可变,这是一个单一的事实来源。 当我将同样的心态带入 Rust 时,我发现我必须做很多克隆。 由于Strings 没有改变,所有的克隆都是不必要的。 下面有一个例子和link to the relevant playground

借用似乎不是一种选择,因为我必须处理引用及其生命周期。我的下一个想法是使用类似RcCow 结构的东西。但是用Rc 之类的东西包裹所有Strings 感觉不自然。在我有限的 Rust 经验中,我从未见过任何暴露的所有权/内存管理结构,即 RcCow。我很好奇一个更有经验的 Rust 开发人员会如何处理这样的问题。

在 Rust 中公开所有权/内存管理结构,如 RcCow 是否真的很自然?我应该使用切片吗?

use std::collections::HashSet;

#[derive(Debug)]
enum Check {
    Known(String),
    Duplicate(String),
    Missing(String),
    Unknown(String)
}

fn main() {
    let known_values: HashSet<_> = [
        "a".to_string(),
        "b".to_string(),
        "c".to_string()]
            .iter().cloned().collect();

    let provided_values = vec![
        "a".to_string(),
        "b".to_string(),
        "z".to_string(),
        "b".to_string()
    ];

    let mut found = HashSet::new();

    let mut check_values: Vec<_> = provided_values.iter().cloned()
        .map(|v| {
            if known_values.contains(&v) {
                if found.contains(&v) {
                    Check::Duplicate(v)
                } else {
                    found.insert(v.clone());
                    Check::Known(v)
                }
            } else {
                Check::Unknown(v)
            }
        }).collect();

    let missing = known_values.difference(&found);

    check_values = missing
        .cloned()
        .fold(check_values, |mut cv, m| {
            cv.push(Check::Missing(m));
            cv
        });

    println!("check_values: {:#?}", check_values);
}

【问题讨论】:

  • 在我有限的 Rust 经验中,我从未见过任何暴露的内存管理结构 -- StringVec 管理自己的内存,就像 Rc是的。
  • 在像 Java 这样的语言中,字符串是通过引用访问的,并且相同的对象被许多代码段共享。 Rust 不是这样工作的。所有权是明确的。您必须故意传递&amp;mut 引用或类似的引用,以使其他一些代码改变您拥有的字符串。我认为你从另一种语言中继承了不适用于 Rust 的习惯。
  • @trentcl 这不是我的问题的重点。是的,我知道StringVec 管理自己的记忆,但他们仍然屈服于所有权规则。我不能在不移动或借用的情况下通过StringVec。这就是我在问题中关注RcCow 的原因。
  • @JohnKugelman 我使用多种语言工作,所以我很可能会继承另一种语言的习惯。
  • 对于您的示例,所有字符串的生命周期都足够长,您可以在任何地方使用 &str。 play.rust-lang.org/…。如果可以的话,我倾向于使用 &str,并且只有在我真的需要独立拥有一个值或构造一个新值时才使用 String。

标签: memory-management rust ownership ownership-semantics


【解决方案1】:

从我的问题的 cmets 中的讨论来看,示例中所有不可变 Strings 的克隆都是正确的。由于 Rust 通过所有权而不是其他语言的引用来处理内存,因此克隆是必要的。

在最好的情况下,不使用Rc,我可以通过在provided_values 上使用移动语义来减少克隆。

更新:一些有趣的阅读

Cow 在我的示例中不起作用,因为它涉及引用的借用。 Rc 将是我必须使用的。在我的示例中,所有内容都必须转换为Rc,但我可以看到这一切都可以通过封装隐藏起来。


use std::collections::HashSet;
use std::rc::Rc;

#[derive(Debug)]
enum Check {
    Known(Rc<String>),
    Duplicate(Rc<String>),
    Missing(Rc<String>),
    Unknown(Rc<String>)
}

fn main() {
    let known_values: HashSet<_> = [
        Rc::new("a".to_string()),
        Rc::new("b".to_string()),
        Rc::new("c".to_string())]
            .iter().cloned().collect();

    let provided_values = vec![
        Rc::new("a".to_string()),
        Rc::new("b".to_string()),
        Rc::new("z".to_string()),
        Rc::new("b".to_string())
    ];

    let mut found = HashSet::new();

    let mut check_values: Vec<_> = provided_values.iter().cloned()
        .map(|v| {
            if known_values.contains(&v) {
                if found.contains(&v) {
                    Check::Duplicate(v)
                } else {
                    found.insert(v.clone());
                    Check::Known(v)
                }
            } else {
                Check::Unknown(v)
            }
        }).collect();

    let missing = known_values.difference(&found);

    check_values = missing
        .cloned()
        .fold(check_values, |mut cv, m| {
            cv.push(Check::Missing(m));
            cv
        });

    println!("check_values: {:#?}", check_values);
}

Playground

【讨论】:

  • 您可能还想尝试Rc&lt;str&gt; 而不是Rc&lt;String&gt; - 因为您的字符串无论如何都是不可变的,这也可能有效,只需少一层间接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 2012-05-06
  • 1970-01-01
  • 2014-09-28
相关资源
最近更新 更多