Files
rust-by-practice/functional-programing/closure.html
sunface 987c59ed75 deploy
2025-04-25 12:48:03 +08:00

605 lines
23 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Closure - Rust By Practice</title>
<!-- Custom HTML head -->
<meta name="description" content="Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../theme/style1.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust By Practice</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice/edit/master/en/src/functional-programing/closure.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="closure"><a class="header" href="#closure">Closure</a></h1>
<p>Closures can capture the enclosing environments. For example we can capture the <code>x</code> variable :</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let x = 1;
let closure = |val| val + x;
assert_eq!(closure(2), 3);
}</code></pre></pre>
<p>From the syntax, we can see that closures are very convenient for on the fly usage. Unlike functions, both the input and return types of a closure can be inferred by the compiler.</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
// Increment via closures and functions.
fn function(i: i32) -&gt; i32 { i + 1 }
// Closures are anonymous, here we are binding them to references
//
// These nameless functions are assigned to appropriately named variables.
let closure_annotated = |i: i32| -&gt; 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());
}</code></pre></pre>
<h2 id="capturing"><a class="header" href="#capturing">Capturing</a></h2>
<p>Closures can capture variables by borrowing or moving. But they prefer to capture by borrowing and only go lower when required:</p>
<ul>
<li>By reference: <code>&amp;T</code></li>
<li>By mutable reference: <code>&amp;mut T</code></li>
<li>By value: <code>T</code></li>
</ul>
<ol>
<li>🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work with least amount of changes*/
fn main() {
let color = String::from("green");
let print = move || println!("`color`: {}", color);
print();
print();
// `color` can be borrowed immutably again, because the closure only holds
// an immutable reference to `color`.
let _reborrow = &amp;color;
println!("{}",color);
}</code></pre></pre>
<ol start="2">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work
- Dont use `_reborrow` and `_count_reborrowed`
- Dont modify `assert_eq`
*/
fn main() {
let mut count = 0;
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
inc();
let _reborrow = &amp;count;
inc();
// The closure no longer needs to borrow `&amp;mut count`. Therefore, it is
// possible to reborrow without an error
let _count_reborrowed = &amp;mut count;
assert_eq!(count, 0);
}</code></pre></pre>
<ol start="3">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work in two ways, none of them is to remove `take(movable)` away from the code
*/
fn main() {
let movable = Box::new(3);
let consume = || {
println!("`movable`: {:?}", movable);
take(movable);
};
consume();
consume();
}
fn take&lt;T&gt;(_v: T) {}</code></pre></pre>
<p>For comparison, the following code has no error:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let movable = Box::new(3);
let consume = move || {
println!("`movable`: {:?}", movable);
};
consume();
consume();
}</code></pre></pre>
<h2 id="type-inferred"><a class="header" href="#type-inferred">Type inferred</a></h2>
<p>The following four closures has no difference in input and return types.</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn add_one_v1 (x: u32) -&gt; u32 { x + 1 }
let add_one_v2 = |x: u32| -&gt; u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
<span class="boring">}</span></code></pre></pre>
<ol start="4">
<li>🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
/* Make it work, only change the following line */
let n = example_closure(5);
}</code></pre></pre>
<h2 id="fn-fnmut-fnonce"><a class="header" href="#fn-fnmut-fnonce">Fn, FnMut, FnOnce</a></h2>
<p>When taking a closure as an input parameter, the closure's complete type must be annotated using one of the following traits:</p>
<ul>
<li>Fn: the closure uses the captured value by reference (&amp;T)</li>
<li>FnMut: the closure uses the captured value by mutable reference (&amp;mut T)</li>
<li>FnOnce: the closure uses the captured value by value (T)</li>
</ul>
<ol start="5">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work by changing the trait bound, in two ways*/
fn fn_once&lt;F&gt;(func: F)
where
F: FnOnce(usize) -&gt; bool,
{
println!("{}", func(3));
println!("{}", func(4));
}
fn main() {
let x = vec![1, 2, 3];
fn_once(|z|{z == x.len()})
}</code></pre></pre>
<ol start="6">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let mut s = String::new();
let update_string = |str| s.push_str(str);
exec(update_string);
println!("{:?}",s);
}
/* Fill in the blank */
fn exec&lt;'a, F: __&gt;(mut f: F) {
f("hello")
}</code></pre></pre>
<h4 id="which-trait-does-the-compiler-prefer-to-use"><a class="header" href="#which-trait-does-the-compiler-prefer-to-use">Which trait does the compiler prefer to use?</a></h4>
<ul>
<li>Fn: the closure uses the captured value by reference (&amp;T)</li>
<li>FnMut: the closure uses the captured value by mutable reference (&amp;mut T)</li>
<li>FnOnce: the closure uses the captured value by value (T)</li>
</ul>
<p>On a variable-by-variable basis, the compiler will capture variables in the least restrictive manner possible.</p>
<p>For instance, consider a parameter annotated as FnOnce. This specifies that the closure may capture by <code>&amp;T</code>, <code>&amp;mut T</code>, or <code>T</code>, but the compiler will ultimately choose based on how the captured variables are used in the closure.
Which trait to use is determined by what the closure does with captured value.</p>
<p>This is because if a move is possible, then any type of borrow should also be possible. Note that the reverse is not true. If the parameter is annotated as <code>Fn</code>, then capturing variables by <code>&amp;mut T</code> or <code>T</code> are not allowed.</p>
<ol start="7">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Fill in the blank */
// A function which takes a closure as an argument and calls it.
// &lt;F&gt; denotes that F is a "Generic type parameter"
fn apply&lt;F&gt;(f: F) where
// The closure takes no input and returns nothing.
F: __ {
f();
}
// A function which takes a closure and returns an `i32`.
fn apply_to_3&lt;F&gt;(f: F) -&gt; i32 where
// The closure takes an `i32` and returns an `i32`.
F: Fn(i32) -&gt; i32 {
f(3)
}
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));
}</code></pre></pre>
<p>Move closures may still implement <code>Fn</code> or <code>FnMut</code>, even though they capture variables by move. This is because the traits implemented by a closure type are determined by what the closure does with captured values, not how it captures them. The <code>move</code> keyword only specifies the latter.</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let s = String::new();
let update_string = move || println!("{}",s);
exec(update_string);
}
fn exec&lt;F: FnOnce()&gt;(f: F) {
f()
}</code></pre></pre>
<p>The following code also has no error:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let s = String::new();
let update_string = move || println!("{}",s);
exec(update_string);
}
fn exec&lt;F: Fn()&gt;(f: F) {
f()
}</code></pre></pre>
<ol start="8">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Fill in the blank */
fn main() {
let mut s = String::new();
let update_string = |str| -&gt; String {s.push_str(str); s };
exec(update_string);
}
fn exec&lt;'a, F: __&gt;(mut f: F) {
f("hello");
}</code></pre></pre>
<h2 id="input-functions"><a class="header" href="#input-functions">Input functions</a></h2>
<p>Since closure can be used as arguments, you might wonder can we use functions as arguments too? And indeed we can.</p>
<ol start="9">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">
/* Implement `call_me` to make it work */
fn call_me {
f();
}
fn function() {
println!("I'm a function!");
}
fn main() {
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}</code></pre></pre>
<h2 id="closure-as-return-types"><a class="header" href="#closure-as-return-types">Closure as return types</a></h2>
<p>Returning a closure is much harder than you may have thought of.</p>
<ol start="10">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Fill in the blank using two approaches,
and fix the error */
fn create_fn() -&gt; __ {
let num = 5;
// How does the following closure capture the environment variable `num`
// &amp;T, &amp;mut T, T ?
|x| x + num
}
fn main() {
let fn_plain = create_fn();
fn_plain(1);
}</code></pre></pre>
<ol start="11">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Fill in the blank and fix the error*/
fn factory(x:i32) -&gt; __ {
let num = 5;
if x &gt; 1{
move |x| x + num
} else {
move |x| x + num
}
}</code></pre></pre>
<h2 id="closure-in-structs"><a class="header" href="#closure-in-structs">Closure in structs</a></h2>
<p><strong>Example</strong></p>
<pre><pre class="playground"><code class="language-rust edition2021">struct Cacher&lt;T,E&gt;
where
T: Fn(E) -&gt; E,
E: Copy
{
query: T,
value: Option&lt;E&gt;,
}
impl&lt;T,E&gt; Cacher&lt;T,E&gt;
where
T: Fn(E) -&gt; E,
E: Copy
{
fn new(query: T) -&gt; Cacher&lt;T,E&gt; {
Cacher {
query,
value: None,
}
}
fn value(&amp;mut self, arg: E) -&gt; E {
match self.value {
Some(v) =&gt; v,
None =&gt; {
let v = (self.query)(arg);
self.value = Some(v);
v
}
}
}
}
fn main() {
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 1);
}</code></pre></pre>
<blockquote>
<p>You can find the solutions <a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/functional-programing/closure.md">here</a>(under the solutions path), but only use it when you need it :)</p>
</blockquote>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../functional-programing/intro.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../functional-programing/iterator.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../functional-programing/intro.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../functional-programing/iterator.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../ace.js"></script>
<script src="../editor.js"></script>
<script src="../mode-rust.js"></script>
<script src="../theme-dawn.js"></script>
<script src="../theme-tomorrow_night.js"></script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../assets/custom3.js"></script>
<script src="../assets/lang1.js"></script>
</div>
</body>
</html>