main.rs
use std::io;fn main() {let mut input = String::new();println!("Say something");match io::stdin().read_line(&mut input) {Ok(_) => {println!("You said {}", input)},Err(e) => {println!("Something went wrong {}", e)}}}
Line comments
// This is a line comment
Multi line comments are allowed but rarely used
/* This is notvery common*/
Doc comments
/// This is mainly used to document functionality
Heading
//! # Main heading
Code
//! ```//! fn main() {...}//! ```
println!("Hello world!");
Formatting
println!("My name is {} and I'm {} years old", "Alex", 29);
Expressions
println!("a + b = {}", 3 + 6)
Positional arguments
println!("{0} has a {2} and {0} has a {1}", "Alex", "cat", "dog");
Named arguments
println!("{name} {surname}", surname="Smith", name="Alex");
Printing traits
print!("binary: {:b}, Hex: {:x}, Octal: {:o}", 5, 5, 5);
Debug
println!("Array {:?}", [1, 2, 3]);
let name = "Michael"let age = 32
Rust is a strongly typed language
Variable type is optional if it can be inferred
Type can be specified explicitly
let amount: i64 = 8473926758472
let amount = 8473926758472 // error
Names can contain letters, numbers, and underscores
Must start with a letter or underscore
Immutable by default
let length = 34;length = 35; // error
Can be declared mutable
let mut length = 34;length = 35;
Shadowing is allowed
let color = "blue";let color = "red";println!("Color is {}", color); // Color is red
Declaring multiple variables simultaneously
let (a, b, c) = (2, 3, 4);
Integer
Size | Signed | Unsigned |
---|---|---|
8bit | i8 | u8 |
16bit | i16 | u16 |
32bit | i32 | u32 |
64bit | i64 | u64 |
128bit | i128 | u128 |
arch | isize | usize |
Float
Size | Float |
---|---|
32bit | f32 |
64bit | f64 |
Type casting
let pi: f32 = 4; // mismatched types error
Number separator
let million = 1_000_000;let random = 3_836.45_346_546;
Boolean
let is_day = true;let is_night = false;
Character
let char1 = 'A';let smiley_face = '\u{1F601}';
String slices
let cat: &str = "Fluffy";
let cat: &'static str = "Fluffy";
String slices are immutable
String objects
let dog = String::new();let mut dog = String::from("Max");
format!
format!("Hi {} how are you", "Mark");
len
println!("{}", dog.len());
push & push_str
dog.push(' '); // push one charecter.dog.push_str("the dog"); // push whole string.println("{}", dog); // Max the dog
replace
let new_dog = dog.replace("the", "is_my");println!("{}", new_dog);
Values that cannot be changed
const URL: &str = "google.com";
Uppercase by convention
Data type is mandatory
Shadowing is not premitted
Global or local scope
Operator | Name | Example |
---|---|---|
+ | addition | 10 + 3 = 3 |
- | subtraction | 10 - 3 = 7 |
* | multiplication | 10 * 3 = 30 |
/ | division | 10 / 3 = 3 |
% | modules | 10 % 3 = 1 |
Note: Increment(++) and decrement(--) are not supported
Operator | Name | Example |
---|---|---|
> | Greater than | 10 > 3 = true |
>= | Greater than or equal to | "A" >= "a" = false |
< | Lesser than | 10 < 3 = false |
<= | Lesser than or equal to | true <= false = false |
== | Equal | 3.0 == 3.1 = true |
!= | Not equal | 'c' != 'C' == true |
Operator | Name | Example |
---|---|---|
&& | AND | true && true = true |
|| | OR | true || false = true |
! | NOT | !true = false |
Operator | Name | Example |
---|---|---|
& | Bitwise AND | 10 > 3 = true |
| | Bitwise OR | "A" >= "a" = false |
^ | Bitwise XOR | 10 < 3 = false |
! | NOT | true <= false = false |
<< | Left shift | 10 << 1 = 20 |
>> | Right shift | 10 >> 1 = 5 |
fn say_hi() {println!("Hello there!");}
fn main() {say_hi();}
fn main() {for i in 1..6 {say_hi();}}
Pass by value
fn main() {let mut name = "John";say_hello(name);println!("{}", name);}fn say_hello(name: &str) {println!("Hello {}", name);}
Pass by reference
fn main() {let mut name = "John";say_hello(&mut name);println("{}", name);}fn say_hello(name: &mut &str) {*name = "Alex";println!("Hello {}", name)}
Return values
fn main() {let mut name = "John";let greeting = say_hello(&mut name);println("{}", greeting);}fn say_hello(name: &mut &str) -> String {let greeting = format!("Hello {}", name);return greeting;}
Create a module
pub mod mod_name {pub fn do_something () {...}}
mod mod_name;fn main() {mod_mane::do_something();}
Nested modules
pub mod mod_name {pub mod submod {fn fun_submodule() {...}}}
mod mod_name;fn main() {mod_name::submod::fun_submodule();}
Multiple modules are grouped into a crate
Two types
Cargo is used to manage crates
External crates are imported into the project must be added to the toml file.
use crate::archive::arch::arch_file as arc;use rand::Rng;mod archive;fn main() {arc("somefile.txt");let mut rng = rand::thread_rng();let a: i32 = rng.gen();println!("{}", a);}
// archive.rspub mod arch {pub fn arch_file(name: &str) {println!("Archiving file {}", name);}}
Generate an integer
use rand::Rng;let mut rng = rand::thread_rng();rng.gen();
Bounded generation
rng.gen_range(0, 10);
// 生成0~10的随机浮点数rng.gen_range(0.0, 10.0)
String generation
use rand::{thread_rng, Rng};use rand::distributions::Alphanumericfn main() {let rand_string: String = thread_rng().sample_iter(&Alphanumeric).take(30).collect();println!("{}", rand_string);}
A collection of values of the same type
let primes = [2, 3, 5, 7, 11];
let doubles: [f64; 4] = [2.0, 4.0, 6.0, 8.0];
Create array with default values
let mut numbers = [0;15];
const DEFAULT: i32 = 3;let numbers = [DEFAULT;15];
Updating elements
numbers[3] = 5;
Using an iterator
for number in numbers.iter() {println!('{}', number + 3);}
Arrays of variables size
let mut primes: Vec<i32> = Vec::new();
let mut primes = vec![2, 3, 5];
Adding elements
primes.push(7);
Removing elements
primes.remove(2);
Create vectors with default values
let mut numbers = vec![2;10];
const DEFAULT: i32 = 6;let mut numbers = [DEFAULT;8];
A slice is a pointer to a block of memory
let numbers = [1, 2, 3, 4, 5];let slice = &numbers[1..4];
Mutable slices allow us to change values
A collection of values of various types
let person = {"John", 27, true};
let person: (&str, i32, bool) = ("John", 27, true);
Accessing elements
println!("Name: {}", person.0);
Updating elements
person.0 = "Jack";
Destructuring a tuple
let (name, age, employed) = person;
number of variables must correspond to number of elements
A collections of key-value pairs
struct Employee {name: String,company: String,age: u32}
let emp1 = Employee {name: String::from("John"),company: String::from("Google"),age: 35}
println("{}", emp1.name);
Adding methods to a structure
impl Employee {fn fn_detail(&self) -> String {format!("name: {}, age: {}, company: {}", &self.name, &self.age, &self.company)}}
A structure can have static methods
impl Employee {fn static_fn_detail() -> String {String::from("Details of a person")}}
A collection of values
enum Color {Red,Green,Blue}
let my_color = Color::Red;
let my_color = Red;
We can add data types to enum elements
enum Person {Name(String),Surname(String),Age(u32)}
Allows us to have variable data types
struct Point<T> {x: T,y: T}
let p1: Point<i32> = Point {x: 6, y: 8};let p2: Point<f64> = Point {x: 3.25, y: 8.43};
struct Point<T, V> {x: T,y: V}let p3: Point<i32, f64> = Point{x: 5, y: 3.24};
if logical_expression {functionality for true}
if logical_expression {functionality for true} else {functionality for false}
if expression1 {functionality for expression1 true} else if expression2 {functionality for expression1 false and expression2 true} else {functionality for both expressions false}
If statement can return a result
let res = if expr1 {result for true} else {result for false}
Similar to when or switch in other languages
match expression {expr1 => { ... }expr2 => { ... }_ => { ... }}
fn country(code: i32) {let country = match code {44 => "UK",34 => "Spain",1...999 => "unknown",_ => "invalid"}println!("Country is {}", country);}
match expression {expr1 => { ... }expr2 => { ... }_ => { ... }}
fn get_oranges(amount: i32) -> &'static str {return match amount {0 => "no",1 | 2 => "one or two",3..=7 => "a few",_ if (amount % 2 == 0) => "an even amount of",_ => "lots of"}}
Loop through a collection or range, execute code for each element
for element in collection {functionality}
fn main() {for i in 1..11 {println!("{0} * {0} = {1}", i, i * i);}}
Continue will skip a step
Break will stop the loop
Loop as long as a condition is true
while condition {...}
Continue skips a step
Break stops the loop
fn get_squares(limit: i32) {let mut x = 1;while x * x < limit {println!("{0} * {0} = {1}", x, x * x);x += 1;}}
fn get_cubes(limit: i32) {let mut x = 1;loop {println!("{0} * {0} * {0} = {1}", x, x * x * x);x += 1if x * x * x > limit {break}}}
Functions
fn main() {say_hi();}fn say_hi() {println!("Hello there!");}
fn main() {let mut name = "John";say_hi(&mut name);print!("The new name is {}", name);}fn say_hi(name: &mut &str) {*name = "Alex";println!("Hello {}!", name);}
Scope
No memory leaks - no need to manually deallocate variables
{let a = 3;}println("a = {}", a) // error
Global variables can be declared but they are unsafe
let a = 3;
fn main() {unsafe { println!("{}", a); }}
A function within a function
An anonymous function, lambda expression
|a: i32, b: i32| println("{}", a + b);|a: i32, b: i32| -> i32 {a + b};
A function can assigned to a variable
let sum = |a: i32, b: i32| -> i32 {a + b};sum(2, 3);
A clujure can be genetic
let gen = |x| { println!("received {}"), x };gen(3);
Functions that take another function as a parameter
fn apply (f: fn(i32) -> i32, a: i32) {}
apply(|x| -> x + 1, a);
Write code that writes code - meta programming
Match an expression and perform some operation
Code is expanded and compiled
macro_rules! my_macro {(match) => ( code to run )}
my_macro!
println!("This is an {} macro", "awesome");
We can match multiple expressions
macro_rules! my_macro {(match1) => ( code to run )(match2) => ( code to run )}
Designators
example
macro_rules! name {($name: expr) => { println!("Hey {}", $name) }}macro_rules! name2 {($($name: expr), *) => ( $(println!("Hey {}", $name);)* );}macro_rules! xy {(x => $e: expr) => (println!("X is {}", $e));(y => $e: expr) => (println!("Y is {}", $e));}macro_rules! build_fn {($fn_name: ident) => {fn $fn_name() {println!("{:?} was called", stringify!($fn_name))}}}fn main() {name!("John");name2!("Alex", "Mary", "Carol");xy!(x => 5);xy!(y => 3 * 9);build_fn!(hey);hey();}
Similar to an interface or abstract class
Add a definition to a structure
trait Name {fn must_implement(&self) -> i32;fn dn_action(&self) {...}fn do_non_instance_action() {...}}
Can have definition only or default implementation
Can have instance and non-instance action
implement a trait
impl Name for Person {fn must_implement(&self) -> { 42 }fn new(name: &str) -> Person {Person(name: name)}}
Can provide a constructor
trait Name {fn new(name: &str) -> Self;}
let john = Person::new("John");
example
struct RustDev {awesome: bool}struct JavaDev {awesome: bool}trait Developer {fn new(awesome: bool) -> Self;fn language(&self) -> &str;fn say_hello(&self) { println!("Hello world!") }}impl Developer for RustDev {fn new(awesome: bool) -> Self {RustDev { awesome: awesome }}fn language(&self) -> &str {"Rust"}fn say_hello(&self) {println!("println!(\"Hello world!\");");}}impl Developer for JavaDev {fn new(awesome: bool) -> Self {JavaDev { awesome: awesome }}fn language(&self) -> &str {"Java 1.8"}fn say_hello(&self) {println!("System.out.println(\"Hello world!\");");}}fn main() {let r = RustDev::new(true);let j = JavaDev::new(false);println!("{}", r.language());r.say_hello();}
Generics can be limited by traits
fn color<T: Colorable> (a: T) {// ...}
Demo
trait Bark {fn bark(&self) -> String;}struct Dog {species: &'static str}struct Cat {color: &'static str}impl Bark for Dog {fn bark(&self) -> String {return format!("{} barking", self.species);}}fn bark_it<T: Bark>(b: T) {println!("{}", b.bark())}fn main() {let dog = Dog { species: "retriever" };let cat = Cat { color: "black" };bark_it(dog);}
The compiler needs to know the space required for a function return type.
A workaround is to return a box with a dyn trait.
fn get_animal() -> Box<dyn Animal> {...}
dyn
is a new addition to the language old code might not have it.
We can add a trait to a structure we didn't create
impl My_Trait for Vec<i32> {...}
#[allow(unused_variables)]#[allow(unused_assignments)]trait Summable<T> {fn sum(&self) -> T;}impl Summable<i32> for Vec<i32> {fn sum(&self) -> i32 {let mut sum: i32 = 0;for i in self {sum += *i;}sum}}fn main() {let a = vec![1, 2, 3, 4, 5];println!("sum = {}", a.sum())}
We can implement standard operators for our custom structs
use std::ops::Add;
struct Custom {// ...}
impl Add for Custom {type Output = Custom;fn add(self: Custom, other: Custom) -> Custom {// ...}}
Demo
use std::ops::Add;#[allow(unused_variables)]#[allow(unused_assignments)]#[derive(Debug)]struct Point {x: f64,y: f64}impl Add for Point {type Output = Point;fn add(self, other:Self) -> Self::Output {Point {x: self.x + other.x,y: self.y + other.y}}}fn main() {let p1 = Point { x: 1.3, y: 4.6 };let p2 = Point { x: 3.7, y: 1.4 };let p3 = p1 + p2;println!("{:?}", p3)}
A generic trait will be converted to the required type at compile time
Monomorphization
Demo
#[allow(unused_variables)]#[allow(unused_assignments)]trait Duplicateable {fn dupl(&self) -> String;}impl Duplicateable for String {fn dupl(&self) -> String {format!("{0}{0}", *self)}}impl Duplicateable for i32 {fn dupl(&self) -> String {format!("{}", *self * 2)}}fn duplicate<T: Duplicateable> (x: T) {println!("{}", x.dupl());}fn main() {let a = 42;let b = "Hi John ".to_string();duplicate(a);duplicate(b);}
A generic trait will be converted to the required type at run time
Demo
#[allow(unused_variables)]#[allow(unused_assignments)]trait Duplicateable {fn dupl(&self) -> String;}impl Duplicateable for String {fn dupl(&self) -> String {format!("{0}{0}", *self)}}impl Duplicateable for i32 {fn dupl(&self) -> String {format!("{}", *self * 2)}}fn duplicate(x: &dyn Duplicateable) {println!("{}", x.dupl())}fn main() {let a = 42;let b = "Hi John ".to_string();duplicate(&a);duplicate(&b);}
Only one variable can own a piece of memory
For primitive types, copying data is cheap
For complex types, ownership is transferred.
Demo
#[allow(unused_variables)]#[allow(unused_assignments)]fn main() {let i = 5;let j = i;println!("{}", j);println!("{}", i);let v = vec![1, 2, 3, 4, 5];// let w = v;// println!("{:?}", w);// println!("{:?}", v);let foo = |v: Vec<i32>| -> Vec<i32> {println!("Vector used in foo");v};let v = foo(v);println!("{:?}", v);}
Only one variable can own a piece of memory
Variables can borrow ownership to other pieces of memory
let a = 6;let b = &a;
println("{}", *b);
Demo
#[allow(unused_variables)]#[allow(unused_assignments)]fn main() {let mut a = 6;{let b = &mut a;println!("{}", *b);*b += 2;}println!("{}", a);let mut v = vec![1, 2, 3, 4, 5];for i in &v {println!("{}", i);v.push(6);}}
An indication of how long an object will live
Rust prevents parts to objects outliving the object
struct Object<'lifetime> {field: &'lifetime str}
Lifetime elision - compiler builds lifetimes for us when evident.
Demo
#[allow(unused_variables)]#[allow(unused_assignments)]#[derive(Debug)]struct Person {name: String}#[derive(Debug)]struct Dog<'l> {name: String,owner: &'l Person}impl Person {fn get_name(&self) -> &String {&self.name}}fn main() {println!("{}", get_str());let p1 = Person { name: String::from("John") };let d1 = Dog { name: String::from("Max"), owner: &p1 };println!("{:?}", d1);let mut a: &String;{let p2 = Person { name: String::from("Mary") };a = p2.get_name();}println!("{}", a);}fn get_str() -> &'static str {"Hello"}