trainings/Rust_2026/rust-bs-2026-09-14/notebooks/14_closures.ipynb

718 lines
31 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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<Person> {\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<String, String> {\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<Person> = 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<Person> = create_people();\n",
" let name_translator: HashMap<String, String> = 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<Person> = 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<Person>, predicate: fn(&Person) -> bool) -> Vec<Person> {\n",
" let mut qualified: Vec<Person> = 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<F>(people: &Vec<Person>, predicate: F) -> Vec<Person>\n",
" where F: Fn(&Person) -> bool \n",
"{\n",
" let mut qualified: Vec<Person> = 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: Box<dyn FnMut()->i32> = 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<dyn FnOnce() -> Vec<i32>> = 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<dyn Fn(f32)>;\n",
"\n",
"struct TemperaturMonitor {\n",
" current_temp: f32,\n",
" on_temperature_changed: Option<Callback>,\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<F: Fn(f32) + 'static>(&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
}