From Trait

One of the more confusing examples for me to learn when studying Dave Macleod’s excellent “Rust in a Month of Lunches” was page 145.

Check the “Rust Book” for the official description of the from trait : https://doc.rust-lang.org/std/convert/trait.From.html


I modified the example to use cities in England to help me relate to it!

Two cities, London and Manchester, are first collected in a Vec<City> and then converted into a Country struct, where the cities are contained.

“Country” struct is populated with the values FROM cities – it’s this struct “Country” that we later use with the print function and do a “custom print out”

lady from trait
fn main() {
    let london = City::new("London", 8_982_000);
    let manchester = City::new("Manchester", 547_627);

    let england_cities = vec![london, manchester];
    let england = Country::from(england_cities);

    england.print_cities();
}
scrapeops
"London" has a population of 8982000.
"Manchester" has a population of 547627.

The code

1. Define the City struct

We define a City with a name and population. Each city in England will be represented by a City struct.

#[derive(Debug)]
struct City {
    name: String,
    population: u32,
}

impl City {
    fn new(name: &str, population: u32) -> Self {
        Self {
            name: name.to_string(),
            population,
        }
    }
}

The new method helps create instances of a City by passing in the city’s name and population.

2. Define the Country struct

Next, we define a Country, which will hold a collection of cities (a Vec<City>).

#[derive(Debug)]
struct Country {
    cities: Vec<City>,
}

3. Implement the From trait

Now, we’ll implement the From<Vec<City>> for Country. This allows us to convert a Vec<City> into a Country.

This makes it simple to transform a vector of cities into a Country using Country::from() or .into().

aka Create a Country FROM a Vec<City>

impl From<Vec<City>> for Country {
    fn from(cities: Vec<City>) -> Self {
        Self { cities }
    }
}

The FROM trait

4. Implement a method for printing city details

We’ll create a method in the Country struct to iterate over the cities and print out their details.

impl Country {
    fn print_cities(&self) {
        for city in &self.cities {
            println!("{:?} has a population of {:?}.", city.name, city.population);
        }
    }
}

Full code


#[derive(Debug)]
struct City {
    name: String,
    population: u32,
}

impl City {
    fn new(name: &str, population: u32) -> Self {
        Self {
            name: name.to_string(),
            population,
        }
    }
}

#[derive(Debug)]
struct Country {
    cities: Vec<City>,
}

impl From<Vec<City>> for Country {
    fn from(cities: Vec<City>) -> Self {
        Self { cities }
    }
}

impl Country {
    fn print_cities(&self) {
        for city in &self.cities {
            println!("{:?} has a population of {:?}.", city.name, city.population);
        }
    }
}

fn main() {
    let london = City::new("London", 8_982_000);
    let manchester = City::new("Manchester", 547_627);

    let england_cities = vec![london, manchester];
    let england = Country::from(england_cities);

    england.print_cities();
}
use std::fs;
use std::io;
use std::num;

enum CliError {
    IoError(io::Error),
    ParseError(num::ParseIntError),
}

impl From<io::Error> for CliError {
    fn from(error: io::Error) -> Self {
        CliError::IoError(error)
    }
}

impl From<num::ParseIntError> for CliError {
    fn from(error: num::ParseIntError) -> Self {
        CliError::ParseError(error)
    }
}

fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> {
    let mut contents = fs::read_to_string(&file_name)?;
    let num: i32 = contents.trim().parse()?;
    Ok(num)
}