Skip to content

← Closures and Iterators step 9 of 28

Easy Primitives

map, filter, filter_map: parse host:port lines

Extract ports from text lines using one filter_map closure.

pub fn parse_ports(lines: Vec<String>) -> Vec<u16>

For each line:

  1. Split at the first :. Skip the line if there is no colon.
  2. Parse the entire remainder as a decimal u16. Skip it if parsing fails.
  3. Keep the number only if it is at least 1024.

Return accepted ports in input order, including duplicates. Invalid lines produce no output element. An empty input returns an empty vector.

The host portion is ignored and may be empty. This is a text-parsing exercise, not a hostname validator or a general network-address parser. Do not trim either portion or add IPv6 handling. The threshold of 1024 is an exercise rule, not a check that a port is available on your machine.

Input line Result Reason
localhost:8080 Keep 8080 Valid number above the threshold.
h:1024 Keep 1024 The threshold is inclusive.
example.com:80 Skip Below the threshold.
:8080 Keep 8080 An empty host is allowed.
x:08080 or x:+8080 Keep 8080 Leading zeroes and a leading plus are accepted.
h:65535 Keep 65535 Largest u16 value.
h:65536 or h:-1 Skip Outside the accepted unsigned range.
h: 8080 or h:8080 Skip Port whitespace is not trimmed.
a:b:c or a::8080 Skip The remainder after the first colon is not a number.
bad or h: Skip Missing colon or empty port.

For example, ["localhost:8080", "example.com:80", "bad", "a:b"] produces [8080].

Choose an adapter that retains the parsed value

Adapter Closure result Effect when fully consumed
map A value U Produce one result for every input item.
filter A bool Keep matching items without changing their type.
filter_map An Option<U> Keep the value inside each Some; omit each None.

filter and filter_map can keep every item, some items, or none. They do not necessarily reduce the count. filter_map fits this task because parsing both decides whether a line is usable and produces the number to keep.

Use ? to skip an invalid line

Inside an Option-returning closure, ? unwraps a Some or returns None from that closure. It does not return from parse_ports or stop the iterator.

split_once(':') already returns an Option. Parsing returns a Result, so use .ok() to convert success into Some and an error into None. The error details are deliberately discarded because this task only returns valid ports.

Here is a complete example of the same pattern for plain numbers:

fn main() {
    let numbers: Vec<u16> = ["12", "bad", "34"]
        .into_iter()
        .filter_map(|text| {
            let number: u16 = text.parse().ok()?;
            Some(number + 1)
        })
        .collect();

    assert_eq!(numbers, [13, 35]);
}

For the port threshold, (port >= 1024).then_some(port) produces Some(port) when the condition is true and None otherwise. then_some evaluates its argument eagerly. Use then(|| ...) when a computation should run only if the condition is true; returning an already-computed port needs no closure.

Give parsing a target type

parse can produce many types. Its target must be supplied explicitly or inferred from surrounding code. Either of these bindings works inside the closure:

let port: u16 = text.parse().ok()?;
let port = text.parse::<u16>().ok()?;

The ::<u16> syntax is often called the turbofish. A standalone binding such as let port = "8080".parse(); has no information identifying the intended parsed type and causes a type-inference error.

Simplify the starter

The starter's is_some/unwrap and is_ok/unwrap stages produce the requested output, but Clippy rejects those patterns with warnings denied. In this exact pipeline the preceding filters make the unwraps safe. Combining selection and extraction makes that relationship easier to follow.

Replace the stages with a single filter_map closure containing the split, parse and threshold checks, then collect the accepted values. Avoid an unwrap in that closure: missing or invalid ports are expected input here.

A passing submission must compile, pass the tests, and pass clippy -D warnings. The single-closure structure is an exercise requirement; output checks and lints do not establish every aspect of implementation style.