【问题标题】:How to append to string values in a hash table in Rust?如何在 Rust 的哈希表中附加字符串值?
【发布时间】:2017-08-10 04:15:56
【问题描述】:

我的源文件包含给定日期的许多产品的文本 CSV 行。我想使用 Rust 整理这些文件,以便最终得到许多新的目标 CSV 文件,每个产品一个,每个包含仅特定于该产品的行的部分。

我当前的解决方案是遍历源文件的行并使用HashMap<String, String> 来收集每个产品的行。我拆分每个源行并使用包含产品 ID 的元素作为键,以在我的HashMap 中获得一个Entry(占用或空置)。如果它是空的,我会使用一个新的String 来初始化该值,该值是预先分配给给定容量的,以便之后我可以有效地附加到它。

// so far, so good (the first CSV item is the product ID)
let mystringval = productmap.entry(splitsource[0].to_owned()).or_insert(String::with_capacity(SOME_CAPACITY));

然后我想将同一源代码行的格式化元素附加到这个Entry。网上有很多例子,比如
https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.entry
如果HashMap 值是整数,如何使这项工作:

// this works if you obtain an Entry from a HashMap containing int vals
*myval += 1;

我还没有想出如何使用这种语法将更多文本附加到我从HashMap<String, String> 获得的Entry,并且我已经尽力在网上研究示例。在 Rust 数据结构中操作非数字条目的例子非常少。

// using the Entry obtained from my first code snippet above
*mystringval.push_str(sourcePortion.as_str());

尝试编译它会产生以下错误:

error: type `()` cannot be dereferenced
   --> coll.rs:102:17
    |
102 |                 *mystringval.push_str(sourcePortion.as_str());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

如何在Entry 值内附加String

【问题讨论】:

  • 只需mystringval.push_str(sourcePortion.as_str()) 就可以了。你可以试一试吗?
  • 是的,确实如此。我想这让我感到惊讶的是,我看到的所有更改 HashMap 条目的示例都包括取消引用字符。你介意写一个答案,包括为什么我的案子不需要那个吗?我很乐意将其标记为已接受。

标签: string hashmap rust append memory-efficient


【解决方案1】:

*mystringval.push_str(sourcePortion.as_str()); 被解析为*(mystringval.push_str(sourcePortion.as_str()));,并且由于String::push_str 返回(),您会得到() cannot be dereferenced 错误。

在取消引用周围使用括号解决了优先级问题:

(*mystringval).push_str(sourcePortion.as_str());

*myval += 1 起作用的原因是因为一元 * 的优先级高于 +=,这意味着它被解析为

(*myval) += 1

由于or_insert 返回&mut V,因此您无需在调用其方法之前取消引用它。以下也有效:

mystringval.push_str(sourcePortion.as_str());

【讨论】:

    【解决方案2】:

    如果你检查or_insert返回的类型:

    fn update_count(map: &mut HashMap<&str, u32>) {
        let () = map.entry("hello").or_insert(0);
    }
    

    你会看到它是一个可变引用:

    error[E0308]: mismatched types
     --> src/main.rs:4:9
      |
    4 |     let () = map.entry("hello").or_insert(0);
      |         ^^ expected &mut u32, found ()
      |
      = note: expected type `&mut u32`
                 found type `()`
    

    这意味着您可以调用任何需要&amp;mut self 接收器的方法,而无需额外的语法:

    fn update_mapping(map: &mut HashMap<&str, String>) {
        map.entry("hello").or_insert_with(String::new).push_str("wow")
    }
    

    回到整数形式,如果我们不放解引用会怎样?

    fn update_count(map: &mut HashMap<&str, i32>) {
        map.entry("hello").or_insert(0) += 1;
    }
    
    error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut i32`
     --> src/main.rs:4:5
      |
    4 |     map.entry("hello").or_insert(0) += 1;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use `+=` on type `&mut i32`
    
    error[E0067]: invalid left-hand side expression
     --> src/main.rs:4:5
      |
    4 |     map.entry("hello").or_insert(0) += 1;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid expression for left-hand side
    

    不同之处在于+= 运算符自动 采用对表达式左侧的可变引用。展开后,它可能看起来像这样:

    use std::ops::AddAssign;
    
    fn update_count(map: &mut HashMap<&str, i32>) {
        AddAssign::add_assign(&mut map.entry("hello").or_insert(0), 1);
    }
    

    添加显式取消引用会使类型恢复为已实现特征的类型:

    use std::ops::AddAssign;
    
    fn update_count(map: &mut HashMap<&str, i32>) {
        AddAssign::add_assign(&mut (*map.entry("hello").or_insert(0)), 1);
    }
    

    【讨论】:

    • 感谢@Shepmaster 的详细回答
    • @ezekiel68 确保对所有有用的答案进行投票,即使是您已接受的答案。
    • 你会成为我的一个像样的stackoverflow公民!
    • @ezekiel68 我们都是关于这些神奇的互联网点!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    • 2017-07-14
    相关资源
    最近更新 更多