Deriving and implementing

Let’s say you make a type: Person. It has a first name, last name, date of birth, and occupation. It has functions to get the whole name and their age.

use chrono::Date;
use chrono::offset::{*};

struct Person<'a> { 
	first_name: &'a str, 
	last_name: &'a str, 
	date_of_birth: Date<Utc>, 
	occupation: &'a str
}

//Constructors
impl <'a> Person<'a> {
	//no self param means this is a static method 
	fn new(first_name: &'a str,
		last_name: &'a str,
		year: u32,
		month: u32,
		day: u32,
		occupation: &'a str) -> Person<'a> {
		return Person {
			first_name,
			last_name,
			date_of_birth: Utc.ymd(year as i32, month, day), 
			occupation
		}; 
	}
}

//Methods
impl <'a> Person<'a> {
	//self param means this is an instance method 
	//as it's a reference this method does not consume 
	//the object
	fn whole_name(&self) -> String {
		return format!("{} {}", self.first_name, self.last_name); 
	}

	fn age_in_years(&self) -> i32 {
		let weeks = Utc::today().signed_duration_since(self.date_of_birth).num_weeks();
		return (weeks / 52) as i32;
	} 

	//the self here isn't a reference so the object
	//is consumed by this method and won't exist
	//after this is method is called
	fn into_tuple(self) -> (String, i32) {
		return (self.whole_name(), self.age_in_years());
	}
}

fn main() {
	//Double colon is for static methods
	let person = Person::new("John", "Smith", 1988, 07, 10, "Author");
	 
	//Period is for instance methods
	println!("{} is a {} who is {} years old.", 
		person.whole_name(), 
		person.occupation, 
		person.age_in_years());
}

The 'a lifetime tells Rust that the &strs will be available as long as the parent Person is. The sample uses the chrono crate, it is a simple to use and common date and time library. If we want to print the object we must implement the Display like this:

#![allow(unused)]
fn main() {
use std::fmt;

struct Person { 
first_name: String, 
last_name: String, 
occupation: String
}

impl fmt::Display for Person {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		return write!(f, "({} {}, {})", 
			self.first_name, 
			self.last_name, 
			self.occupation);
	} 
}
}

You can now write println!("{}", person), there are many traits that can be implemented for any struct that’s part of your project. To avoid boilerplate Rust can automatically derive some traits for structs like so:

#![allow(unused)]
fn main() {
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Default)]
struct Foo {} 
}
TraitUse
DebugAutomatically generates the equivalent of data classes toString(), use with {:?} instead of {}
CloneImplements the clone() method on the struct
CopyAllows structs to be cloned automatically instead of transferring ownership when assigned to new variable
PartialEqImplements equality checking and enables use of the == and != operators on the struct
PartialOrdImplements comparison and enables use of the > and < operators on the struct for types where the comparison may be impossible (e.g. floating numbers)
EqMarker trait (like Sync) meaning that all fields can be always and correctly compared, not valid for all types (e.g. floating numbers)
OrdSame as PartialOrd but for types where comparison is always possible
HashAutomatically generates the equivalent of data classes hashCode(), required to use the struct as key in HashMaps
DefaultImplements a default value for all fields, see Default

All of these require all the fields in the struct to implement the same traits. Numbers, strings, etc implement all the built in derivable types. As with PartialEq and Eq Rust often has two versions of a trait, one that is allowed to fail (and so will generally return Result or Option) and another that is not allowed to fail. In this case Eq will panic if something goes wrong, likewise there is From and TryFrom for converting structs, From will panic if it fails and TryFrom will return Err if it fails.