diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 2093329..bb7c047 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -10,20 +10,6 @@ { "id": "de2ac5df5b921166", "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "Advanced Features.md", - "mode": "source", - "source": false - }, - "icon": "lucide-file", - "title": "Advanced Features" - } - }, - { - "id": "b24fe202609f5b35", - "type": "leaf", "state": { "type": "markdown", "state": { @@ -87,8 +73,7 @@ "title": "Graph view" } } - ], - "currentTab": 1 + ] } ], "direction": "vertical" @@ -231,12 +216,13 @@ "command-palette:Open command palette": false } }, - "active": "b24fe202609f5b35", + "active": "de2ac5df5b921166", "lastOpenFiles": [ "Advanced Features.md", - "Advanced Functions and Closures.md", "Advanced Types.md", "Advanced Traits.md", + "Enums.md", + "Advanced Functions and Closures.md", "Unsafe Rust.md", "Pattern Matching.md", "Places Patterns Can Be Used.md", @@ -258,7 +244,6 @@ "().md", "Smart Pointers.md", "Simultaneous Code Running.md", - "Passing Data Between Threads.md", "minigrep/src/lib.rs", "does_not_compile.svg", "Untitled.canvas", diff --git a/Advanced Functions and Closures.md b/Advanced Functions and Closures.md index 8451555..ad85463 100644 --- a/Advanced Functions and Closures.md +++ b/Advanced Functions and Closures.md @@ -1 +1,106 @@ # Advanced Functions and Closures +This section includes some more advanced features including function pointers and returning closures. + +## Function Pointers +We have talked about how to pass closures to functions; you can also pass regular functions to functions. + +This technique is useful when you want to pass a function that you have already defined rather than defining a new closure. + +Functions coerce to the type `fn` (with a lowercase f), not to be confused with, not to be confused with the `Fn` closure trait. + +The `fn` type is called a *function pointer*. + +Passing function pointers will allow you to use functions as arguments to other functions. + +The syntax for specifying that a parameter is a function pointer is similar to that of closures as shown below. + +This shows that we have defined a function `add_one` that adds one to its parameter. + +The function `do_twice` takes two parameters: a function pointer to any function takes an `i32` parameter and returns an `i32`, and one `i32` value. + +The `do_twice` function calls the function `f` twice, passing it the `arg` value, then adds the two function call results together. + +The `main` function calls `do_twice` with the arguments `add_one` and `5`. +```rust +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {answer}"); +} +``` +Output +``` +The answer is: 12 +``` +Here we specify that the parameter `f` in `do_twice` is an `fn` that takes one parameter of type `i32` and returns a `i32`. + +We can then call `f` from the body of `do_twice`. + +In `main` we can pass the function name `add_one` as the first arg to `do_twice`. + +Unlike closures, `fn` is a type rather than a trait, so we need to specify `fn` as the parameter type directly rather than declaring a generic type parameter with one of the `Fn` traits as a trait bound. + +Function pointers implement all three of the closure traits (`Fn`, `FnMut` and `FnOnce`). + +This means that you can always pass a function pointer as an argument for a function that expects a closure. + +It is best to write functions using a generic type and one of the closure traits so your functions can accept either functions or closures. + +That being said, one example of where you would only want to accept `fn` and not closures is when interfacing with external code that doesn't have closures. + +C functions can accept functions as arguments, but C doesn't have closures. + +As an example of where you could use either a closure defined inline or a named function. + +Lets look at a use of the `map` method provided by the `Iterator` trait in the std library. + +To use the `map` function to turn a vector of numbers into a vector of strings, we could use a closure. + +Like this: +```rust + let list_of_numbers = vec![1, 2, 3]; + let list_of_strings: Vec = + list_of_numbers.iter().map(|i| i.to_string()).collect(); +``` +We could have a function as the argument to `map` instead of the closure. + +Like this: +```rust + let list_of_numbers = vec![1, 2, 3]; + let list_of_strings: Vec = + list_of_numbers.iter().map(ToString::to_string).collect(); +``` +Note, we must use the fully qualified syntax that we talked about earlier in ["Advanced Traits"](./Advanced%20Traits.md) section. + +This is because there are multiple functions available named `to_string`. + +Here we are using the `to_string` function defined in the `ToString` trait, which is in the std library has implemented for any type that implements `Display`. + +Recall form the ["Enum values"]() section of Ch6 that the name of each enum variant that we define also becomes an initializer function. + +We can use these initializer functions as function pointers that implement the closure traits, this means we can specify the initializer functions as arguments for methods that take closures. + +Like this: +```rust + enum Status { + Value(u32), + Stop, + } + + let list_of_statuses: Vec = (0u32..20).map(Status::Value).collect(); +``` +Here we creates `Status::Value` instances using each `u32` value in the range that `map` is called on by using the initializer function of `Status::Value`. + +Some prefer to use this style, and some people prefer to use closures. + +They compile to the same code, so use whatever style is clearer to you. + +## Returning Closures