From Rust documentation, this count
variable wouldn't work without dereferencing (*)
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
However, I have the following code which tries to update a u8 variable (i.e team_val.goals_scored
) in a Struct if the key is found in a hashmap. It works without dereferencing. My understanding from the above Rust documentation was I need to dereference the team_val.goals_scored
in order to update the content of the struct which is also a value for the hash map. Whelp!
My code:
#[derive(Debug)]
struct Team {
name: String,
goals_scored: u8,
goals_conceded: u8,
}
fn build_scores_table(results: String) -> HashMap<String, Team> {
// The name of the team is the key and its associated struct is the value.
let mut scores: HashMap<String, Team> = HashMap::new();
for r in results.lines() {
let v: Vec<&str> = r.split(',').collect();
let team_1_name = v[0].to_string();
let team_1_score: u8 = v[2].parse().unwrap();
let team_2_name = v[1].to_string();
let team_2_score: u8 = v[3].parse().unwrap();
// TODO: Populate the scores table with details extracted from the
// current line. Keep in mind that goals scored by team_1
// will be number of goals conceded from team_2, and similarly
// goals scored by team_2 will be the number of goals conceded by
// team_1.
let mut team_1_struct= Team {
name: team_1_name.clone(),
goals_scored: team_1_score,
goals_conceded: team_2_score
};
let mut team_2_struct= Team {
name: team_2_name.clone(),
goals_scored: team_2_score,
goals_conceded: team_1_score
};
if scores.contains_key(&team_1_name) {
let team_val = scores.entry(team_1_name.clone()).or_insert(team_1_struct);
println!("Yooo {:#?}",team_val);
team_val.goals_scored +=team_1_score;
team_val.goals_conceded += team_2_score;
} else {
scores.insert(team_1_name,team_1_struct);
}
if scores.contains_key(&team_2_name) {
let team_val = scores.entry(team_2_name.clone()).or_insert(team_2_struct);
println!("Yooo {:#?}",team_val);
team_val.goals_scored +=team_2_score;
team_val.goals_conceded += team_1_score;
} else {
scores.insert(team_2_name,team_2_struct);
}
}
scores
}
Rust does some automatic dereferencing, described here. We can see the difference between the documentation code and what you wrote:
// This
*count += 1
// versus this
team_val.goals_scored += team_1_score
^--- Causes an automatic dereferencing
If you're coming from C I think this documentation may be even clearer.
Answering the follow-up question 'can you use entry()
without using clone()
on the key' - you cannot. entry()
consumes what you send it, and doesn't give it back, so the borrow checker will prevent you from doing this. It's currently a 'limitation' on the API - but if you're dealing with something as cheap as a short string to copy, then this shouldn't impact you much.
You can do a fair amount to slim down your code, though (with the caveat that I only did this for one team - it's easily extensible):
use std::collections::HashMap;
struct Team {
name: String,
goals: u8
}
type Scorecard = HashMap<String, Team>;
fn scoring(records: String) -> Scorecard {
let mut scores : Scorecard = HashMap::new();
for r in records.lines() {
let record: Vec<&str> = r.split(',').collect();
let team_name = record[0].to_string();
let team_score: u8 = record[1].parse().unwrap();
// Note that we do not need to create teams here on every iteration.
// There is a chance they already exist, and we can create them only if
// the `or_insert()` clause is activated.
// Note that the `entry()` clause when used with `or_insert()`
// gives you an implicit 'does this key exist?' check.
let team = scores.entry(team_name.clone()).or_insert(Team {
name: team_name,
goals: 0,
});
team.goals += team_score;
}
scores
}
fn main() {
let record = "Thunderers,1\nFlashians,1\nThunderers,2";
let scores = scoring(record.to_string());
for (key, value) in &scores {
println!("{}: The mighty {} scored {} points.", key, value.name, value.goals)
}
// Flattening Some Options!
// let o = Some(Some(5));
// let p = Some(5);
// println!("o: {:#?}", o);
// println!("p: {:#?}", p);
// println!("flattened o: {:#?}", o.flatten());
// println!("flattened p: {:#?}", p.flatten());
}