{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions & Closures" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "People ordered by age: [Person { name: \"Marry\", age: 11, height: 1.5 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n", "People ordered by height: [Person { name: \"Marry\", age: 11, height: 1.5 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" ] } ], "source": [ "#[derive(Debug, PartialEq, Clone)]\n", "struct Person {\n", " name: String,\n", " age: u8,\n", " height: f32\n", "}\n", "\n", "fn create_people() -> Vec {\n", " vec![\n", " Person {\n", " name: \"Alice\".to_string(),\n", " age: 20,\n", " height: 1.7\n", " },\n", " Person {\n", " name: \"Bob\".to_string(),\n", " age: 25,\n", " height: 1.8\n", " },\n", " Person {\n", " name: \"Charlie\".to_string(),\n", " age: 30,\n", " height: 1.9\n", " },\n", " Person {\n", " name: \"David\".to_string(),\n", " age: 35,\n", " height: 1.75\n", " },\n", " Person {\n", " name: \"Eve\".to_string(),\n", " age: 40,\n", " height: 1.75\n", " },\n", " Person {\n", " name: \"Adam\".to_string(),\n", " age: 16,\n", " height: 1.6\n", " },\n", " Person {\n", " name: \"Marry\".to_string(),\n", " age: 11,\n", " height: 1.5\n", " }\n", " ]\n", "}\n", "\n", "let people = create_people();\n", "\n", "let people_sorted_by_age = {\n", " let mut people = people.clone();\n", " people.sort_by_key(|person| person.age);\n", " people\n", "};\n", "\n", "let people_sorted_by_height = {\n", " let mut people = people.clone();\n", " people.sort_by(|person, other| f32::total_cmp(&person.height, &other.height));\n", " people\n", "};\n", "\n", "println!(\"People ordered by age: {:?}\", people_sorted_by_age);\n", "println!(\"People ordered by height: {:?}\", people_sorted_by_height);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Closures Borrow by Default" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "People with translated names: [Person { name: \"Alicja\", age: 20, height: 1.7 }, Person { name: \"Robert\", age: 25, height: 1.8 }, Person { name: \"Karol\", age: 30, height: 1.9 }, Person { name: \"Dawid\", age: 35, height: 1.75 }, Person { name: \"Ewa\", age: 40, height: 1.75 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Maria\", age: 11, height: 1.5 }]\n" ] } ], "source": [ "use std::collections::HashMap;\n", "\n", "fn create_name_translator() -> HashMap {\n", " [\n", " (\"Alice\".to_string(), \"Alicja\".to_string()),\n", " (\"Bob\".to_string(), \"Robert\".to_string()),\n", " (\"Charlie\".to_string(), \"Karol\".to_string()),\n", " (\"David\".to_string(), \"Dawid\".to_string()),\n", " (\"Eve\".to_string(), \"Ewa\".to_string()),\n", " (\"Adam\".to_string(), \"Adam\".to_string()),\n", " (\"Marry\".to_string(), \"Maria\".to_string())\n", " ].iter().cloned().collect()\n", "}\n", "\n", "fn main() {\n", " let people = create_people();\n", "\n", " let name_translator: HashMap<_, _> = create_name_translator();\n", "\n", " let person_name_translator = |person: &Person| {\n", " let translated_name = name_translator.get(&person.name).unwrap_or(&person.name); // name_translator is borrowed\n", " Person {\n", " name: translated_name.to_string(),\n", " age: person.age,\n", " height: person.height\n", " }\n", " };\n", "\n", " let people_translated: Vec = people.iter().map(person_name_translator).collect();\n", "\n", " println!(\"People with translated names: {:?}\", people_translated);\n", "}\n", "\n", "main();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Closures That Move" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "People with translated names: [Person { name: \"Alicja\", age: 20, height: 1.7 }, Person { name: \"Robert\", age: 25, height: 1.8 }, Person { name: \"Karol\", age: 30, height: 1.9 }, Person { name: \"Dawid\", age: 35, height: 1.75 }, Person { name: \"Ewa\", age: 40, height: 1.75 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Maria\", age: 11, height: 1.5 }]\n" ] } ], "source": [ "fn main() {\n", " let people: Vec = create_people();\n", " let name_translator: HashMap = create_name_translator();\n", "\n", " let person_name_translator = move |person: &Person| {\n", " let translated_name = name_translator.get(&person.name).unwrap_or(&person.name); // name_translator is moved\n", " Person {\n", " name: translated_name.to_string(),\n", " age: person.age,\n", " height: person.height\n", " }\n", " };\n", "\n", " let thd = std::thread::spawn(move || { \n", " let people_translated: Vec = people.iter().map(person_name_translator).collect(); // people & person_name_translator are moved\n", " \n", " println!(\"People with translated names: {:?}\", people_translated);\n", " });\n", "\n", " thd.join().unwrap();\n", "}\n", "\n", "main();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Function & Closure Types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Function Type" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "fn is_adult(person: &Person) -> bool {\n", " person.age >= 18\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function `is_adult` has a type: `fn(&Person) -> bool`" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Adults: [Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n" ] } ], "source": [ "let f_predicate: fn(&Person) -> bool = is_adult;\n", "\n", "fn filter_by_predicate(people: &Vec, predicate: fn(&Person) -> bool) -> Vec {\n", " let mut qualified: Vec = Vec::new();\n", " for person in people {\n", " if predicate(person) {\n", " qualified.push(person.clone());\n", " }\n", " }\n", " qualified\n", "}\n", "\n", "let adults = filter_by_predicate(&people, is_adult);\n", "\n", "println!(\"Adults: {:?}\", adults);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When function expects a function as an argument, it accepts a closure that captures nothing:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tall people: [Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" ] } ], "source": [ "let tall_people = filter_by_predicate(&people, |person| person.height > 1.75);\n", "\n", "println!(\"Tall people: {:?}\", tall_people);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A closure that captures variables from the enclosing scope cannot be passed to a function that accepts a function type:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "ename": "Error", "evalue": "mismatched types", "output_type": "error", "traceback": [ "\u001b[31m[E0308] Error:\u001b[0m mismatched types", " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_10:1:1 \u001b[38;5;246m]\u001b[0m", " \u001b[38;5;246m│\u001b[0m", " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mf\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100ml\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100m_\u001b[0m\u001b[38;5;100mb\u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;100m_\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mc\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m.\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mg\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m>\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mg\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68m_\u001b[0m\u001b[38;5;68ml\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m arguments to this function are incorrect", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected fn pointer, found closure", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m `height_limit` captured here", "\u001b[38;5;246m───╯\u001b[0m" ] } ], "source": [ "let height_limit = 1.75;\n", "let tall_people = filter_by_predicate(&people, |person| person.height > height_limit);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Fn` Trait" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Functions and closures implement the `Fn` trait, which is the trait of things that can be called like functions." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "fn filter_by_predicate(people: &Vec, predicate: F) -> Vec\n", " where F: Fn(&Person) -> bool \n", "{\n", " let mut qualified: Vec = Vec::new();\n", " for person in people {\n", " if predicate(person) {\n", " qualified.push(person.clone());\n", " }\n", " }\n", " qualified\n", "}" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Adults: [Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n", "Tall people: [Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" ] } ], "source": [ "let adults = filter_by_predicate(&people, is_adult); // using function pointer\n", "println!(\"Adults: {:?}\", adults);\n", "\n", "let height_limit = 1.75;\n", "let tall_people = filter_by_predicate(&people, |person| person.height > height_limit); // using closure that captures (borrows) height_limit\n", "println!(\"Tall people: {:?}\", tall_people);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Now the function `filter_by_predicate` is generic. It accepts any function or closure `F` that implements the special trait `Fn(&Person) -> bool`.\n", "* The trait `Fn(&Person) -> bool` automatically implemented by all functions and closures that can be called with a `&Person` argument and return a `bool`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `FnMut` Trait\n", "\n", "* The `FnMut` trait is a subtrait of `Fn`. It allows the closure to mutate the captured variables." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Generated number: 11\n", "Generated number: 12\n", "Generated number: 13\n" ] } ], "source": [ "fn create_generator(seed: i32) -> impl FnMut() -> i32 {\n", " let mut state = seed;\n", " move || {\n", " state += 1; // mutating captured state\n", " state\n", " }\n", "}\n", "\n", "fn main() {\n", " let mut generator = create_generator(10);\n", " println!(\"Generated number: {}\", generator());\n", " println!(\"Generated number: {}\", generator());\n", " println!(\"Generated number: {}\", generator());\n", "}\n", "\n", "main();" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "fn main() {\n", " let mut state = 10;\n", "\n", " let mut f = || {\n", " state += 1; // mutating captured state\n", " state\n", " };\n", "\n", " let mut boxed_f: Boxi32> = Box::new(f);\n", "\n", " boxed_f();\n", " boxed_f();\n", "}\n", "\n", "main();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `FnOnce` Trait\n", "\n", "* Some functions and closures can be called only once. They are not allowed to be called multiple times.\n", "* The trait `FnOnce` is the trait of things that can be called once. It is implemented by functions and closures that can be called only once.\n", " * The `FnOnce` trait is a subtrait of the `FnMut` trait, which is a subtrait of the `Fn` trait." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "ename": "Error", "evalue": "use of moved value: `f`", "output_type": "error", "traceback": [ "\u001b[31m[E0382] Error:\u001b[0m use of moved value: `f`", " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_15:1:1 \u001b[38;5;246m]\u001b[0m", " \u001b[38;5;246m│\u001b[0m", " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m|\u001b[0m\u001b[38;5;249m|\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;68m_\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mg\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249me\u001b[0m", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: closure cannot be invoked more than once because it moves the variable `my_string` out of its environment", " \u001b[38;5;240m │\u001b[0m ", " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;37mf\u001b[0m\u001b[38;5;54m(\u001b[0m\u001b[38;5;54m)\u001b[0m\u001b[38;5;249m;\u001b[0m", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m┬\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: this value implements `FnOnce`, which causes it to be moved when called", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `f` moved due to this call", " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mf\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m ", " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m value used here after move", "\u001b[38;5;246m───╯\u001b[0m" ] } ], "source": [ "fn main() {\n", " let my_string = \"Hello, World!\".to_string();\n", " let f = || drop(my_string); // this closure should be called only once\n", "\n", " f();\n", " f();\n", "}" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Data: [1, 2, 3, 4, 5, 665]\n" ] } ], "source": [ "fn main() {\n", " let mut data = vec![1, 2, 3, 4, 5];\n", "\n", " let mut f_once = || {\n", " data.push(665); // mutating captured data\n", " data // moving the data out of the closure\n", " };\n", "\n", " let mut f_once_boxed: Box Vec> = Box::new(f_once);\n", "\n", " let data = f_once_boxed();\n", " println!(\"Data: {:?}\", data);\n", "}\n", "\n", "main();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `Fn` is the family of closures and functions that you can call multiple times without restriction. This highest category also includes all fn functions.\n", "\n", "* `FnMut` is the family of closures that can be called multiple times if the closure itself is declared mut.\n", "\n", "* `FnOnce` is the family of closures that can be called once, if the caller owns the closure.\n", "\n", "* Every `Fn` meets the requirements for `FnMut`, and every `FnMut` meets the requirements for `FnOnce`" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "// mod explain {\n", "// // Pseudocode for `Fn`, `FnMut`, and `FnOnce` traits.\n", "// trait Fn() -> R {\n", "// fn call(&self) -> R;\n", "// }\n", "\n", "// trait FnMut() -> R {\n", "// fn call_mut(&mut self) -> R;\n", "// }\n", "\n", "// trait FnOnce() -> R {\n", "// fn call_once(self) -> R;\n", "// }\n", "// }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Copy and Clone for Closures\n", "\n", "* Just as Rust automatically figures out which closures can be called only once, it can figure out which closures can implement `Copy` and `Clone`, and which cannot." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "fn main() {\n", " let y = 4;\n", " let add_y = move |x| x + y;\n", " let copy_of_add_y = add_y; // closure is copied\n", " assert_eq!(add_y(copy_of_add_y(3)), 11);\n", "\n", " let word: String = \"Hello\".to_string();\n", " let append_word = move |mut s: String| {\n", " s.push_str(&word); \n", " s\n", " };\n", " let copy_of_append_word = append_word.clone(); // closure is cloned\n", " assert_eq!(append_word(copy_of_append_word(\" World\".to_string())), \" WorldHelloHello\");\n", "}\n", "\n", "main();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Callbacks" ] }, { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "rust" } }, "source": [ "* Callbacks are functions that are passed as arguments to other functions.\n", "* Callbacks are used to implement event handlers, asynchronous programming, and other patterns." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "type Callback = Box;\n", "\n", "struct TemperaturMonitor {\n", " current_temp: f32,\n", " on_temperature_changed: Option,\n", "}" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "impl TemperaturMonitor {\n", " fn new() -> Self {\n", " Self {\n", " current_temp: 0.0,\n", " on_temperature_changed: None,\n", " }\n", " }\n", "\n", " fn set_on_temperature_changed(&mut self, callback: F) {\n", " self.on_temperature_changed = Some(Box::new(callback));\n", " }\n", "\n", " fn set_current_temp(&mut self, temp: f32) {\n", " self.current_temp = temp;\n", " if let Some(ref on_temperature_changed) = self.on_temperature_changed {\n", " on_temperature_changed(self.current_temp);\n", " }\n", " } \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Rust makes us add this `'static bound`. Without it, the call to `Box::new(callback)` would be an error, because it’s not safe to store a closure if it contains borrowed references to variables that are about to go out of scope.)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Temperature changed to: 22.5\n", "Temperature changed to: 23.5\n", "Current temperature is: 24.7°C\n", "Current temperature is: 27.1°C\n" ] } ], "source": [ "fn main() {\n", " let mut monitor = TemperaturMonitor::new();\n", " monitor.set_on_temperature_changed(|temp| println!(\"Temperature changed to: {}\", temp));\n", " monitor.set_current_temp(22.5);\n", " monitor.set_current_temp(23.5);\n", "\n", " monitor.set_on_temperature_changed(|temp| println!(\"Current temperature is: {}°C\", temp));\n", " monitor.set_current_temp(24.7);\n", " monitor.set_current_temp(27.1);\n", "}\n", "\n", "main();" ] } ], "metadata": { "kernelspec": { "display_name": "Rust", "language": "rust", "name": "rust" }, "language_info": { "codemirror_mode": "rust", "file_extension": ".rs", "mimetype": "text/rust", "name": "rust", "pygment_lexer": "rust", "version": "" } }, "nbformat": 4, "nbformat_minor": 2 }