diff --git a/src/Functions.rs b/src/Functions.rs new file mode 100644 index 0000000..709394d --- /dev/null +++ b/src/Functions.rs @@ -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); + } +} diff --git a/src/Functions/Closures.rs b/src/Functions/Closures.rs new file mode 100644 index 0000000..5e5d603 --- /dev/null +++ b/src/Functions/Closures.rs @@ -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()); +} diff --git a/src/Functions/Closures/AsInputParameters.rs b/src/Functions/Closures/AsInputParameters.rs new file mode 100644 index 0000000..aae8ec8 --- /dev/null +++ b/src/Functions/Closures/AsInputParameters.rs @@ -0,0 +1,46 @@ +//A function which takes a closure as an argument and calls it. +// denotes that F is a "Generic type parameter" +fn apply(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) -> 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)); +} \ No newline at end of file diff --git a/src/Functions/Closures/AsOutputParameters.rs b/src/Functions/Closures/AsOutputParameters.rs new file mode 100644 index 0000000..7af44b6 --- /dev/null +++ b/src/Functions/Closures/AsOutputParameters.rs @@ -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(); +} diff --git a/src/Functions/Closures/Capturing.rs b/src/Functions/Closures/Capturing.rs new file mode 100644 index 0000000..923a14e --- /dev/null +++ b/src/Functions/Closures/Capturing.rs @@ -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. +} diff --git a/src/Functions/Closures/ExamplesInStd.rs b/src/Functions/Closures/ExamplesInStd.rs new file mode 100644 index 0000000..54afc54 --- /dev/null +++ b/src/Functions/Closures/ExamplesInStd.rs @@ -0,0 +1,2 @@ +pub mod IteratorAny; +pub mod SearchingThroughIterators; diff --git a/src/Functions/Closures/ExamplesInStd/IteratorAny.rs b/src/Functions/Closures/ExamplesInStd/IteratorAny.rs new file mode 100644 index 0000000..e9a5638 --- /dev/null +++ b/src/Functions/Closures/ExamplesInStd/IteratorAny.rs @@ -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)); +} diff --git a/src/Functions/Closures/ExamplesInStd/SearchingThroughIterators.rs b/src/Functions/Closures/ExamplesInStd/SearchingThroughIterators.rs new file mode 100644 index 0000000..80c30d6 --- /dev/null +++ b/src/Functions/Closures/ExamplesInStd/SearchingThroughIterators.rs @@ -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); +} diff --git a/src/Functions/Closures/InputFunctions.rs b/src/Functions/Closures/InputFunctions.rs new file mode 100644 index 0000000..4295bde --- /dev/null +++ b/src/Functions/Closures/InputFunctions.rs @@ -0,0 +1,17 @@ +//Define a function which takes a generic `F` argument bounded by `Fn`, and calls it +fn call_me(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); +} diff --git a/src/Functions/Closures/TypeAnonymity.rs b/src/Functions/Closures/TypeAnonymity.rs new file mode 100644 index 0000000..e14de2a --- /dev/null +++ b/src/Functions/Closures/TypeAnonymity.rs @@ -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) 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); +} diff --git a/src/Functions/DivergingFunctions.rs b/src/Functions/DivergingFunctions.rs new file mode 100644 index 0000000..9858556 --- /dev/null +++ b/src/Functions/DivergingFunctions.rs @@ -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)); +} diff --git a/src/Functions/HigherOrderFunctions.rs b/src/Functions/HigherOrderFunctions.rs new file mode 100644 index 0000000..db32f30 --- /dev/null +++ b/src/Functions/HigherOrderFunctions.rs @@ -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); +} diff --git a/src/Functions/Methods.rs b/src/Functions/Methods.rs new file mode 100644 index 0000000..5a7412d --- /dev/null +++ b/src/Functions/Methods.rs @@ -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, Box); +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 +} diff --git a/src/main.rs b/src/main.rs index e7ebd07..3563367 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); }