Routing problems — given a network with costs on each link, find the cheapest path from one point to another — are completely solved when all costs are non-negative. The algorithm is named for Edsger Dijkstra, who published it in 1959. Every map application, every game pathfinder, every logistics platform uses it. Cost grows roughly with the size of the network — a network ten times bigger takes about eleven times longer to route through. Rust's petgraph and pathfinding libraries both implement it as a one-line call. When your team estimates more than a day on a routing feature with non-negative costs, they are either solving a different problem (negative costs? See page 09. Multiple stops in any order? Page 25) or they are not using the library.
08
Routing — the shortest path through a network
Routing problems are a settled category — and one of the most common places engineering teams waste weeks reinventing the standard answer.
- Build the road graph as a
DiGraphwith travel-minute edge weights, then calldijkstraonce. The return is aHashMapfrom each reachable intersection to the cheapest distance. Sixty years of algorithms, four lines of glue.use petgraph::algo::dijkstra; use petgraph::graph::{DiGraph, NodeIndex}; fn fastest_routes(road: &DiGraph<&str, u32>, from: NodeIndex) -> std::collections::HashMap<NodeIndex, u32> { dijkstra(road, from, None, |e| *e.weight()) } -
|e| *e.weight()is a closure — Rust's name for an anonymous function. Other languages call this a lambda. The pipes| |wrap the parameter list; everything after is the body. Here the closure takes one parametere(a reference to an edge), callse.weight()to get&u32(a reference to the weight stored inside the graph), then uses the prefix*— the dereference operator — to read the underlyingu32out of that reference. The closure tells Dijkstra how to extract a cost from each edge; swap it for a different closure and the algorithm uses a different cost.// |e| *e.weight() // │ │ // │ └── body: dereference the &u32 to get a u32 // └──────── parameter list: one parameter named e (an edge reference) // Same thing written as a named function: fn extract_weight(e: petgraph::graph::EdgeReference<u32>) -> u32 { *e.weight() } // You can swap in any cost function you want: dijkstra(road, from, None, |e| *e.weight()); // real travel minutes dijkstra(road, from, None, |e| *e.weight() * 2); // every road takes twice as long dijkstra(road, from, None, |_| 1u32); // ignore weights — count hops - Input shape: a directed graph where each node is an intersection and each edge has a
u32weight (travel minutes). Output shape: aHashMap<NodeIndex, u32>— for every node reachable fromfrom, the cheapest total cost to get there. Unreachable nodes are simply absent from the map. Below is a four-intersection example with Dijkstra run fromA— notice thatDis reached cheaper viaA → B → D(cost 3) than viaA → C → D(cost 13), and the map records only the winning total.Input — &DiGraph<&str, u32>: A ──2──▶ B ──1──▶ D │ ▲ 5 8 │ │ ▼ │ C ────────────────┘ Edges in the graph: A → B 2 A → C 5 B → D 1 C → D 8 Call: dijkstra(road, A, None, |e| *e.weight()) Output — HashMap<NodeIndex, u32>: { A → 0, // start: zero cost to reach yourself B → 2, // A → B C → 5, // A → C D → 3, // A → B → D = 2 + 1 (beats A → C → D = 5 + 8 = 13) }