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”
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();
}
"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)
}