简体   繁体   English

如何在Rust的哈希表中追加字符串值?

[英]How to append to string values in a hash table in Rust?

I have source files that contain text CSV lines for many products for a given day. 我的源文件包含给定日期许多产品的文本CSV行。 I want to use Rust to collate these files so that I end up with many new destination CSV files, one per product, each containing portions of the lines only specific to that product. 我想使用Rust来整理这些文件,以便最终得到许多新的目标CSV文件,每个产品一个,每个文件包含仅特定于该产品的行的一部分。

My current solution is to loop over the lines of the source files and use a HashMap<String, String> to gather the lines for each product. 我当前的解决方案是遍历源文件的行,并使用HashMap<String, String>收集每个产品的行。 I split each source line and use the element containing the product ID as a key, to obtain an Entry (occupied or vacant) in my HashMap . 我拆分了每个源代码行,并使用包含产品ID的元素作为键,以在我的HashMap获得一个Entry (已占用或空闲)。 If it is vacant, I initialize the value with a new String that is allocated up-front with a given capacity, so that I can efficiently append to it thereafter. 如果它是空的,我将使用一个新的String初始化该值,该String会预先分配给定的容量,以便以后可以有效地附加到该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));

I then want to append formatted elements of the same source line to this Entry . 然后,我想将同一源代码行的格式化元素添加到此Entry There are many examples online, such as 在线上有很多示例,例如
https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.entry https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.entry
of how to make this work if the HashMap value is an integer: HashMap值是整数的情况下如何实现此目的的说明:

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

I haven't figured out how to append more text to the Entry I obtain from my HashMap<String, String> using this kind of syntax, and I've done my best to research examples online. 我还没有弄清楚如何使用这种语法将更多文本添加到从HashMap<String, String>获得的Entry ,并且已尽力在网上研究示例。 There are surprisingly few examples anywhere of manipulating non-numeric entries in Rust data structures. 在Rust数据结构中操作非数字条目的任何地方都出乎意料地很少有示例。

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

Attempting to compile this produces the following error: 尝试编译它会产生以下错误:

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

How can I append to a String inside the Entry value? 如何在Entry值内附加到String

If you inspect the type returned by or_insert : 如果检查or_insert返回的类型:

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

You will see it is a mutable reference: 您将看到它是可变的参考:

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 `()`

That means that you can call any method that needs a &mut self receiver with no extra syntax: 这意味着您可以调用任何需要&mut self接收器的方法,而无需额外的语法:

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

Turning back to the integer form, what happens if we don't put the dereference? 回到整数形式,如果不放置取消引用会发生什么?

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

The difference is that the += operator automatically takes a mutable reference to the left-hand side of the expression. 区别在于+=运算符自动将可变引用引用到表达式的左侧。 Expanded, it might look something like this: 展开后,可能看起来像这样:

use std::ops::AddAssign;

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

Adding the explicit dereference brings the types back to one that has the trait implemented: 添加显式取消引用会使类型返回到已实现特征的一种:

use std::ops::AddAssign;

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

*mystringval.push_str(sourcePortion.as_str()); is parsed as *(mystringval.push_str(sourcePortion.as_str())); 被解析为*(mystringval.push_str(sourcePortion.as_str())); and since String::push_str returns () , you get the () cannot be dereferenced error. 并且由于String::push_str返回() ,您得到了() cannot be dereferenced错误。

Using parentheses around the dereference solves the precedence issue: 在取消引用周围使用括号可解决优先级问题:

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

The reason *myval += 1 works is because unary * has a higher precedence than += , which means it's parsed as *myval += 1起作用的原因是因为一元*的优先级高于+= ,这意味着它被解析为

(*myval) += 1

Since or_insert returns &mut V , you don't need to dereference it before calling its methods. 由于or_insert返回&mut V ,因此您无需在调用其方法之前对其进行解除引用。 The following also works: 以下内容也适用:

mystringval.push_str(sourcePortion.as_str());

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

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