Added chapter 9

This commit is contained in:
2020-06-11 21:06:41 -04:00
parent 933ff6d190
commit 3710fe0b0e
14 changed files with 551 additions and 28 deletions

44
src/Functions.rs Normal file
View File

@@ -0,0 +1,44 @@
pub mod Methods;
pub mod Closures;
pub mod HigherOrderFunctions;
pub mod DivergingFunctions;
//Unlike C/C++, there's no restriction on the order of function definitions
pub fn main(){
//We can use this function here, and define it somewhere later
fizzbuzz_to(100);
}
//Function that returns a boolean value
fn is_divisible_by(lhs: u32, rhs: u32) -> bool{
//Corner case, early return
if(rhs == 0){
return false;
}
//This is an expression, the `return` keyword is not necessary here
lhs % rhs == 0
}
//Functions that "don't" return a value, actually return the unit type `()`
fn fizzbuzz(n: u32) -> (){
if(is_divisible_by(n, 15)){
println!("fizzbuzz");
}
else if(is_divisible_by(n, 3)){
println!("fizz");
}
else if(is_divisible_by(n, 5)){
println!("buzz");
}
else{
println!("{}", n);
}
}
//When a function returns `()`, the return type can be omitted from the signature
fn fizzbuzz_to(n: u32){
for n in 1..(n + 1){
fizzbuzz(n);
}
}

33
src/Functions/Closures.rs Normal file
View File

@@ -0,0 +1,33 @@
pub mod Capturing;
pub mod AsInputParameters;
pub mod TypeAnonymity;
pub mod InputFunctions;
pub mod AsOutputParameters;
pub mod ExamplesInStd;
//Closures in Rust, also called lambda expressions or lambdas, are functions that can capture the enclosing environment.
pub fn main(){
//Increment via closures and functions.
fn function(i: i32) -> i32{
i + 1
}
//Closures are anonymous, here we are binding them to references
//Annotation is identical to function annotation but is optional
//as are the `{}` wrapping the body. These nameless functions
//are assigned to appropriately named variables.
let closure_annotated = |i: i32| -> i32 { i + 1 };
let closure_inferred = |i | i + 1 ;
let i = 1;
// Call the function and closures.
println!("function: {}", function(i));
println!("closure_annotated: {}", closure_annotated(i));
println!("closure_inferred: {}", closure_inferred(i));
//A closure taking no arguments which returns an `i32`.
//The return type is inferred.
let one = || 1;
println!("closure returning one: {}", one());
}

View File

@@ -0,0 +1,46 @@
//A function which takes a closure as an argument and calls it.
//<F> denotes that F is a "Generic type parameter"
fn apply<F>(f: F) where
//The closure takes no input and returns nothing.
F: FnOnce(){
//^ TODO: Try changing this to `Fn` or `FnMut`.
f();
}
//A function which takes a closure and returns an `i32`.
fn apply_to_3<F>(f: F) -> i32 where
//The closure takes an `i32` and returns an `i32`.
F: Fn(i32) -> i32{
f(3)
}
pub fn main(){
use std::mem;
let greeting = "hello";
//A non-copy type.
//`to_owned` creates owned data from borrowed one
let mut farewell = "goodbye".to_owned();
//Capture 2 variables: `greeting` by reference and `farewell` by value.
let diary = || {
//`greeting` is by reference: requires `Fn`.
println!("I said {}.", greeting);
//Mutation forces `farewell` to be captured by mutable reference. Now requires `FnMut`.
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");
//Manually calling drop forces `farewell` to be captured by value. Now requires `FnOnce`.
mem::drop(farewell);
};
//Call the function which applies the closure.
apply(diary);
//`double` satisfies `apply_to_3`'s trait bound
let double = |x| 2 * x;
println!("3 doubled: {}", apply_to_3(double));
}

View File

@@ -0,0 +1,27 @@
fn create_fn() -> impl Fn(){
let text = "Fn".to_owned();
move || println!("This is a: {}", text)
}
fn create_fnmut() -> impl FnMut(){
let text = "FnMut".to_owned();
move || println!("This is a: {}", text)
}
fn create_fnonce() -> impl FnOnce(){
let text = "FnOnce".to_owned();
move || println!("This is a: {}", text)
}
pub fn main(){
let fn_plain = create_fn();
let mut fn_mut = create_fnmut();
let fn_once = create_fnonce();
fn_plain();
fn_mut();
fn_once();
}

View File

@@ -0,0 +1,74 @@
pub fn main(){
use std::mem;
let color = "green";
//A closure to print `color` which immediately borrows (`&`) `color` and stores the borrow and closure in the `print` variable.
//It will remain borrowed until `print` is used the last time.
//`println!` only requires arguments by immutable reference so it doesn't impose anything more restrictive.
let print = || println!("`color`: {}", color);
//Call the closure using the borrow.
print();
//`color` can be borrowed immutably again, because the closure only holds an immutable reference to `color`.
let _reborrow = &color;
print();
//A move or reborrow is allowed after the final use of `print`
let _color_moved = color;
let mut count = 0;
//A closure to increment `count` could take either `&mut count` or `count` but `&mut count` is less restrictive so it takes that.
//Immediately borrows `count`.
// A `mut` is required on `inc` because a `&mut` is stored inside.
//Thus, calling the closure mutates the closure which requires a `mut`.
let mut inc = ||{
count += 1;
println!("`count`: {}", count);
};
//Call the closure using a mutable borrow.
inc();
//The closure still mutably borrows `count` because it is called later.
//An attempt to reborrow will lead to an error.
//let _reborrow = &count;
//^ TODO: try uncommenting this line.
inc();
//The closure no longer needs to borrow `&mut count`. Therefore, it is possible to reborrow without an error
let _count_reborrowed = &mut count;
//A non-copy type.
let movable = Box::new(3);
//`mem::drop` requires `T` so this must take by value. A copy type would copy into the closure leaving the original untouched.
//A non-copy must move and so `movable` immediately moves into the closure.
let consume = ||{
println!("`movable`: {:?}", movable);
mem::drop(movable);
};
//`consume` consumes the variable so this can only be called once.
consume();
//consume();
//^ TODO: Try uncommenting this line.
//`Vec` has non-copy semantics.
let haystack = vec![1, 2, 3];
let contains = move |needle| haystack.contains(needle);
println!("{}", contains(&1));
println!("{}", contains(&4));
//println!("There're {} elements in vec", haystack.len());
//^ Uncommenting above line will result in compile-time error because borrow checker doesn't allow re-using variable after it has been moved.
// Removing `move` from closure's signature will cause closure to borrow _haystack_ variable immutably,
//hence _haystack_ is still available and uncommenting above line will not cause an error.
}

View File

@@ -0,0 +1,2 @@
pub mod IteratorAny;
pub mod SearchingThroughIterators;

View File

@@ -0,0 +1,20 @@
#[allow(array_into_iter)]
pub fn main(){
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
#[allow(array_into_iter)]
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
}

View File

@@ -0,0 +1,36 @@
#[allow(array_into_iter)]
pub fn main(){
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `iter()` for vecs yields `&i32`.
let mut iter = vec1.iter();
// `into_iter()` for vecs yields `i32`.
let mut into_iter = vec2.into_iter();
// `iter()` for vecs yields `&i32`, and we want to reference one of its
// items, so we have to destructure `&&i32` to `i32`
println!("Find 2 in vec1: {:?}", iter .find(|&&x| x == 2));
// `into_iter()` for vecs yields `i32`, and we want to reference one of
// its items, so we have to destructure `&i32` to `i32`
println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2));
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `iter()` for arrays yields `&i32`
println!("Find 2 in array1: {:?}", array1.iter() .find(|&&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`
println!("Find 2 in array2: {:?}", array2.into_iter().find(|&&x| x == 2));
let vec = vec![1, 9, 3, 3, 13, 2];
let index_of_first_even_number = vec.iter().position(|x| x % 2 == 0);
assert_eq!(index_of_first_even_number, Some(5));
let index_of_first_negative_number = vec.iter().position(|x| x < &0);
assert_eq!(index_of_first_negative_number, None);
}

View File

@@ -0,0 +1,17 @@
//Define a function which takes a generic `F` argument bounded by `Fn`, and calls it
fn call_me<F: Fn()>(f: F){
f();
}
//Define a wrapper function satisfying the `Fn` bound
fn function(){
println!("I'm a function!");
}
pub fn main(){
//Define a closure satisfying the `Fn` bound
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}

View File

@@ -0,0 +1,15 @@
//`F` must implement `Fn` for a closure which takes no inputs and returns nothing - exactly what is required for `print`.
fn apply<F>(f: F) where
F: Fn(){
f();
}
pub fn main(){
let x = 7;
//Capture `x` into an anonymous type and implement
//`Fn` for it. Store it in `print`.
let print = || println!("{}", x);
apply(print);
}

View File

@@ -0,0 +1,18 @@
pub fn main(){
fn sum_odd_numbers(up_to: u32) -> u32{
let mut acc = 0;
for i in 0..up_to{
//Notice that the return type of this match expression must be u32 because of the type of the "addition" variable.
let addition: u32 = match i%2 == 1{
//The "i" variable is of type u32, which is perfectly fine.
true => i,
//On the other hand, the "continue" expression does not return u32, but it is still fine,
//because it never returns and therefore does not violate the type requirements of the match expression.
false => continue,
};
acc += addition;
}
acc
}
println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
}

View File

@@ -0,0 +1,35 @@
fn is_odd(n: u32) -> bool{
(n % 2) == 1
}
pub fn main(){
println!("Find the sum of all the squared odd numbers under 1000");
let upper = 1000;
//Imperative approach
//Declare accumulator variable
let mut acc = 0;
//Iterate: 0, 1, 2, ... to infinity
for n in 0.. {
//Square the number
let n_squared = n * n;
if(n_squared >= upper){
//Break loop if exceeded the upper limit
break;
}
else if(is_odd(n_squared)){
//Accumulate value, if it's odd
acc += n_squared;
}
}
println!("imperative style: {}", acc);
//Functional approach
let sum_of_squared_odd_numbers: u32 =
(0..).map(|n| n * n) //All natural numbers squared
.take_while(|&n_squared| n_squared < upper) //Below upper limit
.filter(|&n_squared| is_odd(n_squared)) //That are odd
.fold(0, |acc, n_squared| acc + n_squared); //Sum them
println!("functional style: {}", sum_of_squared_odd_numbers);
}

102
src/Functions/Methods.rs Normal file
View File

@@ -0,0 +1,102 @@
struct Point{
x: f64,
y: f64,
}
//Implementation block, all `Point` methods go in here
impl Point{
//This is a static method
//Static methods don't need to be called by an instance
//These methods are generally used as constructors
fn origin() -> Point{
Point { x: 0.0, y: 0.0 }
}
//Another static method, taking two arguments:
fn new(x: f64, y: f64) -> Point{
Point { x: x, y: y }
}
}
struct Rectangle{
p1: Point,
p2: Point,
}
impl Rectangle{
//This is an instance method
//`&self` is sugar for `self: &Self`, where `Self` is the type of the caller object.
//In this case `Self` = `Rectangle`
fn area(&self) -> f64{
//`self` gives access to the struct fields via the dot operator
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
//`abs` is a `f64` method that returns the absolute value of the caller
((x1 - x2) * (y1 - y2)).abs()
}
fn perimeter(&self) -> f64{
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
}
//This method requires the caller object to be mutable
//`&mut self` desugars to `self: &mut Self`
fn translate(&mut self, x: f64, y: f64){
self.p1.x += x;
self.p2.x += x;
self.p1.y += y;
self.p2.y += y;
}
}
//`Pair` owns resources: two heap allocated integers
struct Pair(Box<i32>, Box<i32>);
impl Pair{
//This method "consumes" the resources of the caller object
//`self` desugars to `self: Self`
fn destroy(self) {
//Destructure `self`
let Pair(first, second) = self;
println!("Destroying Pair({}, {})", first, second);
//`first` and `second` go out of scope and get freed
}
}
pub fn main(){
let rectangle = Rectangle{
//Static methods are called using double colons
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
//Instance methods are called using the dot operator
//Note that the first argument `&self` is implicitly passed, i.e.
//`rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
println!("Rectangle perimeter: {}", rectangle.perimeter());
println!("Rectangle area: {}", rectangle.area());
let mut square = Rectangle{
p1: Point::origin(),
p2: Point::new(1.0, 1.0),
};
//Error! `rectangle` is immutable, but this method requires a mutable object
//rectangle.translate(1.0, 0.0);
//TODO ^ Try uncommenting this line
//Okay! Mutable objects can call mutable methods
square.translate(1.0, 1.0);
let pair = Pair(Box::new(1), Box::new(2));
pair.destroy();
//Error! Previous `destroy` call "consumed" `pair`
//pair.destroy();
// TODO ^ Try uncommenting this line
}

View File

@@ -10,78 +10,92 @@ mod Types;
mod Conversion;
mod Expressions;
mod FlowOfControl;
mod Functions;
fn main(){
//1 Hello World
//1.1 Comments
//HelloWorld::Comments::main();
/*
1.1 Comments
HelloWorld::Comments::main();
//1.2 Formatted print
//HelloWorld::FormattedPrint::main();
HelloWorld::FormattedPrint::main();
//1.2.1
//HelloWorld::FormattedPrint::Debugging::main();
HelloWorld::FormattedPrint::Debugging::main();
//1.2.2
//HelloWorld::FormattedPrint::Display::main();
HelloWorld::FormattedPrint::Display::main();
//1.2.2.1
//HelloWorld::FormattedPrint::TestcaseList::main();
HelloWorld::FormattedPrint::TestcaseList::main();
//1.2.3
//HelloWorld::FormattedPrint::Formatting::main();
HelloWorld::FormattedPrint::Formatting::main();
*/
//2 Primitives
/*
//2.1 LiteralsAndOperators
//Primitives::LiteralsAndOperators::main();
Primitives::LiteralsAndOperators::main();
//2.2 Tuples
//Primitives::Tuples::main();
Primitives::Tuples::main();
//2.3 ArraysAndSlices
//Primitives::ArraysAndSlices::main();
Primitives::ArraysAndSlices::main();
*/
//3 Custom Types
/*
//3.1 Structures
//CustomTypes::Structures::main();
CustomTypes::Structures::main();
//3.2 Enums
//CustomTypes::Enums::main();
CustomTypes::Enums::main();
//3.2.1 Use
//CustomTypes::Enums::Use::main();
CustomTypes::Enums::Use::main();
//3.2.2 C-Like
//CustomTypes::Enums::CLike::main();
CustomTypes::Enums::CLike::main();
//3.2.3 Testcase - Linked-list
//CustomTypes::Enums::TestCaseLinkedList::main();
CustomTypes::Enums::TestCaseLinkedList::main();
//3.3 Constants
//CustomTypes::Constants::main();
CustomTypes::Constants::main();
*/
//4 Variable Bindings
//VariableBindings::main();
/*
VariableBindings::main();
//4.1 Mutability
//VariableBindings::Mutability::main();
VariableBindings::Mutability::main();
//4.2 Scope and Shadowing
//VariableBindings::ScopeAndShadowing::main();
VariableBindings::ScopeAndShadowing::main();
//4.3 Declare First
//VariableBindings::DeclareFirst::main();
VariableBindings::DeclareFirst::main();
//4.4 Freezing
//VariableBindings::Freezing::main();
VariableBindings::Freezing::main();
*/
//5 Types
/*
//5.1 Casting
//Types::Casting::main();
Types::Casting::main();
//5.2 Literals
//Types::Literals::main();
Types::Literals::main();
//5.3 Inference
//Types::Inference::main();
Types::Inference::main();
//5.4 Aliasing
//Types::Aliasing::main();
Types::Aliasing::main();
*/
//6 Conversion
/*
//6.1 From and Into
//Conversion::FromAndInto::main();
Conversion::FromAndInto::main();
//6.2 TryFrom and TryInto
//Conversion::TryFromAndTryInto::main();
Conversion::TryFromAndTryInto::main();
//6.3 To and From Strings
//Conversion::ToAndFromStrings::main();
Conversion::ToAndFromStrings::main();
*/
//7 Expressions
//Expressions::main();
//8 Flow of Control
/*
//8.1 if/else
FlowOfControl::IfElse::main();
//8.2 loop
@@ -113,4 +127,44 @@ fn main(){
FlowOfControl::IfLet::main();
//8.7 while let
FlowOfControl::WhileLet::main();
*/
//9 Functions
println!("9. Functions");
Functions::main();
//9.1 Methods
println!("\n\n9.1 Methods");
Functions::Methods::main();
//9.2 Closures
println!("\n\n9.2 Closures");
Functions::Closures::main();
//9.2.1 Capturing
println!("\n\n9.2.1 Capturing");
Functions::Closures::Capturing::main();
//9.2.2 As input parameters
println!("\n\n9.2.2 As input parameters");
Functions::Closures::AsInputParameters::main();
//9.2.3 Type anonymity
println!("\n\n9.2.3 Type anonymity");
Functions::Closures::TypeAnonymity::main();
//9.2.4 Input functions
println!("\n\n9.2.4 Input functions");
Functions::Closures::InputFunctions::main();
//9.2.5 As output parameters
println!("\n\n9.2.5 As output parameters");
Functions::Closures::AsInputParameters::main();
//9.2.6 Examples in std
println!("\n\n9.2.6 Examples is std");
//9.2.6.1 Iterator::any
println!("9.2.6.1 Iterator::any");
Functions::Closures::ExamplesInStd::IteratorAny::main();
//9.2.6.2 Searching through iterators
println!("\n\n9.2.6.2 Searching through iterators");
Functions::Closures::ExamplesInStd::SearchingThroughIterators::main();
//9.3 Higher Order Functions
println!("\n\n9.3 Higher order functions");
Functions::HigherOrderFunctions::main();
//9.4 Diverging functions
println!("\n\n9.4 Diverging functions");
Functions::DivergingFunctions::main();
}