In PHP
One array does both jobs. $numbers[] = 10 grows a list. $scores["Ada"] = 10 stores a value under a string key. Keys keep insertion order. A missing key is handled with ??.
<?php
$numbers = [];
$numbers[] = 10;
$numbers[] = 30;
$numbers[] = 20;
echo $numbers[0], "\n";
$counts = [];
foreach (preg_split("/\s+/", "the cat and the dog") as $word) {
$counts[$word] = ($counts[$word] ?? 0) + 1;
}
foreach ($counts as $word => $count) {
echo "$word $count\n";
}
The counts print in insertion order: the, cat, and, dog.
array_map and array_filter become iterator chains. Iterators have their own chapter. A nested array used as a record becomes a struct (chapter 4). JSON arrives through serde, in the API chapter.
In Rust
A list is a Vec<T>. vec![] starts it. push, pop, len, is_empty, and contains are the usual operations. Every element has the same type. Mixed types need an enum (chapter 4).
numbers[0] reads an element. On a Vec, numbers[9] compiles and then panics, because the length is not part of the type:
thread 'main' panicked at src/main.rs:3:27:
index out of bounds: the len is 3 but the index is 9
numbers.get(9) is None. for n in &numbers reads. for n in &mut numbers writes. sort orders the values. sort_by_key orders by one field.
A string-keyed array is a HashMap<String, V>. Build the key with .to_string(). insert stores it. get("Ada") looks it up and returns Option. contains_key and remove do what they say. Walking the map visits every pair.
*n += 1 and *counts.entry(...): * writes through to the value the loop or entry points at. The Rust side of $counts[$word] = ($counts[$word] ?? 0) + 1 is *counts.entry(word.to_string()).or_insert(0) += 1. entry(...).or_default() is the same idea when the value type has a useful Default (for example 0 for integers). retain keeps only matching entries. extend appends from another iterator. The String is how the map owns the word. get("Ada") looks up with the text of the key.
A set of unique values is a HashSet. PHP often fakes that with array_unique or array_flip so the keys act as the set. Rust has a real set type: use std::collections::HashSet; then HashSet::from([1000, 2500, 800]).
Watch for: A PHP array keeps insertion order.
HashMapiteration order is unspecified, and it can change between runs.BTreeMapwalks keys in sorted order. Theindexmapcrate keeps insertion order when that is the behaviour you need.
contains(&30): & lends the value for the call. Chapter 3 explains. |row| row.1 is a closure, a small inline function. Closures get their own lesson.
use std::collections::{BTreeMap, HashMap};
fn main() {
let mut numbers: Vec<i32> = vec![];
let empty = numbers.is_empty();
println!("{empty}");
numbers.push(10);
numbers.push(30);
numbers.push(20);
let len = numbers.len();
let has_30 = numbers.contains(&30);
let first = numbers[0];
println!("{len} {has_30} {first}");
let missing = numbers.get(9);
println!("{missing:?}");
for n in &numbers {
println!("item {n}");
}
for n in &mut numbers {
*n += 1;
}
numbers.sort();
println!("{numbers:?}");
let last = numbers.pop();
println!("{last:?}");
let mut rows = vec![("pear", 2), ("apple", 1)];
rows.sort_by_key(|row| row.1);
println!("{rows:?}");
let mut scores = HashMap::new();
scores.insert("Ada".to_string(), 10);
scores.insert("Grace".to_string(), 12);
let ada = scores.get("Ada");
let has_ada = scores.contains_key("Ada");
println!("{ada:?} {has_ada}");
for (name, score) in &scores {
println!("{name} {score}");
}
let removed = scores.remove("Grace");
println!("{removed:?}");
let sentence = "the cat and the dog";
let mut counts = BTreeMap::new();
for word in sentence.split_whitespace() {
*counts.entry(word.to_string()).or_insert(0) += 1;
}
for (word, count) in &counts {
println!("{word} {count}");
}
}
empty is true. After the pushes, the line is 3 true 10, and missing is None. The loop prints item 10, item 30, item 20. After adding one and sorting, the vec is [11, 21, 31] and pop returns Some(31). sort_by_key prints [("apple", 1), ("pear", 2)]. get("Ada") is Some(10). The two names can print in either order. remove returns Some(12). The BTreeMap prints and, cat, dog, then the.
Idiomatic Rust:
Vecfor a list,HashMap<String, V>for string keys,BTreeMapwhen the printout has to be sorted..getwhen the index or the key might be absent.
Side by side
| PHP | Rust | |
|---|---|---|
| List | $n[] = 10 |
Vec<T>, push |
| Read | $n[0], missing is null |
[0] panics. .get is Option |
| String keys | $scores["Ada"] |
HashMap, insert with .to_string(), get("Ada") |
| Count | ($counts[$w] ?? 0) + 1 |
*counts.entry(word.to_string()).or_insert(0) += 1 |
| Order | Insertion order | HashMap is unspecified. BTreeMap is sorted |
Try it
Open src/main.rs in the Playground. The last four lines are the word counts in sorted order: and 1, cat 1, dog 1, the 2. Change the sentence so a word repeats, and run it again. That word's count goes up, and the lines stay alphabetical.
Key takeaways
Vec<T>is the list:push,pop,len,is_empty,contains,sort,sort_by_key. One element type.v[i]panics wheniis out of range.v.get(i)returnsOption.for n in &vreads, andfor n in &mut vwrites.HashMap<String, V>is the associative array.getreturnsOption. Build keys with.to_string().entry().or_insert/or_default,retain, andextendcover common map edits.HashSetis the unique-value set (array_unique/array_flipin PHP).HashMaporder can change between runs.BTreeMapprints keys in sorted order.
What's next: Strings at a glance