diff --git a/404.html b/404.html new file mode 100644 index 0000000..f8414f0 --- /dev/null +++ b/404.html @@ -0,0 +1,3 @@ + +404 Not Found +

404 Not Found

diff --git a/atom.xml b/atom.xml new file mode 100644 index 0000000..817e8a7 --- /dev/null +++ b/atom.xml @@ -0,0 +1,9224 @@ + + + Rust course + A Rust course at MIM UW + + + Zola + 2024-11-27T00:00:00+00:00 + https://mimuw-jnp2-rust.github.io/atom.xml + + Async: Part 1 + 2024-11-27T00:00:00+00:00 + 2024-11-27T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/11-async-1/ + + <h2 id="tokio">Tokio</h2> +<p>We'll use the <a href="https://tokio.rs/tokio/tutorial">Tokio tutorial</a> (chapters <code>Overview</code>-<code>Channels</code>).</p> +<h2 id="common-rust-lifetime-misconceptions">Common Rust Lifetime Misconceptions</h2> +<p>Please read <a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md">this blogpost</a>.</p> +<h2 id="assignment-6-graded">Assignment 6 (graded)</h2> +<p><a href="https://classroom.github.com/a/eYvC5SSn">Calculator</a></p> +<p>Deadline: 04.12.2024 23:59</p> + + + + + Fearless concurrency + 2024-11-14T00:00:00+00:00 + 2024-11-14T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/ + + <h2 id="parallelism-vs-concurrency">Parallelism vs Concurrency</h2> +<p>Concurrency is when tasks <strong>can make</strong> progress <strong>independently</strong> of each other.</p> +<p>Parallelism is when multiple tasks <strong>make</strong> progress <strong>at the same time</strong>.</p> +<h1 id="concurrency-models-in-rust">Concurrency models in Rust</h1> +<h2 id="threads">Threads</h2> +<p>Nothing unusual here.</p> +<p>Threads can be created with the <code>thread::spawn</code> function <a href="https://doc.rust-lang.org/std/thread/fn.spawn.html">docs - please read them!</a>.</p> +<p>This method returns a <code>JoinHandle&lt;T&gt;</code> which can be used to wait for the thread to finish. <code>T</code> is the type of the thread's return value.</p> +<p>Another way to spawn threads is using <a href="https://doc.rust-lang.org/std/thread/fn.scope.html"><code>scope</code></a>. Threads created in such way are mandatorily joined at the end of the scope, which guarantees that they will borrow items for no longer that the lifetime of the scope. Hence, they can borrow non-<code>'static</code> items!</p> +<h4 id="propagating-panics">Propagating panics</h4> +<p>In Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).</p> +<h4 id="closures">Closures</h4> +<p>Closures which are used to create threads must take ownership of any values they use. It can be forced with the <code>move</code> keyword.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::thread; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> v </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> handle </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Here&#39;s a vector: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, v); +</span><span> }); +</span><span> +</span><span> handle.</span><span style="color:#4271ae;">join</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span>} +</span></code></pre> +<p>Normal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!</p> +<p>But what if we need to share some state?</p> +<h3 id="send-and-sync">Send and Sync</h3> +<p>They are marker traits used to indicate that a type or a reference to it can be used across threads. See the <a href="https://doc.rust-lang.org/nomicon/send-and-sync.html">nomicon</a> for more information.</p> +<blockquote> +<ul> +<li>A type is <code>Send</code> if it is safe to move it (<em>send</em> it) to another thread.</li> +<li>A type is <code>Sync</code> if it is safe to share (<em>sync</em>) between threads (<code>T</code> is <code>Sync</code> if and only if <code>&amp;T</code> is <code>Send</code>).</li> +</ul> +</blockquote> +<p>This makes sense, because <code>Sync</code> is about <em>sharing</em> object between threads, and <code>&amp;</code> is the <em>shared</em> reference.</p> +<p>There is also <a href="https://users.rust-lang.org/t/example-of-a-type-that-is-not-send/59835/3">a great answer on Rust forum</a>, listing + explaining example types that are <code>!Send</code> or <code>!Sync</code>.</p> +<p>For more convenient analysis, examples are listed here:</p> +<h4 id="send-sync"><code>Send + !Sync</code>:</h4> +<ul> +<li><code>UnsafeCell</code> (=&gt; <code>Cell</code>, <code>RefCell</code>);</li> +</ul> +<h4 id="send-sync-1"><code>!Send + !Sync</code>:</h4> +<ul> +<li><code>Rc</code></li> +<li><code>*const T</code>, <code>*mut T</code> (raw pointers)</li> +</ul> +<h4 id="send-sync-2"><code>!Send + Sync</code>:</h4> +<ul> +<li><code>MutexGuard</code> (hint: <code>!Send</code> for POSIX reasons)</li> +</ul> +<p>Exercise for the reader: explain reasons for all limitations of the above types.</p> +<h2 id="sharing-state-between-threads">Sharing state between threads</h2> +<h3 id="message-passing">Message passing</h3> +<p>One possible way is to use message passing. We can use a blocking queue (called <code>mpsc</code> - <a href="https://doc.rust-lang.org/std/sync/mpsc/index.html">&quot;multi producer single consumer FIFO queue&quot;</a>) to do it. +We talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.</p> +<h3 id="mutexes">Mutexes</h3> +<p>In Rust, a mutex <em>wraps</em> a value and makes it thread-safe. +Because it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the <code>RefCell</code> type.</p> +<p><code>Arc</code> is a smart pointer like <code>Rc</code> but it can be shared between threads.</p> +<p>Please read more about them in <a href="https://doc.rust-lang.org/stable/book/ch16-03-shared-state.html">the book</a>.</p> +<p><a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html">The docs</a> also mention <code>poisoning</code>.</p> +<h3 id="rwlocks">RwLocks</h3> +<p><a href="https://doc.rust-lang.org/std/sync/struct.RwLock.html">RwLocks</a> are similar to mutexes, but they distinguish between read and write locks.</p> +<h3 id="atomic-types">Atomic types</h3> +<p>Atomic types are described in <a href="https://doc.rust-lang.org/std/sync/atomic/">the docs</a>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Arc; +</span><span style="color:#8959a8;">use </span><span>std::sync::atomic::{AtomicUsize, Ordering}; +</span><span style="color:#8959a8;">use </span><span>std::{hint, thread}; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(AtomicUsize::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span> </span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> spinlock_clone.</span><span style="color:#4271ae;">store</span><span>(</span><span style="color:#f5871f;">0</span><span>, Ordering::SeqCst); +</span><span> }); +</span><span> +</span><span> </span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span> </span><span style="color:#8959a8;">while</span><span> spinlock.</span><span style="color:#4271ae;">load</span><span>(Ordering::SeqCst) </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Err</span><span>(panic) </span><span style="color:#3e999f;">=</span><span> thread.</span><span style="color:#4271ae;">join</span><span>() { +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread had an error: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, panic); +</span><span> } +</span><span>} +</span></code></pre> +<p>Note that <code>atomic</code> values don't have to be wrapped in a mutex when shared across threads.</p> +<h3 id="wait">Wait...</h3> +<p>If most types are <code>Sync + Send</code>, then what stops us from using a standard, non-atomic integer in the example above?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0594]: cannot assign to data in an `Arc` +</span><span> --&gt; src/main.rs:9:9 +</span><span> | +</span><span>9 | *spinlock_clone += 1; +</span><span> | ^^^^^^^^^^^^^^^^^^^^ cannot assign +</span><span> | +</span><span> = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc&lt;i32&gt;` +</span></code></pre> +<p>...so we would have to use a <code>RefCell</code> to be able to modify the value through a shared reference...</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(RefCell::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone.</span><span style="color:#4271ae;">borrow_mut</span><span>() </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock.</span><span style="color:#4271ae;">borrow</span><span>() </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<p>...but <code>RefCell</code> isn't <code>Sync</code>:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0277]: `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> --&gt; src/main.rs:9:18 +</span><span> | +</span><span>9 | let thread = thread::spawn(move|| { +</span><span> | ^^^^^^^^^^^^^ `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> | +</span><span> = help: the trait `Sync` is not implemented for `RefCell&lt;i32&gt;` +</span><span> = note: required because of the requirements on the impl of `Send` for `Arc&lt;RefCell&lt;i32&gt;&gt;` +</span><span> = note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]` +</span><span>note: required by a bound in `spawn` +</span></code></pre> +<p>And that bound mentioned in the last line looks like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">spawn</span><span>&lt;F, T&gt;(</span><span style="color:#f5871f;">f</span><span>: F) -&gt; JoinHandle&lt;T&gt; </span><span style="color:#8959a8;">where +</span><span> F: FnOnce() -&gt; T, +</span><span> F: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span><span> T: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span></code></pre> +<h4 id="exercise-for-the-reader">Exercise for the reader</h4> +<p>Why is it impossible to share a reference to a <code>Mutex</code> between threads spawned with <code>std::thread::spawn</code>?</p> +<h2 id="data-parallelism-with-rayon">Data parallelism with Rayon</h2> +<p><a href="https://docs.rs/rayon/latest/rayon/">Rayon</a> is a library for parallelization of data processing. +It can be used to parallelize the execution of functions over a collection of data by switching the standard <code>Iterator</code> to a <code>ParallelIterator</code>. +It works very similar to <a href="https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#executing_streams_in_parallel">Java's parallel streams</a>.</p> +<p>Why do that? Because thread synchronization is hard! <a href="https://doc.rust-lang.org/nomicon/races.html">Rust prevents data races</a>, but <a href="https://users.rust-lang.org/t/deadlock-is-it-a-bug-or-is-it-intentional/1544">logical races and deadlocks are impossible to prevent!</a>!</p> +<p><a href="https://github.com/rayon-rs/rayon/blob/master/FAQ.md">Rayon's FAQ</a> is worth reading.</p> +<h2 id="reading">Reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html">The Book</a></li> +<li><a href="http://archive.today/WFlZV">Safely writing code that isn't thread-safe</a></li> +</ul> +<h2 id="no-assignment-this-week">No assignment this week</h2> +<p>Please work on the first iteration of the big project instead.</p> + + + + + Closures and Iterators + 2024-10-30T00:00:00+00:00 + 2024-10-30T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/ + + <h1 id="closures">Closures</h1> +<p>Closures (Polish: &quot;domknięcia&quot;) are anonymous functions that can access variables from the scope in which they were defined.</p> +<h2 id="closure-syntax">Closure syntax</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> #[</span><span style="color:#c82829;">rustfmt</span><span>::</span><span style="color:#c82829;">skip</span><span>] +</span><span> { +</span><span> </span><span style="color:#999999;">// This is formatted so that without rust-analyzer it renders as well-aligned. +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">add_one_v1 </span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">u32</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ x </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1 </span><span>} </span><span style="color:#999999;">// This is an ordinary function. +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">add_one_v2 </span><span style="color:#3e999f;">= </span><span>|</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">u32</span><span>| -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ x </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1 </span><span>}; </span><span style="color:#999999;">// Closures use pipes instead of parentheses. +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">add_one_v3 </span><span style="color:#3e999f;">= </span><span>|</span><span style="color:#f5871f;">x</span><span>| { x </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1 </span><span>}; </span><span style="color:#999999;">// Both parameters and return value can have their types inferred. +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">add_one_v4 </span><span style="color:#3e999f;">= </span><span>|</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1 </span><span>; </span><span style="color:#999999;">// If the body is a single expression, braces can be omitted. +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _res </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">add_one_v1</span><span>(</span><span style="color:#f5871f;">0_</span><span style="color:#8959a8;">u32</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> _res </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">add_one_v2</span><span>(</span><span style="color:#f5871f;">0_</span><span style="color:#8959a8;">u32</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> _res </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">add_one_v3</span><span>(</span><span style="color:#f5871f;">0_</span><span style="color:#8959a8;">u32</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> _res </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">add_one_v4</span><span>(</span><span style="color:#f5871f;">0_</span><span style="color:#8959a8;">u32</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// This does not compile, because closures are not generic. +</span><span> </span><span style="color:#999999;">// Their type is inferred once and stays the same. +</span><span> </span><span style="color:#999999;">// let _res = add_one_v4(0_i32); +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/closures_syntax.rs">closures_syntax.rs</a>)</sub></p> +<h2 id="closures-types">Closures' types</h2> +<p>Closures are unnameable types. That is, each closure gets its own unique type from the compiler, +but we cannot name it. Therefore, closures' types must be inferred. +We will often use <code>impl</code> keyword with closure traits (e.g., <code>impl Fn</code>) - those traits are described below.</p> +<h2 id="closures-capture-environment">Closures capture environment</h2> +<p>Closures can capture variables from the environment where they are defined. They can do that in two ways:</p> +<ul> +<li>Capturing References (borrowing), or</li> +<li>Moving Ownership.</li> +</ul> +<p><strong>HOW</strong> closures capture variables is one thing. +But even more important is <strong>WHAT</strong> closures do with their captures.</p> +<h3 id="functions-closures-hierarchy">Functions &amp; closures hierarchy</h3> +<p>Based on <strong>WHAT</strong> a closure does with its captures, it implements closure traits:</p> +<ul> +<li><code>FnOnce</code> - closures that may move out of their captures environment (and thus called once).</li> +<li><code>FnMut</code> - closures that may mutate their captures, but don't move out of their captures environment (so can be called multiple times, but require a mutable reference);</li> +<li><code>Fn</code> - closures that do not mutate their captures (so can be called multiple times through an immutable reference).</li> +</ul> +<p>For completeness, there is a (concrete) type of function pointers:</p> +<ul> +<li><code>fn</code> - functions, closures with no captures.</li> +</ul> +<p>Those traits and the <code>fn</code> type form a hierarchy: <code>fn</code> ⊆ <code>Fn</code> ⊆ <code>FnMut</code> ⊆ <code>FnOnce</code></p> +<!--> $$ fn \subseteq Fn \subseteq FnMut \subseteq FnOnce $$ --> +<p>The following code sample demonstrates various ways to capture environment (borrowing or moving) and various kinds of closures, based on what they do with their captures:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#4271ae;">borrowing_immutably_closure</span><span>(); +</span><span> </span><span style="color:#4271ae;">borrowing_mutably_closure</span><span>(); +</span><span> </span><span style="color:#4271ae;">moving_in_nonmutating_closure</span><span>(); +</span><span> </span><span style="color:#4271ae;">moving_in_mutating_closure</span><span>(); +</span><span> </span><span style="color:#4271ae;">moving_in_moving_out_closure</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">borrowing_immutably_closure</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> println!(</span><span style="color:#718c00;">&quot;Before defining closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">only_borrows </span><span style="color:#3e999f;">= </span><span>|| println!(</span><span style="color:#718c00;">&quot;From closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> +</span><span> </span><span style="color:#999999;">// This would not really only borrow... (it needs Vec by value). +</span><span> </span><span style="color:#999999;">// let only_borrows = || std::mem::drop::&lt;Vec&lt;_&gt;&gt;(list); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Before calling closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> </span><span style="color:#4271ae;">only_borrows</span><span>(); +</span><span> println!(</span><span style="color:#718c00;">&quot;After calling closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">borrowing_mutably_closure</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> println!(</span><span style="color:#718c00;">&quot;Before defining closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut </span><span style="color:#4271ae;">borrows_mutably </span><span style="color:#3e999f;">= </span><span>|| list.</span><span style="color:#4271ae;">push</span><span>(</span><span style="color:#f5871f;">7</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// println!(&quot;Before calling closure: {:?}&quot;, list); +</span><span> </span><span style="color:#4271ae;">borrows_mutably</span><span>(); +</span><span> println!(</span><span style="color:#718c00;">&quot;After calling closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">moving_in_nonmutating_closure</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> println!(</span><span style="color:#718c00;">&quot;Before defining closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> +</span><span> </span><span style="color:#999999;">// This closure would just borrow the list, because it only prints it. +</span><span> </span><span style="color:#999999;">// However, as spawning threads require passing `impl FnOnce + &#39;static`, +</span><span> </span><span style="color:#999999;">// we need to use `move` keyword to force the closure to move `list` +</span><span> </span><span style="color:#999999;">// into its captured environment. +</span><span> std::thread::spawn(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>println!(</span><span style="color:#718c00;">&quot;From thread: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list)) +</span><span> .</span><span style="color:#4271ae;">join</span><span>() +</span><span> .</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">moving_in_mutating_closure</span><span>() { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">append_42</span><span>(</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">appender</span><span>: impl FnMut(</span><span style="color:#f5871f;">i32</span><span>)) { +</span><span> </span><span style="color:#4271ae;">appender</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> appender </span><span style="color:#3e999f;">= </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> println!(</span><span style="color:#718c00;">&quot;Before defining closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, list); +</span><span> +</span><span> </span><span style="color:#999999;">// The `move` keyword is necessary to prevent dangling reference to `list`. +</span><span> </span><span style="color:#999999;">// Of course, the borrow checker protects us from compiling code without `move`. +</span><span> </span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|</span><span>num</span><span style="color:#3e999f;">|</span><span> list.</span><span style="color:#4271ae;">push</span><span>(num) +</span><span> }; +</span><span> +</span><span> </span><span style="color:#4271ae;">append_42</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> appender); +</span><span> </span><span style="color:#4271ae;">append_42</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> appender); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">moving_in_moving_out_closure</span><span>() { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">append_multiple_times</span><span>(</span><span style="color:#f5871f;">appender</span><span>: impl FnOnce(&amp;</span><span style="color:#f5871f;">mut Vec</span><span>&lt;</span><span style="color:#f5871f;">String</span><span>&gt;) + Clone) { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> list </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Vec</span><span>::new(); +</span><span> +</span><span> </span><span style="color:#999999;">// We can clone this `FnOnce`, because we additionally require `Clone`. +</span><span> </span><span style="color:#999999;">// If we didn&#39;t clone it, we couldn&#39;t call it more than *once*. +</span><span> appender.</span><span style="color:#4271ae;">clone</span><span>()(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> list); +</span><span> </span><span style="color:#4271ae;">appender</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> list); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> appender </span><span style="color:#3e999f;">= </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> string </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Ala&quot;</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;Before defining closure: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, string); +</span><span> +</span><span> </span><span style="color:#999999;">// The `move` keyword is necessary to prevent dangling reference to `list`. +</span><span> </span><span style="color:#999999;">// Of course, the borrow checker protects us from compiling code without `move`. +</span><span> </span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|</span><span>list: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c99e00;">Vec</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt;</span><span style="color:#3e999f;">|</span><span> list.</span><span style="color:#4271ae;">push</span><span>(string) +</span><span> }; +</span><span> +</span><span> </span><span style="color:#999999;">// As `appender` is only `FnOnce`, we need to clone before we consume it by calling it. +</span><span> </span><span style="color:#4271ae;">append_multiple_times</span><span>(appender.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">append_multiple_times</span><span>(appender); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/closures_capturing.rs">closures_capturing.rs</a>)</sub></p> +<h3 id="closures-as-trait-objects-in-dynamic-dispatch">Closures as trait objects (in dynamic dispatch)</h3> +<p>The following code sample shows how one can use closures as <code>dyn Trait</code> objects, bypassing the problem of them having anonymous types:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">some_function</span><span>() -&gt; String { +</span><span> </span><span style="color:#c99e00;">String</span><span>::new() +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> v1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;v1&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let mut </span><span style="color:#4271ae;">borrowing_immutably_closure </span><span style="color:#3e999f;">= </span><span>|| v1.</span><span style="color:#4271ae;">clone</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> v2 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;v2&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let mut </span><span style="color:#4271ae;">borrowing_mutably_closure </span><span style="color:#3e999f;">= </span><span>|| { +</span><span> v2.</span><span style="color:#4271ae;">push</span><span>(</span><span style="color:#718c00;">&#39;.&#39;</span><span>); +</span><span> v2.</span><span style="color:#4271ae;">clone</span><span>() +</span><span> }; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> v3 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;v3&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let mut</span><span> moving_in_nonmutating_closure </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">||</span><span> v3.</span><span style="color:#4271ae;">clone</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> v4 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;v4&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let mut</span><span> moving_in_mutating_closure </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> v4.</span><span style="color:#4271ae;">push</span><span>(</span><span style="color:#718c00;">&#39;.&#39;</span><span>); +</span><span> v4.</span><span style="color:#4271ae;">clone</span><span>() +</span><span> }; +</span><span> </span><span style="color:#8959a8;">let</span><span> v5 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;v5&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">moving_in_moving_out_closure </span><span style="color:#3e999f;">= </span><span>|| v5; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> fn_once_callables: [</span><span style="color:#3e999f;">&amp;</span><span>dyn </span><span style="color:#c99e00;">FnOnce</span><span>() -&gt; String; </span><span style="color:#f5871f;">5</span><span>] </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>some_function, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>borrowing_immutably_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>borrowing_mutably_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>moving_in_nonmutating_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>moving_in_moving_out_closure, +</span><span> ]; +</span><span> +</span><span> #[</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> </span><span style="color:#8959a8;">for</span><span> fn_once_callable </span><span style="color:#3e999f;">in</span><span> fn_once_callables { +</span><span> </span><span style="color:#999999;">// Cannot move a value of type `dyn FnOnce() -&gt; String`. +</span><span> </span><span style="color:#999999;">// The size of `dyn FnOnce() -&gt; String` cannot be statically determined. +</span><span> </span><span style="color:#999999;">// println!(&quot;{}&quot;, fn_once_callable()); +</span><span> +</span><span> </span><span style="color:#999999;">// So, for FnOnce, we need to be their owners to be able to call them, +</span><span> </span><span style="color:#999999;">// and we can&#39;t have a `dyn` object owned on stack. +</span><span> </span><span style="color:#999999;">// We will solve this problem soon with smart pointers (e.g., Box). +</span><span> </span><span style="color:#999999;">// This will give us `std::function` -like experience. +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Mutable reference to FnMut is required to be able to call it. +</span><span> </span><span style="color:#8959a8;">let</span><span> fn_mut_callables: [</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> dyn </span><span style="color:#c99e00;">FnMut</span><span>() -&gt; String; </span><span style="color:#f5871f;">4</span><span>] </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> borrowing_immutably_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> borrowing_mutably_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> moving_in_nonmutating_closure, +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> moving_in_mutating_closure, +</span><span> ]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for</span><span> fn_mut_callable </span><span style="color:#3e999f;">in</span><span> fn_mut_callables { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#4271ae;">fn_mut_callable</span><span>()); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> fn_callables: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#3e999f;">&amp;</span><span>dyn </span><span style="color:#c99e00;">Fn</span><span>() -&gt; String] </span><span style="color:#3e999f;">= +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#3e999f;">&amp;</span><span>borrowing_immutably_closure, </span><span style="color:#3e999f;">&amp;</span><span>moving_in_nonmutating_closure]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for</span><span> fn_callable </span><span style="color:#3e999f;">in</span><span> fn_callables { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#4271ae;">fn_callable</span><span>()); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/closures_fun.rs">closures_fun.rs</a>)</sub></p> +<h2 id="examples">Examples</h2> +<p>We'll go through the examples from <a href="https://doc.rust-lang.org/rust-by-example/fn/closures.html">Rust by Example</a>. +More examples will be seen when working with iterators.</p> +<h1 id="iterators">Iterators</h1> +<p>In Rust, there is no hierarchy of types for collections (because there is no inheritance in general). +Instead, what makes a collection is that it can be iterated over.</p> +<p>A usual way in Rust to perform an iteration over something, be it a range of values or items in a collection, is creating a (lazy) iterator over it and transforming it using <em>iterator adaptors</em>. For example, if <code>T: Iterator</code>, then <code>T::map()</code> creates a <code>Map&lt;T&gt;</code> adaptor. Once a final iterator is created, it has to be actually activated (iterated over), which is most commonly done by:</p> +<ul> +<li>exhausting it with the <code>for</code> loop,</li> +<li>manually iterating over it using <code>next()</code> calls,</li> +<li>collecting its contents into inferred collection (<code>collect()</code>),</li> +<li>consuming it with a <em>consuming adaptor</em> (e.g., <code>sum()</code>, <code>count</code>),</li> +</ul> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::collections::HashSet; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Various ways to create a String. +</span><span> </span><span style="color:#8959a8;">let mut</span><span> strings </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#c99e00;">String</span><span>::new(), +</span><span> </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a&quot;</span><span>), +</span><span> </span><span style="color:#718c00;">&quot;b&quot;</span><span>.</span><span style="color:#4271ae;">into</span><span>(), +</span><span> </span><span style="color:#718c00;">&quot;c&quot;</span><span>.</span><span style="color:#4271ae;">to_owned</span><span>(), +</span><span> </span><span style="color:#718c00;">&quot;d&quot;</span><span>.</span><span style="color:#4271ae;">to_string</span><span>(), +</span><span> </span><span style="color:#718c00;">&quot;e&quot;</span><span>.</span><span style="color:#4271ae;">chars</span><span>().</span><span style="color:#4271ae;">collect</span><span>(), +</span><span> ]; +</span><span> +</span><span> </span><span style="color:#999999;">// `iter()` is a usual method that creates an iterator over immutable references to the collection&#39;s items. +</span><span> </span><span style="color:#8959a8;">let</span><span> _all_len_0_or_1 </span><span style="color:#3e999f;">=</span><span> strings +</span><span> .</span><span style="color:#4271ae;">iter</span><span>() +</span><span> .</span><span style="color:#4271ae;">filter</span><span>(|</span><span style="color:#f5871f;">s</span><span>| </span><span style="color:#3e999f;">!</span><span>s.</span><span style="color:#4271ae;">is_empty</span><span>()) +</span><span> .</span><span style="color:#4271ae;">all</span><span>(|</span><span style="color:#f5871f;">s</span><span>| s.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// `iter_mut()` is a usual method that creates an iterator over mutable references to the collection&#39;s items. +</span><span> </span><span style="color:#8959a8;">for</span><span> s </span><span style="color:#3e999f;">in</span><span> strings.</span><span style="color:#4271ae;">iter_mut</span><span>().</span><span style="color:#4271ae;">map_while</span><span>(|</span><span style="color:#f5871f;">s</span><span>| </span><span style="color:#8959a8;">match</span><span> s.</span><span style="color:#4271ae;">as_str</span><span>() { +</span><span> </span><span style="color:#718c00;">&quot;c&quot; </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c99e00;">None</span><span>, +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span style="color:#c99e00;">Some</span><span>(s), +</span><span> }) { +</span><span> </span><span style="color:#3e999f;">*</span><span>s </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#718c00;">&quot;b&quot;</span><span>, </span><span style="color:#718c00;">&quot;aba&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// This code is equivalent to the `for` above. +</span><span> </span><span style="color:#999999;">// `for` is usually more idiomatic, but `for_each` is sometimes cleaner and sometimes faster. +</span><span> strings +</span><span> .</span><span style="color:#4271ae;">iter_mut</span><span>() +</span><span> .</span><span style="color:#4271ae;">map_while</span><span>(|</span><span style="color:#f5871f;">s</span><span>| </span><span style="color:#8959a8;">match</span><span> s.</span><span style="color:#4271ae;">as_str</span><span>() { +</span><span> </span><span style="color:#718c00;">&quot;c&quot; </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c99e00;">None</span><span>, +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span style="color:#c99e00;">Some</span><span>(s), +</span><span> }) +</span><span> .</span><span style="color:#4271ae;">for_each</span><span>(|</span><span style="color:#f5871f;">s</span><span>| </span><span style="color:#3e999f;">*</span><span>s </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#718c00;">&quot;b&quot;</span><span>, </span><span style="color:#718c00;">&quot;aba&quot;</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// `into_iter()` is a method from `IntoIterator` trait that converts a collection to an iterator +</span><span> </span><span style="color:#8959a8;">let mut</span><span> empty_strings_iter </span><span style="color:#3e999f;">=</span><span> strings.</span><span style="color:#4271ae;">into_iter</span><span>().</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">s</span><span>| { +</span><span> s.</span><span style="color:#4271ae;">clear</span><span>(); +</span><span> s +</span><span> }); +</span><span> +</span><span> </span><span style="color:#999999;">// This is a set of empty Strings... +</span><span> </span><span style="color:#8959a8;">let</span><span> empty_strings_set </span><span style="color:#3e999f;">=</span><span> empty_strings_iter.</span><span style="color:#4271ae;">clone</span><span>().collect::&lt;HashSet&lt;</span><span style="color:#3e999f;">_</span><span>&gt;&gt;(); +</span><span> +</span><span> </span><span style="color:#999999;">// And this is a Vec of immutable references to empty Strings. +</span><span> </span><span style="color:#8959a8;">let</span><span> empty_string_refs_vec </span><span style="color:#3e999f;">=</span><span> empty_strings_set.</span><span style="color:#4271ae;">iter</span><span>().collect::&lt;</span><span style="color:#c99e00;">Vec</span><span>&lt;</span><span style="color:#3e999f;">_</span><span>&gt;&gt;(); +</span><span> +</span><span> </span><span style="color:#999999;">// equivalent to `empty_string_refs_vec.into_iter()` +</span><span> </span><span style="color:#8959a8;">for</span><span> s </span><span style="color:#3e999f;">in</span><span> empty_string_refs_vec { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s) +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">while let </span><span style="color:#c99e00;">Some</span><span>(s) </span><span style="color:#3e999f;">=</span><span> empty_strings_iter.</span><span style="color:#4271ae;">next_back</span><span>() { +</span><span> assert!(s.</span><span style="color:#4271ae;">is_empty</span><span>()); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/iterator_exhaustion.rs">iterator_exhaustion.rs</a>)</sub></p> +<p>Iterators are highly optimised, so they are high-level code that compiles down to simple and optimised machine code (intended as <em>zero-cost abstractions</em>).</p> +<p>We'll go through the official <a href="https://doc.rust-lang.org/stable/std/iter/">docs</a>.</p> +<ul> +<li>Most methods are defined in the <a href="https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html">Iterator trait</a>.</li> +<li><a href="https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html">IntoIterator</a> is also worth noting, because it makes types work with the <code>for</code> loop.</li> +<li>For completeness, there is <a href="https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html">FromIterator</a>, which is required for <code>collect()</code> to work.</li> +</ul> +<h1 id="reading">Reading</h1> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch12-00-an-io-project.html">The Book, chapter 12 (that's a project!)</a></li> +<li><a href="https://doc.rust-lang.org/book/ch13-00-functional-features.html">The Book, chapter 13</a></li> +<li><a href="https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html">The Book, chapter 14</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-05-advanced-functions-and-closures.html">The Book, Advanced Functions and Closures</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html">The Book, Advanced Traits</a></li> +</ul> +<h2 id="assignment-4-graded">Assignment 4 (graded)</h2> +<p><a href="https://classroom.github.com/a/9aJix-LK">Lazy</a></p> +<p>Deadline: 06.11.2024 23:59</p> + + + + + Reasoning About Types + 2024-10-24T00:00:00+00:00 + 2024-10-24T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/ + + <h1 id="type-traits">Type traits</h1> +<p>Traits are a way to defined common behavior between different types. They can be compared to <em>interfaces</em> from many other mainstream languages or to type classes from Haskell, however, Rust is not an object-oriented language and there are some notable differences between type traits and Java interfaces.</p> +<p>The way we describe behavior in Rust is through methods. Traits consist of a set of these methods which then should be implemented by a type. We've already encountered examples of these, like the <code>Clone</code> trait which specified that the <code>clone()</code> method can be called on some given type. Now, let's take a deeper look and try defining our own trait.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.username, </span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/basic_trait.rs">basic_trait.rs</a>)</sub></p> +<h2 id="default-implementations">Default implementations</h2> +<p>Trait definitions can also be provided with default implementations of behaviors.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Upload { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">struct </span><span>Photo { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span> </span><span style="color:#c82829;">width</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">height</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Description { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;No description available.&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// All default implementations +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Upload {} +</span><span> +</span><span style="color:#999999;">// Default implementations can be overwritten +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> x </span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.filename, </span><span style="color:#c82829;">self</span><span>.width, </span><span style="color:#c82829;">self</span><span>.height) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Default implementations can rely on methods with no defaults +</span><span style="color:#8959a8;">trait </span><span>Size { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">size</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">width</span><span>() </span><span style="color:#3e999f;">* </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">height</span><span>() +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Size </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.width +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.height +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Using default impl of `size()` +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> upload </span><span style="color:#3e999f;">=</span><span> Upload { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;notes.txt&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Upload: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, upload.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> photo </span><span style="color:#3e999f;">=</span><span> Photo { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;stock_crustacean.png&quot;</span><span>), +</span><span> width: </span><span style="color:#f5871f;">100</span><span>, +</span><span> height: </span><span style="color:#f5871f;">150</span><span>, +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Photo: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;Size: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">size</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/trait_default.rs">trait_default.rs</a>)</sub></p> +<h2 id="what-about-derive">What about <em>derive</em>?</h2> +<p>There is a trait-related thing we have used quite extensively and not explained yet, namely the <code>#[derive]</code> attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.</p> +<p>Derivable traits:</p> +<ul> +<li> +<p>Equality traits: <code>Eq</code>, <code>PartialEq</code> and comparison traits: <code>Ord</code> and <code>PartialOrd</code>. The <code>Partial-</code> versions exist because there are types which don't fulfill the reflexivity requirement of equality (<code>NaN != NaN</code>) or do not form a total order (<code> NaN &lt; 0.0 == false</code> and <code>NaN &gt;= 0.0 == false</code>).</p> +</li> +<li> +<p>Data duplication traits: <code>Clone</code> and <code>Copy</code></p> +</li> +<li> +<p><code>Hash</code> - allows using values of that type as keys in a hashmap</p> +</li> +<li> +<p><code>Default</code> - provides a zero-arg constructor function</p> +</li> +<li> +<p><code>Debug</code> - provides a formatting of the value which can be used in debugging context. It should <em>NOT</em> be implemented manually. In general, if it's possible to derive the <code>Debug</code>, there are no reasons against doing it.</p> +</li> +</ul> +<h3 id="when-is-it-possible-to-derive-a-trait">When is it possible to derive a trait?</h3> +<p>When all fields of a struct/variants of an enum implement that trait.</p> +<h3 id="should-all-traits-always-be-derived-if-it-is-possible">Should all traits always be derived if it is possible?</h3> +<p>No. Although it may be tempting to just slap <code>#[derive(Clone, Copy)]</code> everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.</p> +<h1 id="generics">Generics</h1> +<p>Suppose we want to find the largest element in a sequence and return it. Very much on purpose, we didn't specify what type these elements would be - ideally, we would love it to work on all types that have a defined notion of a <em>largest</em> element. However, to make things simpler for now, let's focus only on two primitive types: <code>i32</code> and <code>char</code>. Let's try to write the code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>]) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">char</span><span>]) -&gt; </span><span style="color:#8959a8;">char </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/non_generic.rs">non_generic.rs</a>)</sub></p> +<p>Perfect, it works! Now only twenty more types to go...</p> +<p>Fortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>Cleaner already - we merged possibly very many implementations into one. But, when we try to compile this:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0369]: binary operation `&gt;` cannot be applied to type `T` +</span><span> --&gt; src/main.rs:5:17 +</span><span> | +</span><span>5 | if item &gt; largest { +</span><span> | ---- ^ ------- T +</span><span> | | +</span><span> | T +</span><span> | +</span><span>help: consider restricting type parameter `T` +</span><span> | +</span><span>1 | fn largest&lt;T: std::cmp::PartialOrd&gt;(list: &amp;[T]) -&gt; T { +</span><span> | ++++++++++++++++++++++ +</span></code></pre> +<p>Since <code>T</code> can be of absolutely any type now, the compiler cannot be sure that operator <code>&gt;</code> is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>We call this a <em>trait bound</em>, a way to provide constraints on what kind of types we are talking about in a given context. This implementation almost works now. Let's look at the new error.</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0508]: cannot move out of type `[T]`, a non-copy slice +</span><span> --&gt; src/main.rs:2:23 +</span><span> | +</span><span>2 | let mut largest = list[0]; +</span><span> | ^^^^^^^ +</span><span> | | +</span><span> | cannot move out of here +</span><span> | move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider borrowing here: `&amp;list[0]` +</span><span> +</span><span>error[E0507]: cannot move out of a shared reference +</span><span> --&gt; src/main.rs:4:18 +</span><span> | +</span><span>4 | for &amp;item in list { +</span><span> | ----- ^^^^ +</span><span> | || +</span><span> | |data moved here +</span><span> | |move occurs because `item` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider removing the `&amp;`: `item` +</span></code></pre> +<p>Our function attempts to take ownership, but, again, the compiler doesn't know whether <code>T</code> can just be trivially copied. Rust allows us to combine multiple trait bounds together:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/generic_largest.rs">generic_largest.rs</a>)</sub></p> +<h2 id="a-powerful-tool">A powerful tool</h2> +<p>There's a lot more that we can do with generics:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Debug; +</span><span> +</span><span style="color:#999999;">// generic enums +</span><span style="color:#8959a8;">enum </span><span>OurOption&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic structs +</span><span style="color:#8959a8;">struct </span><span>Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: U, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T, U&gt; Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T, </span><span style="color:#f5871f;">y</span><span>: U) -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> </span><span style="color:#8959a8;">Self </span><span>{ x, y } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Pair&lt;T&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: T, +</span><span>} +</span><span> +</span><span style="color:#999999;">// conditional implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt; Pair&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&gt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// alternative syntax +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Pair&lt;T&gt; +</span><span style="color:#8959a8;">where +</span><span> T: PartialOrd + Copy, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">smallest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&lt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Here information about the concrete underlying type is preserved. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">cloning_machine</span><span>&lt;T: </span><span style="color:#c99e00;">Clone </span><span style="color:#3e999f;">+</span><span> Debug&gt;(</span><span style="color:#f5871f;">item</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>T) -&gt; T { +</span><span> item.</span><span style="color:#4271ae;">clone</span><span>() +</span><span>} +</span><span> +</span><span style="color:#999999;">// Here information about the concrete underlying type is erased. +</span><span style="color:#999999;">// We can only either format or clone the result. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">erasing_cloning_machine1</span><span>(</span><span style="color:#f5871f;">item</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>(</span><span style="color:#f5871f;">impl Clone</span><span> + </span><span style="color:#f5871f;">Debug</span><span>)) -&gt; impl Clone </span><span style="color:#3e999f;">+</span><span> Debug { +</span><span> item.</span><span style="color:#4271ae;">clone</span><span>() +</span><span>} +</span><span> +</span><span style="color:#999999;">// Ditto. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">erasing_cloning_machine2</span><span>&lt;T: </span><span style="color:#c99e00;">Clone </span><span style="color:#3e999f;">+</span><span> Debug&gt;(</span><span style="color:#f5871f;">item</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>T) -&gt; impl Clone </span><span style="color:#3e999f;">+</span><span> Debug { +</span><span> item.</span><span style="color:#4271ae;">clone</span><span>() +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> _opt </span><span style="color:#3e999f;">= </span><span>OurOption::Some(</span><span style="color:#f5871f;">10</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _p1 </span><span style="color:#3e999f;">=</span><span> Tuple2 { x: </span><span style="color:#f5871f;">5</span><span>, y: </span><span style="color:#f5871f;">10 </span><span>}; +</span><span> </span><span style="color:#8959a8;">let</span><span> _p2 </span><span style="color:#3e999f;">= </span><span>Tuple2::new(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2.5</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr2 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">cloning_machine</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>arr); +</span><span> </span><span style="color:#8959a8;">let</span><span> _x </span><span style="color:#3e999f;">=</span><span> arr2[</span><span style="color:#f5871f;">0</span><span>]; </span><span style="color:#999999;">// This compiles, because `cloning_machine` preserves the type. +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, arr2); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr3 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">erasing_cloning_machine1</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>arr); +</span><span> </span><span style="color:#999999;">// arr3[0]; // won&#39;t compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, arr3); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr4 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">erasing_cloning_machine2</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>arr); +</span><span> </span><span style="color:#999999;">// arr4[0]; // won&#39;t compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, arr4); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/generics.rs">generics.rs</a>)</sub></p> +<p>A bit more involved example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::fmt::{Display, Formatter}; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>DefaultishablyPrintable&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">defaultish_print</span><span>() +</span><span> </span><span style="color:#8959a8;">where +</span><span> T: Display + Default, +</span><span> { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, T::default()) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Foo; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Bar; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#718c00;">&quot;this is a bar&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Default </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">default</span><span>() -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> Bar </span><span style="color:#999999;">// well, we have no other choice +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;Bar&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#3e999f;">&lt;</span><span>Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;</span><span style="color:#3e999f;">&gt;</span><span>::defaultish_print(); +</span><span> &lt;Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;Bar&gt;&gt;::defaultish_print(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/generics_fun.rs">generics_fun.rs</a>)</sub></p> +<h2 id="static-vs-dynamic-dispatch">Static vs dynamic dispatch</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>Speak { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Dog; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Dog { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hau hau&quot; </span><span style="color:#999999;">// it&#39;s a Polish dog! +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Human; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Human { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hello world&quot; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// It works like templates in C++ +</span><span style="color:#999999;">// A different function will be generated for each T during compilation +</span><span style="color:#999999;">// This process is called &quot;monomorphization&quot; +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">static_dispatch</span><span>&lt;T: Speak&gt;(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>T) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#999999;">// Only one copy of that function will exist in the compiled binary +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>dyn Speak) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> dog </span><span style="color:#3e999f;">=</span><span> Dog; +</span><span> </span><span style="color:#8959a8;">let</span><span> human </span><span style="color:#3e999f;">=</span><span> Human; +</span><span> +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#999999;">// The observable behavior is identical +</span><span> </span><span style="color:#999999;">// Static dispatch in general is a bit faster, +</span><span> </span><span style="color:#999999;">// because there is no need to perform a &quot;vtable lookup&quot;. +</span><span> </span><span style="color:#999999;">// But it can also result in bigger binary sizes. +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/static_dynamic_dispatch.rs">static_dynamic_dispatch.rs</a>)</sub></p> +<h1 id="lifetimes">Lifetimes</h1> +<p>Going back to the lesson about ownership, if we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); +</span><span>} +</span></code></pre> +<p>we should expect to get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `x` does not live long enough +</span><span> --&gt; src/main.rs:7:17 +</span><span> | +</span><span>7 | r = &amp;x; +</span><span> | ^^ borrowed value does not live long enough +</span><span>8 | } +</span><span> | - `x` dropped here while still borrowed +</span><span>9 | +</span><span>10 | println!(&quot;r: {}&quot;, r); +</span><span> | - borrow later used here +</span></code></pre> +<p>Courtesy of the borrow checker, we didn't end up with a dangling reference. But what exactly is happening behind the scenes? Rust introduces a concept of annotated lifetimes, where the lifetime of each value is being marked and tracked by the checker. Let's look at some examples:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; </span><span style="color:#999999;">// ---------+-- &#39;a +</span><span> </span><span style="color:#999999;">// | +</span><span> { </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// -+-- &#39;b | +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// | | +</span><span> } </span><span style="color:#999999;">// -+ | +</span><span> </span><span style="color:#999999;">// | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | +</span><span>} </span><span style="color:#999999;">// ---------+ +</span></code></pre> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// ----------+-- &#39;b +</span><span> </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// --+-- &#39;a | +</span><span> </span><span style="color:#999999;">// | | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | | +</span><span> </span><span style="color:#999999;">// --+ | +</span><span>} </span><span style="color:#999999;">// ----------+ +</span></code></pre> +<h2 id="annotations">Annotations</h2> +<p>Let's consider the following code finding the longer out of two strings:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;abcd&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;xyz&quot;</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span></code></pre> +<p>If we try to compile this, we will get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:9:33 +</span><span> | +</span><span>9 | fn longest(x: &amp;str, y: &amp;str) -&gt; &amp;str { +</span><span> | ---- ---- ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` +</span><span>help: consider introducing a named lifetime parameter +</span><span> | +</span><span>9 | fn longest&lt;&#39;a&gt;(x: &amp;&#39;a str, y: &amp;&#39;a str) -&gt; &amp;&#39;a str { +</span><span> | ++++ ++ ++ ++ +</span></code></pre> +<p>This is because Rust doesn't know which of the two provided strings (<code>x</code> or <code>y</code>) will be returned from the function. And because they potentially have different lifetimes, the lifetime of what we are returning remains unclear to the compiler - it needs our help.</p> +<p>Rust provides syntax for specifying lifetimes. The lifetime parameter name from the example (<code>a</code>) doesn't have any concrete meaning - it's just an arbitrary name for this one lifetime.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#999999;">// a reference +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a i32 </span><span style="color:#999999;">// a reference with an explicit lifetime +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a mut i32 </span><span style="color:#999999;">// a mutable reference with an explicit lifetime +</span></code></pre> +<p>So, knowing this, let's address the compiler's demands.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span></code></pre> +<p>When working with lifetimes, our work will usually revolve around specifying relationships between lifetimes of different values so that the compiler can successfully reason about the program's safety. In the context of the example above, this signature means that both of the function's arguments and its output will live at least as long as lifetime <code>'a</code>. In practice, this means that the output's lifetime will be equal to the smaller of the two inputs' lifetimes.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">first</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">second</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> first.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> second.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> first +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> second +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;long string is long&quot;</span><span>); +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;xyz&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2.</span><span style="color:#4271ae;">as_str</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// This doesn&#39;t compile - incorrect lifetimes +</span><span> </span><span style="color:#999999;">// +</span><span> </span><span style="color:#999999;">// let string1 = String::from(&quot;long string is long&quot;); +</span><span> </span><span style="color:#999999;">// let result; +</span><span> </span><span style="color:#999999;">// { +</span><span> </span><span style="color:#999999;">// let string2 = String::from(&quot;xyz&quot;); +</span><span> </span><span style="color:#999999;">// result = longest(string1.as_str(), string2.as_str()); +</span><span> </span><span style="color:#999999;">// } +</span><span> </span><span style="color:#999999;">// println!(&quot;The longest string is {}&quot;, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/lifetimes_basic.rs">lifetimes_basic.rs</a>)</sub></p> +<p>Trying to compile the second variant displeases the compiler (just like we hoped).</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `string2` does not live long enough +</span><span> --&gt; src/main.rs:6:44 +</span><span> | +</span><span>6 | result = longest(string1.as_str(), string2.as_str()); +</span><span> | ^^^^^^^^^^^^^^^^ borrowed value does not live long enough +</span><span>7 | } +</span><span> | - `string2` dropped here while still borrowed +</span><span>8 | println!(&quot;The longest string is {}&quot;, result); +</span><span> | ------ borrow later used here +</span></code></pre> +<h2 id="lifetime-elision">Lifetime elision</h2> +<p>We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this <em>lifetime elision</em>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span><span> </span><span style="color:#8959a8;">if</span><span> seq.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> seq +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">2</span><span>] +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> seq </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>]; +</span><span> +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;First two elements of the sequence: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, +</span><span> </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span>]) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/lifetimes_elision.rs">lifetimes_elision.rs</a>)</sub></p> +<p>The above works, even though we didn't specify any lifetime parameters at all. The reason lies in the rules we mentioned, which are as follows (where input lifetimes are lifetimes on parameters and output lifetimes are lifetimes on return values):</p> +<ul> +<li> +<p>Each parameter that is a reference gets its own lifetime parameter.</p> +</li> +<li> +<p>If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.</p> +</li> +<li> +<p>If there are multiple input lifetime parameters, but one of them is <code>&amp;self</code> or <code>&amp;mut self</code>, the lifetime of <code>self</code> is assigned to all output lifetime parameters.</p> +</li> +</ul> +<p>Let's try to understand how the compiler inferred the lifetimes of our <code>first_two</code> functions. We start with the following signature:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Then, we apply the first rule:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Next, we check the second rule. It applies here as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>With that, we arrive at a state where all lifetimes are specified.</p> +<h2 id="static-lifetime">Static lifetime</h2> +<p>There exists one special lifetime called <code>'static</code>, which means that a reference can live for the entire duration of the program. All string literals are annotated with this lifetime as they are stored directly in the program's binary. Full type annotation of a string literal in Rust is therefore as follows:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;I have a static lifetime.&quot;</span><span>; +</span></code></pre> +<h1 id="trait-lifetimes-a-challenging-tandem">Trait + lifetimes - a challenging tandem</h1> +<p>Let's go back to our <code>basic_trait.rs</code> example. The <code>Summary</code> trait was really wasteful: it always allocated the <code>String</code>s on heap, even though we only needed to display the formatted string, and we could do that without allocations. How? By using <code>Display</code> trait, of course.</p> +<p>The simplest possible optimisation would be like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Display; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> write!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location +</span><span> ) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> write!(f, </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.username, </span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/basic_trait_display.rs">basic_trait_display.rs</a>)</sub></p> +<p>This eliminates the heap allocations, but there's another catch. What if <code>NewsArticle</code> already had another (non-summarizing) <code>Display</code> implementation? We would end up in a double-trait-implementation conflict, which is a compile-time error.</p> +<p>We can solve the one-type-one-trait-impl problem by introducing another type just for summarizing. The first attempt could be to use generics in traits:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Display; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary&lt;&#39;a, Summarizer: Display&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; Summarizer; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> writeln!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location +</span><span> )</span><span style="color:#3e999f;">?</span><span>; +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> NewsArticle); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticleSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> article </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> article.headline, article.author, article.location +</span><span> ) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Summary&lt;</span><span style="color:#8959a8;">&#39;a</span><span>, NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;&gt; </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> NewsArticleSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> Tweet); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>TweetSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!(f, </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.username, tweet.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Summary&lt;</span><span style="color:#8959a8;">&#39;a</span><span>, TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;&gt; </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> TweetSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Summary&lt;</span><span style="color:#8959a8;">&#39;a</span><span>, NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;&gt; </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> unimplemented!(</span><span style="color:#718c00;">&quot;This is only to make code type-check and compile.&quot;</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> empty_article </span><span style="color:#3e999f;">=</span><span> NewsArticle { +</span><span> headline: </span><span style="color:#718c00;">&quot;&quot;</span><span>.</span><span style="color:#4271ae;">into</span><span>(), +</span><span> location: </span><span style="color:#c99e00;">String</span><span>::new(), +</span><span> author: </span><span style="color:#c99e00;">String</span><span>::default(), +</span><span> content: </span><span style="color:#c99e00;">Default</span><span>::default(), +</span><span> }; +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new article: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, empty_article.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> </span><span style="color:#999999;">// Compile error: `type annotations needed; multiple `impl`s satisfying `Tweet: Summary&lt;&#39;_, _&gt;` found` +</span><span> </span><span style="color:#999999;">// println!(&quot;1 new tweet: {}&quot;, tweet.summarize()); +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, +</span><span> &lt;Tweet </span><span style="color:#3e999f;">as </span><span>Summary&lt;&#39;</span><span style="color:#3e999f;">_</span><span>, TweetSummarizer&gt;&gt;::summarize(</span><span style="color:#3e999f;">&amp;</span><span>tweet) +</span><span> ); +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, +</span><span> &lt;Tweet </span><span style="color:#3e999f;">as </span><span>Summary&lt;&#39;</span><span style="color:#3e999f;">_</span><span>, NewsArticleSummarizer&gt;&gt;::summarize(</span><span style="color:#3e999f;">&amp;</span><span>tweet) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/trait_generic_type.rs">trait_generic_type.rs</a>)</sub></p> +<p>The problem here is that nothing hinders us from implement the trait (with various type parameters) for the same type, which leads to awkward ambiguity when calling the trait's methods (see <code>main</code> fn).</p> +<p>The use of generic types in <code>Summary</code> trait makes it semantics like this:</p> +<blockquote> +<p>A type can be summarized with any type supporting it.</p> +</blockquote> +<p>When we want the trait to require exactly one possible generic implementation for a given type, we can leverage <em>associated types</em>. Example here:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Display; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary&lt;&#39;a&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Summarizer: Display; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Summarizer; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> writeln!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location +</span><span> )</span><span style="color:#3e999f;">?</span><span>; +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> NewsArticle); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticleSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> article </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> article.headline, article.author, article.location +</span><span> ) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Summary&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">type </span><span>Summarizer </span><span style="color:#3e999f;">= </span><span>NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Summarizer { +</span><span> NewsArticleSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> Tweet); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>TweetSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!(f, </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.username, tweet.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Summary&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">type </span><span>Summarizer </span><span style="color:#3e999f;">= </span><span>TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Summarizer { +</span><span> TweetSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/trait_associated_type.rs">trait_associated_type.rs</a>)</sub></p> +<p>The use of associated types in Summary trait makes it semantics like this:</p> +<blockquote> +<p>A type can be summarized with at most one specific type.</p> +</blockquote> +<p>Yet another approach (arguably, the cleanest one) would be to use the <code>impl trait</code> syntax in a trait (quite recently stabilized!). +Example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Display; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; impl Display; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> writeln!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location +</span><span> )</span><span style="color:#3e999f;">?</span><span>; +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticleSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> NewsArticle); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>NewsArticleSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> article </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!( +</span><span> f, +</span><span> </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, +</span><span> article.headline, article.author, article.location +</span><span> ) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; impl Display { +</span><span> NewsArticleSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; impl Display { +</span><span> </span><span style="color:#8959a8;">struct </span><span>TweetSummarizer&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> Tweet); +</span><span> +</span><span> </span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>TweetSummarizer&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>std::fmt::Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>; +</span><span> write!(f, </span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.username, tweet.content) +</span><span> } +</span><span> } +</span><span> +</span><span> TweetSummarizer(</span><span style="color:#c82829;">self</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/impl_trait.rs">impl_trait.rs</a>)</sub></p> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch10-00-generics.html">The Book, chapter 10</a></p> +</li> +<li> +<p><a href="https://oswalt.dev/2021/06/polymorphism-in-rust/">Polymorphism in Rust</a></p> +</li> +</ul> +<h2 id="assignment-3-graded">Assignment 3 (graded)</h2> +<p><a href="https://classroom.github.com/a/VTyPdlC2">Passage Pathing</a></p> +<p>Deadline: 30.10.2024 23:59</p> + + + + + Data Types + 2024-10-17T00:00:00+00:00 + 2024-10-17T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/03-data-types/ + + <h2 id="aggregating-data">Aggregating data</h2> +<p>Below is a compact overview of Rust's structs</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Copy, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Position(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>); </span><span style="color:#999999;">// tuple struct +</span><span> +</span><span style="color:#999999;">// Could Hero derive the Copy trait? +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Hero { +</span><span> </span><span style="color:#c82829;">name</span><span>: String, +</span><span> </span><span style="color:#c82829;">level</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">experience</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">position</span><span>: Position, +</span><span>} +</span><span> +</span><span style="color:#999999;">// we can add methods to structs using the &#39;impl&#39; keyword +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// static method (in Rust nomenclature: &quot;associated function&quot;) +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">name</span><span>: String) -&gt; Hero { +</span><span> Hero { +</span><span> name, +</span><span> level: </span><span style="color:#f5871f;">1</span><span>, +</span><span> experience: </span><span style="color:#f5871f;">0</span><span>, +</span><span> position: Position(</span><span style="color:#f5871f;">0</span><span>, </span><span style="color:#f5871f;">0</span><span>), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// multiple impl blocks are possible for one struct +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// instance method, first argument (self) is the calling instance +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">distance</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">pos</span><span>: Position) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#999999;">// shorthand to: `self: &amp;Self` +</span><span> </span><span style="color:#999999;">// field `i` of a tuple or a tuple struct can be accessed through &#39;tuple.i&#39; +</span><span> (pos.</span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">0</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() </span><span style="color:#3e999f;">+ </span><span>(pos.</span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">1</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// mutable borrow of self allows to change instance fields +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">level_up</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#999999;">// shorthand to: `self: &amp;mut Self` +</span><span> </span><span style="color:#c82829;">self</span><span>.experience </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#c82829;">self</span><span>.level </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// &#39;self&#39; is not borrowed here and will be moved into the method +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">die</span><span>(</span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#999999;">// shorthand to: `self: Self` +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Here lies </span><span style="color:#666969;">{}</span><span style="color:#718c00;">, a hero who reached level </span><span style="color:#666969;">{}</span><span style="color:#718c00;">. RIP.&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.name, </span><span style="color:#c82829;">self</span><span>.level +</span><span> ); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Calling associated functions requires scope (`::`) operator. +</span><span> </span><span style="color:#8959a8;">let mut</span><span> hero: Hero </span><span style="color:#3e999f;">= </span><span>Hero::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Ferris&quot;</span><span>)); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); </span><span style="color:#999999;">// &#39;self&#39; is always passed implicitly +</span><span> +</span><span> </span><span style="color:#999999;">// fields other than &#39;name&#39; will be the same as in &#39;hero&#39; +</span><span> </span><span style="color:#8959a8;">let</span><span> steve </span><span style="color:#3e999f;">=</span><span> Hero { +</span><span> name: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Steve The Normal Guy&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">..</span><span>hero +</span><span> }; +</span><span> +</span><span> assert_eq!(hero.level, steve.level); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> twin </span><span style="color:#3e999f;">=</span><span> hero.</span><span style="color:#4271ae;">clone</span><span>(); +</span><span> +</span><span> </span><span style="color:#999999;">// we can compare Hero objects because it derives the PartialEq trait +</span><span> assert_eq!(hero, twin); +</span><span> twin.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_ne!(hero, twin); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_eq!(hero, twin); +</span><span> +</span><span> </span><span style="color:#999999;">// we can print out a the struct&#39;s debug string with &#39;{:?}&#39; +</span><span> println!(</span><span style="color:#718c00;">&quot;print to stdout: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, hero); +</span><span> +</span><span> hero.</span><span style="color:#4271ae;">die</span><span>(); </span><span style="color:#999999;">// &#39;hero&#39; is not usable after this invocation, see the method&#39;s definiton +</span><span> +</span><span> </span><span style="color:#999999;">// the dbg! macro prints debug strings to stderr along with file and line number +</span><span> </span><span style="color:#999999;">// dbg! takes its arguments by value, so better borrow them not to have them +</span><span> </span><span style="color:#999999;">// moved into dbg! and consumed. +</span><span> dbg!(</span><span style="color:#718c00;">&quot;print to stderr: {}&quot;</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>twin); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> pos </span><span style="color:#3e999f;">=</span><span> Position(</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">0</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> dist </span><span style="color:#3e999f;">=</span><span> steve.</span><span style="color:#4271ae;">distance</span><span>(pos); </span><span style="color:#999999;">// no clone here as Position derives the Copy trait +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, pos); +</span><span> assert_eq!(dist, </span><span style="color:#f5871f;">42</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/data_types.rs">data_types.rs</a>)</sub></p> +<h2 id="enums">Enums</h2> +<p>It is often the case that we want to define a variable that can only take +a certain set of values and the values are known up front. In C you can use an <code>enum</code> for this.</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;stdio.h&gt; +</span><span> +</span><span style="color:#8959a8;">enum </span><span>shirt_size { +</span><span> small, +</span><span> medium, +</span><span> large, +</span><span> xlarge +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_size</span><span>(</span><span style="color:#8959a8;">enum</span><span> shirt_size </span><span style="color:#f5871f;">size</span><span>) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;my size is &quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">switch </span><span>(size) { +</span><span> </span><span style="color:#8959a8;">case</span><span> small: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;small&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> medium: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;medium&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> large: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;large&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> xlarge: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;xlarge&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">default</span><span>: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;unknown&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">=</span><span> medium; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/enums.c">enums.c</a>)</sub></p> +<p>However, in C enums are just integers. Nothing prevents us from writing</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">666</span><span>; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span></code></pre> +<p>C++ introduces enum classes which are type-safe. Legacy enums are also somewhat safer than in C (same code as above):</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>&lt;source&gt;:27:31: error: invalid conversion from &#39;int&#39; to &#39;shirt_size&#39; [-fpermissive] +</span><span> 27 | enum shirt_size my_size = 666; +</span><span> | ^~~ +</span><span> | | +</span><span> | int +</span></code></pre> +<p>Some programming languages (especially functional ones) allow programmers to define +enums which carry additional information. Such types are usually called <code>tagged unions</code> +or <code>algebraic data types</code>.</p> +<p>In C++ we can use <code>union</code> with an <code>enum</code> tag to define it:</p> +<pre data-lang="cpp" style="background-color:#ffffff;color:#4d4d4c;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;iostream&gt; +</span><span> +</span><span style="color:#999999;">// Taken from: https://en.cppreference.com/w/cpp/language/union +</span><span> +</span><span style="color:#999999;">// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +</span><span style="color:#999999;">// and three variant members (c, i, d) +</span><span style="color:#8959a8;">struct </span><span>S +</span><span>{ +</span><span> </span><span style="color:#8959a8;">enum</span><span>{</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#c99e00;">INT</span><span>, DOUBLE} tag; +</span><span> </span><span style="color:#8959a8;">union +</span><span> { +</span><span> </span><span style="color:#8959a8;">char</span><span> c; +</span><span> </span><span style="color:#8959a8;">int</span><span> i; +</span><span> </span><span style="color:#8959a8;">double</span><span> d; +</span><span> }; +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_s</span><span>(</span><span style="color:#8959a8;">const</span><span> S</span><span style="color:#3e999f;">&amp; </span><span style="color:#f5871f;">s</span><span>) +</span><span>{ +</span><span> </span><span style="color:#8959a8;">switch</span><span>(s.</span><span style="color:#c82829;">tag</span><span>) +</span><span> { +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">CHAR</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">c </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">INT</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::DOUBLE: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">d </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() +</span><span>{ +</span><span> S s </span><span style="color:#3e999f;">= </span><span>{S::</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>}; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span> s.</span><span style="color:#c82829;">tag </span><span style="color:#3e999f;">=</span><span> S::</span><span style="color:#c99e00;">INT</span><span>; +</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">123</span><span>; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/tagged_union.cpp">tagged_union.cpp</a>)</sub></p> +<p>C++17 introduced a new feature called <code>variant</code> which generalizes this concept. +You can read more about it <a href="https://en.cppreference.com/w/cpp/utility/variant">here</a>.</p> +<p>Java has a more or less analogous feature called <code>sealed classes</code> +since <a href="https://docs.oracle.com/en/java/javase/17/language/sealed-classes-and-interfaces.html.">version 17</a>.</p> +<h2 id="enums-in-rust">Enums in Rust</h2> +<p>Let's see how they are defined in Rust.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>NamedSize { +</span><span> Small, +</span><span> Medium, +</span><span> Large, +</span><span> </span><span style="color:#666969;">XL</span><span>, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>ShirtSize { +</span><span> Named(NamedSize), +</span><span> Numeric(</span><span style="color:#8959a8;">u32</span><span>), +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Isn&#39;t it strange that some clothes&#39; sizes are adjectives like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">,&quot;</span><span>, +</span><span> ShirtSize::Named(NamedSize::Small) +</span><span> ); +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;but sometimes they are numbers like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">?&quot;</span><span>, +</span><span> ShirtSize::Numeric(</span><span style="color:#f5871f;">42</span><span>) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/enums.rs">enums.rs</a>)</sub></p> +<p>In Rust, enums are a core feature of the language. +You may have heard that one of Rust's defining characteristics is +the absence of <a href="https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions">&quot;the billion dollar mistake&quot;</a>. +So what can we do to say that a value is missing if there is no <code>null</code>?</p> +<p>In Rust, we can use the <code>Option</code> type to represent the absence of a value.</p> +<p>Option is defined as:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">enum </span><span>Option&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span></code></pre> +<p>The <code>&lt;T&gt;</code> part is called the &quot;type parameter&quot; and it causes Option to be generic. +We won't go deeper into this for now.</p> +<p>The fact that variables which could be <code>null</code> in other languages have a different type in Rust is +the solution to the billion dollar mistake!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> not_null: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> not_null </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">43</span><span>; +</span><span> </span><span style="color:#999999;">// not_null = None; // this won&#39;t compile because it&#39;s a different type! +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">43</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// such construction is rare, but possible +</span><span> </span><span style="color:#8959a8;">let mut</span><span> double_nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// assert_ne!(double_nullable, Some(42)); // this won&#39;t even compile because it&#39;s a different type! +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// None and Some(None) are different! +</span><span> assert_ne!(double_nullable, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now recall that division by 0 *panics* +</span><span> </span><span style="color:#999999;">// A panic is an unrecoverable error +</span><span> </span><span style="color:#999999;">// It is not an exception! +</span><span> </span><span style="color:#999999;">// And in Rust there are no exceptions, so there are no try/catch blocks +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine that we want to divide one number by another +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> dividend </span><span style="color:#3e999f;">/</span><span> divisor +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We get the divisor from the user, so it can be 0 +</span><span> </span><span style="color:#999999;">// We want to handle this situation gracefully - we don&#39;t want to crash the program! +</span><span> </span><span style="color:#999999;">// We can do this by using the Option&lt;T&gt; type +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">safe_divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">if</span><span> divisor </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#c99e00;">None +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c99e00;">Some</span><span>(dividend </span><span style="color:#3e999f;">/</span><span> divisor) +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Fortunately, such a function is already included in the standard library +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> </span><span style="color:#999999;">// We need to specify the type explicitly +</span><span> </span><span style="color:#999999;">// because checked_div is implemented for all integer types +</span><span> </span><span style="color:#999999;">// and Rust won&#39;t know which type we want to use +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">21</span><span>)); +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine we search for a value in an array. +</span><span> </span><span style="color:#8959a8;">let</span><span> numbers </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>, </span><span style="color:#f5871f;">5</span><span>]; +</span><span> </span><span style="color:#8959a8;">let</span><span> three </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(three, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> </span><span style="color:#8959a8;">let</span><span> seven </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">7</span><span>); +</span><span> assert_eq!(seven, </span><span style="color:#c99e00;">None</span><span>); +</span><span> </span><span style="color:#999999;">// We won&#39;t delve deeper into the details of how iterators work for now, +</span><span> </span><span style="color:#999999;">// but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust +</span><span> +</span><span> </span><span style="color:#999999;">// Usually there are two kinds of methods: +</span><span> </span><span style="color:#999999;">// ones that will panic if the argument is incorrect, +</span><span> </span><span style="color:#999999;">// numbers[8]; // this will panic! +</span><span> </span><span style="color:#999999;">// and `checked` ones that return an Option +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// We can use `unwrap` to get the value out of an Option +</span><span> </span><span style="color:#999999;">// but we must be absolutely sure that the Option is Some, otherwise we&#39;ll get a panic +</span><span> </span><span style="color:#999999;">// numbers.get(8).unwrap(); // this will panic! +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>).</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">unwrap_or</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#f5871f;">0</span><span>); </span><span style="color:#999999;">// or we can provide a default value +</span><span> +</span><span> </span><span style="color:#999999;">// Usually instead of unwrapping we use pattern matching, we&#39;ll get to this in a minute +</span><span> </span><span style="color:#999999;">// but first let&#39;s see what else we can do with an option +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> </span><span style="color:#999999;">// We can use `map` to transform the value inside an Option +</span><span> </span><span style="color:#8959a8;">let</span><span> doubled </span><span style="color:#3e999f;">=</span><span> number.</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>); +</span><span> assert_eq!(doubled, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">84</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use flatten to reduce one level of nesting +</span><span> </span><span style="color:#8959a8;">let</span><span> nested </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> assert_eq!(nested.</span><span style="color:#4271ae;">flatten</span><span>(), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use `and_then` to chain multiple options +</span><span> </span><span style="color:#999999;">// This operation is called `flatmap` in some languages +</span><span> </span><span style="color:#8959a8;">let</span><span> chained </span><span style="color:#3e999f;">=</span><span> number +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>)) +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(chained, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// The last two things we&#39;ll cover here are `take` and `replace` +</span><span> </span><span style="color:#999999;">// They are important when dealing with non-Copy types +</span><span> </span><span style="color:#999999;">// `take` will return the value inside an Option and leave a None in its place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> option: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#999999;">// Again, we need to specify the type +</span><span> </span><span style="color:#999999;">// Even though we want to say that there is no value inside the Option, +</span><span> </span><span style="color:#999999;">// this absent value must have a concrete type! +</span><span> assert_eq!(option.</span><span style="color:#4271ae;">take</span><span>(), </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(option, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> y </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(y, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// `replace` can be used to swap the value inside an Option +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">None</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/option.rs">option.rs</a>)</sub></p> +<h2 id="pattern-matching">Pattern matching</h2> +<p>Pattern matching is a powerful feature of Rust and many functional languages, but it's slowly making +its way into imperative languages like Java and Python as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Pattern matching is basically a switch on steroids. +</span><span> </span><span style="color:#8959a8;">let</span><span> number </span><span style="color:#3e999f;">= </span><span>rand::random::&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;(); +</span><span> </span><span style="color:#8959a8;">match</span><span> number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7 </span><span>{ +</span><span> </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is *almost* divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is not divisible by 7&quot;</span><span>), +</span><span> } +</span><span> +</span><span> #[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span> </span><span style="color:#8959a8;">enum </span><span>Color { +</span><span> Pink, +</span><span> Brown, +</span><span> Lime, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> color </span><span style="color:#3e999f;">= </span><span>Color::Lime; +</span><span> </span><span style="color:#8959a8;">match</span><span> color { +</span><span> Color::Pink </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color!&quot;</span><span>), </span><span style="color:#999999;">// _ is a wildcard +</span><span> </span><span style="color:#999999;">// Rust will statically check that we covered all cases or included a default case. +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can also use pattern matching to match on multiple values. +</span><span> </span><span style="color:#8959a8;">match </span><span>(color, number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7</span><span>) { +</span><span> (Color::Pink, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color and number!&quot;</span><span>), +</span><span> (Color::Pink, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite number!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color or number!&quot;</span><span>), +</span><span> } +</span><span> </span><span style="color:#999999;">// (This is not special syntax, we&#39;re just pattern matching tuples.) +</span><span> +</span><span> </span><span style="color:#999999;">// But we can also *destructure* the value +</span><span> </span><span style="color:#8959a8;">struct </span><span>Human { +</span><span> </span><span style="color:#c82829;">age</span><span>: </span><span style="color:#8959a8;">u8</span><span>, +</span><span> </span><span style="color:#c82829;">favorite_color</span><span>: Color, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> john </span><span style="color:#3e999f;">=</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> }; +</span><span> +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#3e999f;">&amp;</span><span>john { +</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Okay, that&#39;s John!&quot;</span><span>), +</span><span> Human { +</span><span> favorite_color: Color::Pink, +</span><span> </span><span style="color:#3e999f;">.. +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not John, but still his favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Somebody else?&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Note two things: +</span><span> </span><span style="color:#999999;">// 1. Color is *not* Eq, so we can&#39;t use == to compare it, but pattern matching is fine. +</span><span> </span><span style="color:#999999;">// 2. We *borrowed* the value, so we can use it after the match. +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;John is </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> years old and still kicking!&quot;</span><span>, john.age); +</span><span> +</span><span> </span><span style="color:#999999;">// To save some time, we can use `if let` to match against only one thing +</span><span> </span><span style="color:#999999;">// We could also use `while let ... {}` in the same way +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;He&#39;s also a man of great taste&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can match ranges... +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old!&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use match and capture the value at the same time. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use guards to check for multiple conditions. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">12</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *odd* teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *even* man, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is normal&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Finally, let&#39;s look at some references now +</span><span> </span><span style="color:#8959a8;">let</span><span> reference: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= &amp;</span><span style="color:#f5871f;">4</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">match</span><span> reference { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>val </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Value under reference is: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, val), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// `ref` can be used to create a reference when destructuring +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// `john` is still valid, because we borrowed using `ref` +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;John still has his color - </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">!&quot;</span><span>, favorite_color); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> john </span><span style="color:#3e999f;">=</span><span> john; +</span><span> +</span><span> </span><span style="color:#999999;">// `ref mut` borrows mutably +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref mut</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// We use `*` to dereference +</span><span> </span><span style="color:#3e999f;">*</span><span>favorite_color </span><span style="color:#3e999f;">= </span><span>Color::Brown; +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Tastes do change with time and John likes </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;"> now.&quot;</span><span>, +</span><span> john.favorite_color +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/pattern_matching.rs">pattern_matching.rs</a>)</sub></p> +<h2 id="result">Result</h2> +<p>We said there are no exceptions in Rust and panics mean errors which cannot be caught. +So how do we handle situations which can fail? That's where the <code>Result</code> type comes in.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fs::File; +</span><span style="color:#8959a8;">use </span><span>std::io; +</span><span style="color:#8959a8;">use </span><span>std::io::Read; +</span><span> +</span><span style="color:#999999;">// Let&#39;s try reading from a file. +</span><span style="color:#999999;">// Obviously this can fail. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_try</span><span>() -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(</span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">match</span><span> file { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#8959a8;">mut</span><span> file) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#999999;">// We got a file! +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> </span><span style="color:#999999;">// Matching each result quickly become tedious... +</span><span> </span><span style="color:#8959a8;">match</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c99e00;">Err</span><span>(error), +</span><span> } +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#999999;">// This is needed in order to change the type from `io::Result&lt;File&gt;` to `io::Result&lt;()&gt;` +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// The &#39;?&#39; operator allows us to return early in case of an error +</span><span style="color:#999999;">// (it automatically converts the error type) +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">second_try</span><span>(</span><span style="color:#f5871f;">filename</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>) -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(filename)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> filenames </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/null&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/cpu&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/fuse&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;there_certainly_is_no_such_file&quot;</span><span>, +</span><span> ]; +</span><span> </span><span style="color:#8959a8;">for</span><span> filename </span><span style="color:#3e999f;">in</span><span> filenames { +</span><span> println!(</span><span style="color:#718c00;">&quot;Trying to read from &#39;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&#39;&quot;</span><span>, filename); +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#4271ae;">second_try</span><span>(filename) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, gibberish), +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Error: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, error), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/03-data-types/result.rs">result.rs</a>)</sub></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li>The Book, chapters <a href="https://doc.rust-lang.org/book/ch05-00-structs.html">5</a>, +<a href="https://doc.rust-lang.org/stable/book/ch06-00-enums.html">6</a>, +<a href="https://doc.rust-lang.org/stable/book/ch08-00-common-collections.html">8</a> +and <a href="https://doc.rust-lang.org/stable/book/ch09-00-error-handling.html">9</a></li> +<li><a href="https://doc.rust-lang.org/std/option/">Option docs</a></li> +<li><a href="https://doc.rust-lang.org/std/result/">Result docs</a></li> +</ul> +<h2 id="assignment-2-graded">Assignment 2 (graded)</h2> +<p><a href="https://classroom.github.com/a/gDraT0lo">Communications</a></p> +<p>Deadline: 23.10.2024 23:59</p> + + + + + Ownership Model + 2024-10-08T00:00:00+00:00 + 2024-10-08T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/02-ownership/ + + <h2 id="why-all-the-fuss">Why all the fuss?</h2> +<p>Even if you've never seen Rust code before, chances are you still heard the term <em>borrow checker</em> or something about Rust's ownership. Indeed, Rust's ownership model lies at the very core of its uniqueness. But to fully understand it and appreciate it, let's first take a look at how memory management is handled in most popular languages.</p> +<ul> +<li> +<p><strong>Garbage Collection</strong> - in many high-level programming languages, like Java, Haskell or Python, memory management is done fully by the language, relieving the programmer from this burden. This prevents memory leaks and memory related errors (like <em>use after free</em>), but does come at a cost - there is a runtime overhead, both memory and performance wise, caused by the constantly running garbage collection algorithms and the programmer usually has very little control over when the garbage collection takes place. Also, garbage collection does not prevent concurrency-related errors, such as data races, in any way.</p> +</li> +<li> +<p><strong>Mind your own memory</strong> - in low-level languages and specific ones like C++, performance comes first so we cannot really afford to run expansive bookkeeping and cleaning algorithms. Most of these languages compile directly to machine code and have no language-specific runtime environment. That means that the only place where memory management can happen is in the produced code. While compilers insert these construction and destruction calls for stack allocated memory, it generally requires a lot of discipline from the programmer to adhere to good practices and patterns to avoid as many memory related issues as possible and one such bug can be quite deadly to the program and a nightmare to find and fix. These languages basically live by the <em>&quot;your memory, your problem&quot;</em> mantra.</p> +</li> +</ul> +<p>And then we have Rust. Rust is a systems programming language and in many ways it's akin to C++ - it's basically low-level with many high-level additions. But unlike C++, it doesn't exactly fall into either of the categories described above, though it's way closer to the second one. It performs no additional management at runtime, but instead imposes a set of rules on the code, making it easier to reason about and thus check for its safety and correctness at compile time - these rules make up Rust's <strong>ownership model</strong>.</p> +<p>In a way, programming in Rust is like pair-programming with a patient and very experienced partner. Rust's compiler will make sure you follow all the good patterns and practices (by having them ingrained in the language itself) and very often even tell you how to fix the issues it finds.</p> +<p><em><strong>Disclaimer:</strong> when delving deeper into Rust below we will make heavy use of concepts like scopes, moving data, stack and heap, which should have been introduced as part of the C++ course. If you need a refresher of any of these, it's best to do so now, before reading further.</em></p> +<h2 id="start-with-the-basics-ownership">Start with the basics - ownership</h2> +<p>In the paragraph above we mentioned a set of rules that comprise Rust's ownership model. <a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules">The book</a> starts off with the following three as its very foundation:</p> +<ol> +<li> +<p>Each value in Rust is tied to a specific variable - we call that variable its <strong>owner</strong>.</p> +</li> +<li> +<p>There can only be one owner at a time.</p> +</li> +<li> +<p>When the owner goes out of scope, the value will be destroyed (or in Rust terms - <em>dropped</em>).</p> +</li> +</ol> +<p>The third point might make you think about C++ and its automatic storage duration. We will later see that, while very similar at first, Rust expands on these mechanics quite a bit. The following code illustrates the basic version of this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> a: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// allocation on the stack, &#39;a&#39; becomes an owner +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;a&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;a&#39;, the owner, goes out of scope and the value is dropped +</span></code></pre> +<p>So far, so good. Variables are pushed onto the stack when they enter the scope and destroyed during stack unwinding that happens upon leaving their scope. However, allocating and deallocating simple integers doesn't impress anybody. Let's try something more complex:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is allocated on the stack, while its contents (&quot;a string&quot;) +</span><span> </span><span style="color:#999999;">// are allocated on the heap. &#39;s&#39; is the owner of this String object. +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;s&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;s&#39;, the owner, goes out of scope and the String is dropped, its heap allocated memory freed +</span></code></pre> +<p>If you recall the RAII (Resource Acquisition Is Initialization) pattern from C++, the above is basically the same thing. We go two for two now in the similarity department, so... is Rust really any different then? There is a part of these examples that we skipped over - actually doing something with the values.</p> +<h2 id="moving-around-is-fun">Moving around is fun</h2> +<p>Let's expand on the last example. The scoping is not really important for that one, so we don't include it here.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// same thing, &#39;s&#39; is now an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s; </span><span style="color:#999999;">// easy, &#39;s2&#39; becomes another owner... right? +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// this doesn&#39;t work, can you guess why? +</span></code></pre> +<p>At first glance everything looks great. If we write this code (well, an equivalent of it) in basically any other popular language, it will compile no issue - but it does not here and there's a good reason why.</p> +<p>To understand what's happening, we have to consult the rules again, rule 2 in particular. It says that there can only be one owner of any value at a given time. So, <code>s</code> and <code>s2</code> cannot own the same object. Okay, makes sense, but what is happening in this line then - <code>let s2 = s;</code>? Experience probably tells you that <code>s</code> just gets copied into <code>s2</code>, creating a new String object. That would result in each variable owning its very own instance of the string and each instance having exactly one owner. Sounds like everyone should be happy now, but wait - in that case the last line should work no issue, right? But it doesn't, so can't be a copy. Let's see now what the compiler actually has to say:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0382]: borrow of moved value: `s` +</span><span> --&gt; src/main.rs:6:42 +</span><span> | +</span><span>2 | let s = String::from(&quot;a string&quot;); +</span><span> | - move occurs because `s` has type `String`, which does not implement the `Copy` trait +</span><span>3 | +</span><span>4 | let s2 = s; +</span><span> | - value moved here +</span><span>5 | +</span><span>6 | println!(&quot;And the contents are: {}&quot;, s); +</span><span> | ^ value borrowed here after move +</span></code></pre> +<p><em>&quot;value moved here&quot;</em> - gotcha! So <code>s</code> is being moved to <code>s2</code>, which also means that <code>s2</code> now becomes the new owner of the string being moved and <code>s</code> cannot be used anymore. In Rust, the default method of passing values around is by move, not by copy. While it may sound a bit odd at first, it actually has some very interesting implications. But before we get to them, let's fix our code, so it compiles now. To do so, we have to explicitly tell Rust to make a copy by invoking the <code>clone</code> method:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">clone</span><span>(); </span><span style="color:#999999;">// &#39;s2&#39; now contains its own copy +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// success! +</span></code></pre> +<p>The compiler is happy now and so are we. The implicit move takes some getting used to, but the compiler is here to help us. Now, let's put the good, old C++ on the table again and compare the two lines:</p> +<div style="text-align: center"> +<p><code>let s2 = s;</code> is equivalent to <code>auto s2 = std::move(s);</code></p> +<p><code>let s2 = s.clone()</code> is equivalent to <code>auto s2 = s</code></p> +</div> +<p>There are a few important things to note here:</p> +<ul> +<li> +<p>Making a copy is oftentimes not cheap. Memory needs to be allocated and copied, and a call to the system has to be made. We should prefer to move things as much as possible to avoid this cost - in C++ we have a myriad of language features like <code>std::move</code> and <em>r-references</em> to achieve this. Every programmer worth their salt needs to be well versed in all of them to write efficient C++ code and simply forgetting one move can lead to significant performance loss (and this happens to even the most senior devs ever existing, let's not pretend). On the contrary, in Rust you need to make an effort to make a copy and that makes you very aware of the cost you're paying - something that we'll see quite a lot of in the language. Also, if you forget a clone there's no harm done - it just won't compile!</p> +</li> +<li> +<p>Hidden in all of this is another nice thing Rust gives us. In C++, nothing prevents you from using variables after they've been moved from, leading to unexpected errors in a more complex code. In Rust, that variable (in our case <code>s</code>) simply becomes invalid and the compiler gives us a nice error about it.</p> +</li> +</ul> +<h3 id="but-what-about-ints">But what about ints?</h3> +<p>A good question to ask. Copying primitives is cheap. And it's not convenient for the programmer to have to always write <code>.clone()</code> after every primitive. If we take a look at the error from the previous example:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>move occurs because `s` has type `String`, which does not implement the `Copy` trait` +</span></code></pre> +<p>It says that <code>s</code> was moved because the <code>String</code> type doesn't have the <code>Copy</code> trait. We will talk about traits more in depth in the future lessons, but what this basically means is that <code>String</code> is not specified to be copied by default. All primitive types (<code>i32</code>, <code>bool</code>, <code>f64</code>, <code>char</code>, etc.) and tuples consisting only of primitive types implement the <code>Copy</code> trait.</p> +<h3 id="exercise">Exercise</h3> +<p>How to fix that code?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); +</span><span>} +</span></code></pre> +<h2 id="let-s-borrow-some-books">Let's borrow some books</h2> +<p>We now know how to move things around and how to clone them if moving is not possible. But what if making a copy is unnecessary - maybe we just want to let someone look at our resource and keep on holding onto it once they're done. Consider the following example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(book.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Cloning is pretty excessive here. Imagine recommending a book to your friend and instead of lending it to them for the weekend, you scan it and print an exact copy. Not the best way to go about it, is it? Thankfully, Rust allows us to access a resource without becoming an owner through the use of references and the <code>&amp;</code> operator. This is called a borrow.</p> +<p>The adjusted code should look like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>book); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>As with everything, references are too, by default, immutable, which means that the <code>read_book</code> function is not able to modify that book passed into it. We can also borrow something mutably by specifying it both in the receiving function signature and the place it gets called. Maybe you want to have your book signed by its author?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">push_str</span><span>(</span><span style="color:#718c00;">&quot; ~ Arthur Author&quot;</span><span>); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// note that the book has to be marked as mutable in the first place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// it&#39;s always clear when a parameter might get modified +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); </span><span style="color:#999999;">// book is now signed +</span><span>} +</span></code></pre> +<p>Pretty neat, but doesn't seem that safe right now. Let's try to surprise our friend:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">clear</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>book; </span><span style="color:#999999;">// an immutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// a mutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(r); </span><span style="color:#999999;">// would be pretty sad to open a blank book when it was not +</span><span> </span><span style="color:#999999;">// what we borrowed initially +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Fortunately for us (and our poor friend just wanting to read), the compiler steps in and doesn't let us do that, printing the following message:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0502]: cannot borrow `book` as mutable because it is also borrowed as immutable +</span><span> --&gt; src/main.rs:14:14 +</span><span> | +</span><span>12 | let r = &amp;book; // an immutable borrow +</span><span> | ----- immutable borrow occurs here +</span><span>13 | +</span><span>14 | erase_book(&amp;mut book); // a mutable borrow +</span><span> | ^^^^^^^^^ mutable borrow occurs here +</span><span>15 | +</span><span>16 | read_book(r); // would be pretty sad to open a blank book when it was not +</span><span> | - immutable borrow later used here +</span></code></pre> +<p>This is where the famous borrow checker comes in. To keep things super safe, Rust clearly states what can and cannot be done with references and tracks their lifetimes. Exactly one of the following is always true for references to a given resource:</p> +<ul> +<li> +<p>There exists only one mutable reference and no immutable references, <strong>or</strong></p> +</li> +<li> +<p>There is any number of immutable references and no mutable ones.</p> +</li> +</ul> +<p>You may notice a parallel to the <em>readers - writers</em> problem from concurrent programming. In fact, the way Rust's borrow checker is designed lends itself incredibly well to preventing data race related issues.</p> +<h3 id="dangling-references">Dangling references</h3> +<p>Rust also checks for dangling references. If we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> reference_to_nothing </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">dangle</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dangle</span><span>() -&gt; </span><span style="color:#3e999f;">&amp;</span><span>String { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;hello&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>s +</span><span>} +</span></code></pre> +<p>we will get an adequate error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:5:16 +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;String { +</span><span> | ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but there is no value for it to be borrowed from +</span><span>help: consider using the `&#39;static` lifetime +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;&#39;static String { +</span><span> | ^^^^^^^^ +</span></code></pre> +<p>The message above suggests specifing a lifetime for the returned string. In Rust, the lifetime of each variable is also a part of its type, but we will talk more about it later.</p> +<h3 id="exercise-1">Exercise</h3> +<p>Our previous solution using <code>clone()</code> was pretty inefficient. How should this code look now?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); </span><span style="color:#999999;">// we could&#39;ve ommitted the clone() here. Why? +</span><span>} +</span></code></pre> +<h2 id="everyone-gets-a-slice">Everyone gets a slice</h2> +<p>The last part of working with references that we will cover in this lesson are slices. A <em>slice</em> in Rust is a view over continuous data. Let us start with a string slice - the <code>&amp;str</code> type.</p> +<p><em><strong>Note:</strong> for the purposes of these examples we assume we are working with ASCII strings. More comprehensive articles on handling strings are linked at the end of this lesson.</em></p> +<p>To create a string slice from the <code>String</code> object <code>s</code>, we can simply write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// creates a slice of length 2, starting with the character at index 1 +</span></code></pre> +<p>This makes use of the <code>&amp;</code> operator and Rust's range notation to specify the beginning and end of the slice. Thus, we can also write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">2</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// everything from index 2 till the end +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">1</span><span>]; </span><span style="color:#999999;">// only the first byte +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// the whole string as a slice +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">as_str</span><span>(); </span><span style="color:#999999;">// also the whole string +</span></code></pre> +<p>You might have noticed that we always built <code>String</code> values using the <code>from()</code> method and never actually used the string literals directly. What type is a string literal then? Turns out it's the new string slice we just learned about!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;string literal&quot;</span><span>; +</span></code></pre> +<p>In fact, it makes a lot sense - string literals, after all, are not allocated on the heap, but rather placed in a special section of the resulting binary. It's only natural we just reference that place with a slice.</p> +<p>Slices can also be taken from arrays:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> array: [</span><span style="color:#8959a8;">i32</span><span>; </span><span style="color:#f5871f;">4</span><span>] </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">10</span><span>, </span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#f5871f;">2</span><span>]; </span><span style="color:#999999;">// creates an array of four 32 bit integers +</span><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>] </span><span style="color:#3e999f;">= &amp;</span><span>array[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// results in a slice [10, 5] +</span></code></pre> +<h3 id="exercise-2">Exercise</h3> +<p>Can this code still be improved from the previous version utilizing references? Think about the signature of <code>count_animals</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span>} +</span></code></pre> +<h3 id="further-reading">Further reading</h3> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/std/primitive.char.html">Char documentation</a></p> +</li> +<li> +<p><a href="https://fasterthanli.me/articles/working-with-strings-in-rust">Working with strings in Rust</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html">The Book, chapter 4</a></p> +</li> +</ul> +<h3 id="assignment-1-graded">Assignment 1 (graded)</h3> +<p><a href="https://classroom.github.com/a/prGDl5Xa">ordering in Van Binh</a></p> +<p>Deadline: 16.10.2024 23:59</p> + + + + + Organizational lesson + 2024-10-06T00:00:00+00:00 + 2024-10-06T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/00-organizational/ + + <h1 id="rust-course">Rust course</h1> +<p>We will be using <a href="https://classroom.github.com">Github Classroom</a> for task submission and <a href="https://discord.gg/UyvepYkPs9">Discord</a> for discussions.</p> +<p>Our main learning/teaching resource will be <a href="https://doc.rust-lang.org/stable/book/">&quot;The Book&quot;</a>.</p> +<p>Also worth mentioning: <a href="https://doc.rust-lang.org/rust-by-example/index.html">&quot;Rust by Example&quot;</a>.</p> +<h2 id="grading">Grading</h2> +<ul> +<li>1/3 of the grade is based on small tasks. There will be approximately 1 task every two weeks and each task will be graded on a scale of 0 to 3.</li> +<li>2/3 of the grade is based on a big project. You can choose a topic yourself, but it must be accepted by me. The project has to be split into two parts. It can be done in groups of two (or bigger, if ambitious enough).</li> +<li>The grade may be increased by a bonus. You can get a bonus for: +<ul> +<li>Making a presentation about some advanced topic (const generics, futures, macros, etc.) or about architecture of a selected Rust open-source library</li> +<li>Contributing to a selected Rust open-source library</li> +<li>Contributing to this course's materials</li> +<li>Quizzes, homeworks, general activity etc.</li> +</ul> +</li> +</ul> +<h2 id="project-deadlines">Project Deadlines</h2> +<ol> +<li>2024-11-7: Project ideas should be presented to me for further refining. If you wish to pair up with someone, now is the time to tell me.</li> +<li>2024-11-14: Final project ideas should be accepted by now.</li> +<li>2024-12-12: Deadline for submitting the first part of the project.</li> +<li>2025-01-09: Deadline for submitting the second and final part of the project.</li> +</ol> + + + + + Introduction to Rust + 2024-09-20T00:00:00+00:00 + 2024-09-20T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/01-introduction/ + + <p><img src="https://www.rust-lang.org/logos/rust-logo-blk.svg" alt="Logo" /></p> +<h1 id="a-language-empowering-everyone-to-build-reliable-and-efficient-software">A language empowering everyone to build reliable and efficient software.</h1> +<p>(<a href="https://rustacean.net/">unofficial logo</a>)</p> +<h2 id="why-use-rust">Why use Rust?</h2> +<ul> +<li>It is <strong>safe</strong> (compared to C++ for example, as we will see in a minute)</li> +<li>It is <strong>fast</strong> (because it is compiled to machine code)</li> +<li>It is ergonomic and pleasant to use (static typing, expressive type system, helpful compiler +warnings)</li> +<li>It +is <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">loved by programmers</a></li> +<li>It provides excellent tooling</li> +</ul> +<h2 id="why-learn-rust">Why learn Rust?</h2> +<p>Even if you don't end up using Rust, learning it expands your horizons</p> +<ul> +<li>it helps especially with the awareness of what you can and can't do in concurrent applications</li> +<li>it helps you understand memory management and learn its good practices</li> +</ul> +<h2 id="why-not-to-learn-rust">Why not to learn Rust?</h2> +<ul> +<li>Some people say Rust is too hard to learn because of the borrow checker</li> +<li>Once you get to know Cargo you won't ever want to use a language without a built-in package +manager ;)</li> +<li>You will start hating C++</li> +</ul> +<h2 id="demos">Demos</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/cpp_meme.jpg" alt="Meme" /></p> +<p>Let's compare the same code written in <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/errors_demo.c">C</a>, <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/errors_demo.cpp">C++</a> +and <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/errors_demo.rs">Rust</a>.</p> +<h2 id="code-you-sent-in-previous-editions">Code you sent in previous editions!</h2> +<h3 id="aleksander-tudruj">Aleksander Tudruj</h3> +<pre data-lang="cpp" style="background-color:#ffffff;color:#4d4d4c;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;iostream&gt; +</span><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;unordered_map&gt; +</span><span> +</span><span style="color:#8959a8;">using </span><span>name </span><span style="color:#3e999f;">=</span><span> std::string; +</span><span style="color:#8959a8;">using </span><span>age </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">int</span><span>; +</span><span style="color:#8959a8;">using </span><span>person </span><span style="color:#3e999f;">=</span><span> std::pair&lt;name, age&gt;; +</span><span style="color:#8959a8;">using </span><span>address </span><span style="color:#3e999f;">=</span><span> std::string; +</span><span style="color:#8959a8;">using </span><span>address_book </span><span style="color:#3e999f;">=</span><span> std::unordered_map&lt;person, address&gt;; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_address_book</span><span>(</span><span style="color:#8959a8;">const</span><span> address_book </span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">book</span><span>) +</span><span>{ +</span><span> </span><span style="color:#8959a8;">for </span><span>(</span><span style="color:#8959a8;">const auto </span><span style="color:#3e999f;">&amp;</span><span>[person, address] </span><span style="color:#3e999f;">:</span><span> book) +</span><span> { +</span><span> std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> person.</span><span style="color:#c82829;">first </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&quot; is &quot; </span><span style="color:#3e999f;">&lt;&lt;</span><span> person.</span><span style="color:#c82829;">second </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&quot; years old and lives at &quot; </span><span style="color:#3e999f;">&lt;&lt;</span><span> address </span><span style="color:#3e999f;">&lt;&lt;</span><span> std::endl; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() +</span><span>{ +</span><span> +</span><span> address_book </span><span style="color:#c82829;">people</span><span style="color:#4271ae;">{}</span><span>; +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;John&quot;</span><span>, </span><span style="color:#f5871f;">20</span><span>}, </span><span style="color:#718c00;">&quot;221B Baker Street, London&quot;</span><span>}); +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;Mary&quot;</span><span>, </span><span style="color:#f5871f;">30</span><span>}, </span><span style="color:#718c00;">&quot;Avenue des Champs-Élysées, Paris&quot;</span><span>}); +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;Jack&quot;</span><span>, </span><span style="color:#f5871f;">73</span><span>}, </span><span style="color:#718c00;">&quot;Wall Street, New York&quot;</span><span>}); +</span><span> </span><span style="color:#c82829;">print_address_book</span><span style="color:#4271ae;">(people)</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#f5871f;">0</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/tudruj.cpp">tudruj.cpp</a>)</sub></p> +<h3 id="krystyna-gasinska">Krystyna Gasińska</h3> +<pre data-lang="python" style="background-color:#ffffff;color:#4d4d4c;" class="language-python "><code class="language-python" data-lang="python"><span style="color:#999999;"># sample 1 - different ways of removing elements from the list while iterating +</span><span>list1 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list1)</span><span>: +</span><span> </span><span style="color:#8959a8;">del </span><span>item +</span><span>list1 +</span><span> +</span><span style="color:#999999;"># [1, 2, 3, 4] +</span><span> +</span><span>list2 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list2)</span><span>: +</span><span> </span><span style="color:#4271ae;">list2.</span><span style="color:#c82829;">remove</span><span style="color:#4271ae;">(item) +</span><span>list2 +</span><span> +</span><span style="color:#999999;"># [2, 4] +</span><span> +</span><span>list3 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list3[:])</span><span>: +</span><span> </span><span style="color:#4271ae;">list3.</span><span style="color:#c82829;">remove</span><span style="color:#4271ae;">(item) +</span><span>list3 +</span><span> +</span><span style="color:#999999;"># [] +</span><span> +</span><span>list4 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list4)</span><span>: +</span><span> </span><span style="color:#4271ae;">list4.</span><span style="color:#c82829;">pop</span><span style="color:#4271ae;">(idx) +</span><span>list4 +</span><span> +</span><span style="color:#999999;"># [2, 4] +</span><span> +</span><span style="color:#999999;"># sample 2 - string interning +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc&quot; +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc&quot; +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&#39;&#39;</span><span style="color:#4271ae;">.</span><span style="color:#c82829;">join</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">&#39;a&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;b&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;c&#39;</span><span style="color:#4271ae;">]) +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&#39;&#39;</span><span style="color:#4271ae;">.</span><span style="color:#c82829;">join</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">&#39;a&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;b&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;c&#39;</span><span style="color:#4271ae;">]) +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc!&quot; +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc!&quot; +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#999999;"># sample 3 - chained operations +</span><span>(</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">False</span><span>) </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>] +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span>(</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>]) +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>] </span><span style="color:#999999;"># unexpected... +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span style="color:#999999;"># sample 4 - is operator +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">256 +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">256 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257 +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span>a, b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257</span><span>, </span><span style="color:#f5871f;">257 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span style="color:#f5871f;">257 </span><span style="color:#3e999f;">is </span><span style="color:#f5871f;">257 +</span><span> +</span><span style="color:#999999;"># &lt;&gt;:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># &lt;&gt;:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># 257 is 257 +</span><span> +</span><span style="color:#999999;"># sample 5 - local variables +</span><span style="color:#8959a8;">def </span><span style="color:#4271ae;">f</span><span>(</span><span style="color:#f5871f;">trufel</span><span>): +</span><span> </span><span style="color:#8959a8;">if </span><span>trufel: +</span><span> y </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">1 +</span><span> y </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1 +</span><span> +</span><span style="color:#c82829;">f</span><span style="color:#4271ae;">(</span><span style="color:#f5871f;">True</span><span style="color:#4271ae;">) </span><span style="color:#999999;"># everything is fine +</span><span> +</span><span style="color:#c82829;">f</span><span style="color:#4271ae;">(</span><span style="color:#f5871f;">False</span><span style="color:#4271ae;">) </span><span style="color:#999999;"># gives error: local variable &#39;y&#39; referenced before assignment +</span><span> +</span><span style="color:#999999;"># --------------------------------------------------------------------------- +</span><span style="color:#999999;"># UnboundLocalError Traceback (most recent call last) +</span><span style="color:#999999;"># Input In [17], in &lt;cell line: 1&gt;() +</span><span style="color:#999999;"># ----&gt; 1 f(False) +</span><span> +</span><span style="color:#999999;"># Input In [15], in f(trufel) +</span><span style="color:#999999;"># 3 if trufel: +</span><span style="color:#999999;"># 4 y = 1 +</span><span style="color:#999999;"># ----&gt; 5 y += 1 +</span><span> +</span><span style="color:#999999;"># UnboundLocalError: local variable &#39;y&#39; referenced before assignment +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/gasinska.py">gasinska.py</a>)</sub></p> +<h3 id="antoni-koszowski">Antoni Koszowski</h3> +<pre data-lang="go" style="background-color:#ffffff;color:#4d4d4c;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#999999;">// mutowalność jest wbudowana w język +</span><span> +</span><span style="color:#8959a8;">type </span><span>S </span><span style="color:#8959a8;">struct </span><span>{ +</span><span> </span><span style="color:#c82829;">A </span><span style="color:#c99e00;">string +</span><span> </span><span style="color:#c82829;">B </span><span>[]</span><span style="color:#c99e00;">string +</span><span>} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#c82829;">x </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">S</span><span>{</span><span style="color:#718c00;">&quot;x-A&quot;</span><span>, []</span><span style="color:#c99e00;">string</span><span>{</span><span style="color:#718c00;">&quot;x-B&quot;</span><span>}} +</span><span> </span><span style="color:#c82829;">y </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">x </span><span style="color:#999999;">// copy the struct +</span><span> </span><span style="color:#c82829;">y</span><span>.</span><span style="color:#c82829;">A </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;y-A&quot; +</span><span> </span><span style="color:#c82829;">y</span><span>.</span><span style="color:#c82829;">B</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;y-B&quot; +</span><span> +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Println</span><span>(</span><span style="color:#c82829;">x</span><span>, </span><span style="color:#c82829;">y</span><span>) +</span><span> </span><span style="color:#999999;">// Outputs &quot;{x-A [y-B]} {y-A [y-B]}&quot; -- x was modified! +</span><span>} +</span><span> +</span><span style="color:#999999;">// slices i kwestia append +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">doStuff</span><span>(</span><span style="color:#f5871f;">value </span><span>[]</span><span style="color:#c99e00;">string</span><span>) { +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>) +</span><span> +</span><span> </span><span style="color:#c82829;">value2 </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">value</span><span>[:] +</span><span> </span><span style="color:#c82829;">value2 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">append</span><span>(</span><span style="color:#c82829;">value2</span><span>, </span><span style="color:#718c00;">&quot;b&quot;</span><span>) +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#718c00;">, value2=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>, </span><span style="color:#c82829;">value2</span><span>) +</span><span> +</span><span> </span><span style="color:#c82829;">value2</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;z&quot; +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#718c00;">, value2=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>, </span><span style="color:#c82829;">value2</span><span>) +</span><span>} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#c82829;">slice1 </span><span style="color:#3e999f;">:= </span><span>[]</span><span style="color:#c99e00;">string</span><span>{</span><span style="color:#718c00;">&quot;a&quot;</span><span>} </span><span style="color:#999999;">// length 1, capacity 1 +</span><span> +</span><span> </span><span style="color:#c82829;">doStuff</span><span>(</span><span style="color:#c82829;">slice1</span><span>) +</span><span> </span><span style="color:#999999;">// Output: +</span><span> </span><span style="color:#999999;">// value=[a] -- ok +</span><span> </span><span style="color:#999999;">// value=[a], value2=[a b] -- ok: value unchanged, value2 updated +</span><span> </span><span style="color:#999999;">// value=[a], value2=[z b] -- ok: value unchanged, value2 updated +</span><span> +</span><span> </span><span style="color:#c82829;">slice10 </span><span style="color:#3e999f;">:= </span><span style="color:#4271ae;">make</span><span>([]</span><span style="color:#c99e00;">string</span><span>, </span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">10</span><span>) </span><span style="color:#999999;">// length 1, capacity 10 +</span><span> </span><span style="color:#c82829;">slice10</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;a&quot; +</span><span> +</span><span> </span><span style="color:#c82829;">doStuff</span><span>(</span><span style="color:#c82829;">slice10</span><span>) +</span><span> </span><span style="color:#999999;">// Output: +</span><span> </span><span style="color:#999999;">// value=[a] -- ok +</span><span> </span><span style="color:#999999;">// value=[a], value2=[a b] -- ok: value unchanged, value2 updated +</span><span> </span><span style="color:#999999;">// value=[z], value2=[z b] -- WTF?!? value changed??? +</span><span>} +</span><span> +</span><span style="color:#999999;">// error handling +</span><span> +</span><span style="color:#c82829;">len</span><span>, </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">reader</span><span>.</span><span style="color:#c82829;">Read</span><span>(</span><span style="color:#c82829;">bytes</span><span>) +</span><span style="color:#8959a8;">if </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">nil </span><span>{ +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">== </span><span style="color:#c82829;">io</span><span>.</span><span style="color:#c82829;">EOF </span><span>{ +</span><span> </span><span style="color:#999999;">// All good, end of file +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">err +</span><span> } +</span><span>} +</span><span> +</span><span> +</span><span style="color:#999999;">// interfejs nil +</span><span> +</span><span style="color:#8959a8;">type </span><span>Explodes </span><span style="color:#8959a8;">interface </span><span>{ +</span><span> </span><span style="color:#4271ae;">Bang</span><span>() +</span><span> </span><span style="color:#4271ae;">Boom</span><span>() +</span><span>} +</span><span> +</span><span style="color:#999999;">// Type Bomb implements Explodes +</span><span style="color:#8959a8;">type </span><span>Bomb </span><span style="color:#8959a8;">struct </span><span>{} +</span><span style="color:#8959a8;">func </span><span>(</span><span style="color:#3e999f;">*</span><span style="color:#8959a8;">Bomb</span><span>) </span><span style="color:#4271ae;">Bang</span><span>() {} +</span><span style="color:#8959a8;">func </span><span>(</span><span style="color:#8959a8;">Bomb</span><span>) </span><span style="color:#4271ae;">Boom</span><span>() {} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">var </span><span style="color:#c82829;">bomb </span><span style="color:#3e999f;">*</span><span style="color:#8959a8;">Bomb </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">nil +</span><span> </span><span style="color:#8959a8;">var </span><span style="color:#c82829;">explodes </span><span style="color:#8959a8;">Explodes </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">bomb +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#c82829;">bomb</span><span>, </span><span style="color:#c82829;">explodes</span><span>) </span><span style="color:#999999;">// &#39;0x0 (0x10a7060,0x0)&#39; +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">explodes </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">nil </span><span>{ +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#718c00;">&quot;Not nil!&quot;</span><span>) </span><span style="color:#999999;">// &#39;Not nil!&#39; What are we doing here?!?! +</span><span> </span><span style="color:#c82829;">explodes</span><span>.</span><span style="color:#c82829;">Bang</span><span>() </span><span style="color:#999999;">// works fine +</span><span> </span><span style="color:#c82829;">explodes</span><span>.</span><span style="color:#c82829;">Boom</span><span>() </span><span style="color:#999999;">// panic: value method main.Bomb.Boom called using nil *Bomb pointer +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#718c00;">&quot;nil!&quot;</span><span>) </span><span style="color:#999999;">// why don&#39;t we end up here? +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{} +</span><span style="color:#999999;">// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany. +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/koszowski.go">koszowski.go</a>)</sub></p> +<h3 id="mieszko-grodzicki">Mieszko Grodzicki</h3> +<pre data-lang="python" style="background-color:#ffffff;color:#4d4d4c;" class="language-python "><code class="language-python" data-lang="python"><span style="color:#8959a8;">def </span><span style="color:#4271ae;">add_contents</span><span>(</span><span style="color:#f5871f;">input_list</span><span>, </span><span style="color:#f5871f;">contents</span><span style="color:#3e999f;">=</span><span>[]): +</span><span> </span><span style="color:#8959a8;">for </span><span>val </span><span style="color:#8959a8;">in </span><span>input_list: +</span><span> </span><span style="color:#4271ae;">contents.</span><span style="color:#c82829;">append</span><span style="color:#4271ae;">(val) +</span><span> </span><span style="color:#8959a8;">return </span><span>contents +</span><span> +</span><span style="color:#4271ae;">print(</span><span style="color:#c82829;">add_contents</span><span style="color:#4271ae;">([</span><span style="color:#f5871f;">1</span><span style="color:#4271ae;">])) </span><span style="color:#999999;"># [1] +</span><span style="color:#4271ae;">print(</span><span style="color:#c82829;">add_contents</span><span style="color:#4271ae;">([</span><span style="color:#f5871f;">2</span><span style="color:#4271ae;">])) </span><span style="color:#999999;"># [1, 2] +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/grodzicki.py">grodzicki.py</a>)</sub></p> +<h2 id="installing-rust">Installing Rust</h2> +<ul> +<li><a href="https://rustup.rs/">Rustup</a></li> +<li>Setup an IDE +<ul> +<li><a href="https://www.jetbrains.com/clion/">CLion</a> (you can get +it <a href="https://www.jetbrains.com/community/education/">for free</a>) +and <a href="https://intellij-rust.github.io/">RustRover</a></li> +<li><a href="https://code.visualstudio.com/">VSCode</a> +and <a href="https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer">rust-analyzer</a></li> +<li>rust-analyzer also works +with <a href="https://rust-analyzer.github.io/manual.html#installation">other IDEs</a></li> +</ul> +</li> +</ul> +<h2 id="useful-tools">Useful tools</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/clippy.jpg" alt="Clippy" /></p> +<ul> +<li><code>cargo clippy</code> (for static analysis)</li> +<li>there's also <code>cargo check</code>, but it's less powerful than clippy</li> +<li><code>cargo fmt</code> (for code formatting)</li> +</ul> +<h3 id="rust-playground">Rust Playground</h3> +<ul> +<li><a href="https://play.rust-lang.org/">online Rust compiler</a></li> +</ul> +<h2 id="hello-world">Hello world</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;World&quot;</span><span>; +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); </span><span style="color:#999999;">// using the println! macro +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/hello_world.rs">hello_world.rs</a>)</sub></p> +<h3 id="variables">Variables</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// inferred type +</span><span> </span><span style="color:#8959a8;">let</span><span> y: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; </span><span style="color:#999999;">// specified type +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40 </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">2</span><span>; </span><span style="color:#999999;">// shadowing +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x); </span><span style="color:#999999;">// prints 42 +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// x = 0; // compilation error, variables are by default immutable +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// declare as mutable +</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#999999;">// now we can reassign +</span><span> +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; </span><span style="color:#999999;">// x = x + 1 +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/variables.rs">variables.rs</a>)</sub></p> +<h3 id="conditionals">Conditionals</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">42 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 42&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">43 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 43&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is not 42 or 43&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can also use ifs as expressions +</span><span> </span><span style="color:#8959a8;">let</span><span> a_or_b </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;a&quot; </span><span style="color:#999999;">// notice no semicolon at the end +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;b&quot; +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/conditionals.rs">conditionals.rs</a>)</sub></p> +<h3 id="loops">Loops</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;i is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, i); </span><span style="color:#999999;">// i in [0, 10) +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">while</span><span> x </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">50 </span><span>{ +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> y </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> iterations </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> iterations </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> iterations </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#8959a8;">continue</span><span>; +</span><span> } +</span><span> y </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> y </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can use labels to refer to a specific loop +</span><span> </span><span style="color:#8959a8;">let mut</span><span> count </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> &#39;counting_up: </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> remaining </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> remaining </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">9 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> </span><span style="color:#8959a8;">if</span><span> count </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break &#39;counting_up</span><span>; </span><span style="color:#999999;">// ends the outer loop +</span><span> } +</span><span> remaining </span><span style="color:#3e999f;">-= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> count </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use break with a value. +</span><span> </span><span style="color:#999999;">// Because loops are expressions too, +</span><span> </span><span style="color:#999999;">// the value we break with will be returned from the functions +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> value </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> counter </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break </span><span style="color:#f5871f;">32</span><span>; +</span><span> } +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/loops.rs">loops.rs</a>)</sub></p> +<h3 id="functions">Functions</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">get_5</span><span>() -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#f5871f;">5 </span><span style="color:#999999;">// we could also write &quot;return 5;&quot; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">print_sum</span><span>(</span><span style="color:#f5871f;">a</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">b</span><span>: </span><span style="color:#8959a8;">u32</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;a + b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, a </span><span style="color:#3e999f;">+</span><span> b); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; +</span><span> </span><span style="color:#4271ae;">print_sum</span><span>(a, </span><span style="color:#4271ae;">get_5</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/01-introduction/functions.rs">functions.rs</a>)</sub></p> +<h2 id="test-assignment-not-graded">Test assignment (not graded)</h2> +<p>Click <a href="https://classroom.github.com/a/l3iF_TJU">here</a></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/book/">The Book, chapters 1-3</a></li> +</ul> +<h2 id="additional-reading">Additional reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/rust-by-example/">Rust By Example</a></li> +</ul> + + + + + Project feedback + 2022-12-29T00:00:00+00:00 + 2022-12-29T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/ + + <h1 id="project-feedback">Project feedback</h1> +<h2 id="unwrapping-options-results">Unwrapping options/results</h2> +<p>Always ask yourself twice if you really need to unwrap. In most cases, you don't have to. Use pattern matching instead, +as it provides a static guarantee that the value is present.</p> +<p>Pattern matching prevents you from writing code like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">some_function</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">is_some</span><span>() { +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x.</span><span style="color:#4271ae;">unwrap</span><span>()); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Let&#39;s say this line was added later and/or you forgot to put it in the if statement. +</span><span> </span><span style="color:#4271ae;">do_something</span><span>(x.</span><span style="color:#4271ae;">unwrap</span><span>()); </span><span style="color:#999999;">// this will blow up if x == None! +</span><span>} +</span></code></pre> +<p>Instead, you can write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">some_function</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(x) </span><span style="color:#3e999f;">=</span><span> x { +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x); +</span><span> </span><span style="color:#4271ae;">do_something</span><span>(x); +</span><span> } +</span><span>} +</span></code></pre> +<h2 id="question-mark-operator">Question mark operator</h2> +<p>In methods that return <code>Result</code> or <code>Option</code>, you can use the question mark operator to return early if the value is <code>None</code> or <code>Err</code>. +See: <a href="https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html">https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html</a></p> +<h2 id="logging">Logging</h2> +<p>You can use the <a href="https://crates.io/crates/log">log</a> crate to log messages. It's better than <code>println!</code> because it +can be easily turned off. It also allows you to use different severity levels (e.g. <code>info</code>, <code>warn</code>, <code>error</code>) and only +log messages above a certain level.</p> +<h2 id="string-vs-str">&amp;String vs &amp;str</h2> +<p>See <a href="https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters">https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters</a> +In general, if you want to pass a reference to a string, use <code>&amp;str</code> instead of <code>&amp;String</code>.</p> +<h2 id="use-current-versions-of-dependencies">Use current versions of dependencies</h2> +<p>You can use <a href="https://crates.io/crates/cargo-upgrades">cargo upgrades</a> to check for outdated dependencies.</p> +<h2 id="if-your-project-has-separate-binaries-use-multiple-binaries-or-a-workspace">If your project has separate binaries, use multiple binaries or a workspace</h2> +<p>You can have multiple binaries in a single cargo project. Simply place them in the <code>src/bin</code> directory. +You can run them with <code>cargo run --bin &lt;name&gt;</code>. Alternatively, you can setup a +<a href="https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html">workspace</a>.</p> +<h2 id="run-clippy-cargo-fmt">Run clippy &amp; cargo fmt</h2> +<p>This should have become a habit by now. You can disable clippy warnings for a single item with <code>#[allow(clippy::...)]</code>, +but in most cases you shouldn't do that.</p> +<h2 id="if-you-need-to-escape-characters-in-a-string-use-raw-strings">If you need to escape characters in a string, use raw strings</h2> +<p>See <a href="https://doc.rust-lang.org/reference/tokens.html#raw-string-literals">https://doc.rust-lang.org/reference/tokens.html#raw-string-literals</a></p> +<h2 id="how-to-handle-errors">How to handle errors?</h2> +<p>Short: <a href="https://kerkour.com/rust-error-handling">https://kerkour.com/rust-error-handling</a></p> +<p>Long: <a href="https://www.lpalmieri.com/posts/error-handling-rust/">https://www.lpalmieri.com/posts/error-handling-rust/</a></p> +<h2 id="don-t-pass-around-locked-mutex-s-contents">Don't pass around locked mutex's contents</h2> +<p>If you have a mutex, you can use <code>lock()</code> to get a guard that will unlock the mutex when it goes out of scope. +But don't pass the contents of the guard to functions that can block (unless the mutex <em>must</em> be locked for +the entire duration of the function). +Instead of:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Mutex; +</span><span style="color:#8959a8;">use </span><span>std::thread; +</span><span style="color:#8959a8;">use </span><span>std::time::Duration; +</span><span style="color:#8959a8;">use </span><span>std::time::Instant; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#f5871f;">counter</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut i32</span><span>) { +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> </span><span style="color:#3e999f;">*</span><span>counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= </span><span>Mutex::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> thread::scope(|</span><span style="color:#f5871f;">s</span><span>| { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= &amp;</span><span>counter; +</span><span> s.</span><span style="color:#4271ae;">spawn</span><span>(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> started&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> now </span><span style="color:#3e999f;">= </span><span>Instant::now(); +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">=</span><span> counter.</span><span style="color:#4271ae;">lock</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span> </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> counter); </span><span style="color:#999999;">// lock is held for 2 seconds +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> finished after </span><span style="color:#666969;">{}</span><span style="color:#718c00;">s&quot;</span><span>, now.</span><span style="color:#4271ae;">elapsed</span><span>().</span><span style="color:#4271ae;">as_secs</span><span>()); +</span><span> }); +</span><span> } +</span><span> }) +</span><span>} +</span></code></pre> +<p>You should do this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Mutex; +</span><span style="color:#8959a8;">use </span><span>std::thread; +</span><span style="color:#8959a8;">use </span><span>std::time::Duration; +</span><span style="color:#8959a8;">use </span><span>std::time::Instant; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#f5871f;">counter</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>Mutex&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;) { </span><span style="color:#999999;">// &lt;-- changed +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">=</span><span> counter.</span><span style="color:#4271ae;">lock</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); </span><span style="color:#999999;">// &lt;-- changed +</span><span> </span><span style="color:#3e999f;">*</span><span>counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#999999;">// lock is held only for the duration of the block +</span><span> </span><span style="color:#999999;">// it is important to create a new scope here, otherwise the lock would be held for another second +</span><span> } +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= </span><span>Mutex::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> thread::scope(|</span><span style="color:#f5871f;">s</span><span>| { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= &amp;</span><span>counter; +</span><span> s.</span><span style="color:#4271ae;">spawn</span><span>(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> started&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> now </span><span style="color:#3e999f;">= </span><span>Instant::now(); +</span><span> </span><span style="color:#4271ae;">handle_function</span><span>(counter); </span><span style="color:#999999;">// &lt;-- changed! we don&#39;t lock here +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> finished after </span><span style="color:#666969;">{}</span><span style="color:#718c00;">s&quot;</span><span>, now.</span><span style="color:#4271ae;">elapsed</span><span>().</span><span style="color:#4271ae;">as_secs</span><span>()); +</span><span> }); +</span><span> } +</span><span> }) +</span><span>} +</span><span> +</span></code></pre> +<p>Compare the output of the two programs. The first one will take 20 seconds to finish, while the second one will take 2 seconds.</p> +<p>First one:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>Thread 1 started +</span><span>Thread 0 started +</span><span>Thread 2 started +</span><span>Thread 3 started +</span><span>Thread 4 started +</span><span>Thread 5 started +</span><span>Thread 6 started +</span><span>Thread 7 started +</span><span>Thread 8 started +</span><span>Thread 9 started +</span><span>Thread 1 finished after 2s +</span><span>Thread 0 finished after 4s +</span><span>Thread 2 finished after 6s +</span><span>Thread 3 finished after 8s +</span><span>Thread 4 finished after 10s +</span><span>Thread 5 finished after 12s +</span><span>Thread 6 finished after 14s +</span><span>Thread 7 finished after 16s +</span><span>Thread 8 finished after 18s +</span><span>Thread 9 finished after 20s +</span><span> +</span></code></pre> +<p>Second one:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>Thread 0 started +</span><span>Thread 2 started +</span><span>Thread 1 started +</span><span>Thread 3 started +</span><span>Thread 4 started +</span><span>Thread 5 started +</span><span>Thread 6 started +</span><span>Thread 7 started +</span><span>Thread 8 started +</span><span>Thread 9 started +</span><span>Thread 1 finished after 2s +</span><span>Thread 2 finished after 2s +</span><span>Thread 0 finished after 2s +</span><span>Thread 3 finished after 2s +</span><span>Thread 4 finished after 2s +</span><span>Thread 5 finished after 2s +</span><span>Thread 6 finished after 2s +</span><span>Thread 7 finished after 2s +</span><span>Thread 8 finished after 2s +</span><span>Thread 9 finished after 2s +</span></code></pre> + + + + + Project feedback + 2022-12-29T00:00:00+00:00 + 2022-12-29T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/12-project-feedback/ + + <h1 id="project-feedback">Project feedback</h1> +<h2 id="unwrapping-options-results">Unwrapping options/results</h2> +<p>Always ask yourself twice if you really need to unwrap. In most cases, you don't have to. Use pattern matching instead, +as it provides a static guarantee that the value is present.</p> +<p>Pattern matching prevents you from writing code like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">some_function</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">is_some</span><span>() { +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x.</span><span style="color:#4271ae;">unwrap</span><span>()); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Let&#39;s say this line was added later and/or you forgot to put it in the if statement. +</span><span> </span><span style="color:#4271ae;">do_something</span><span>(x.</span><span style="color:#4271ae;">unwrap</span><span>()); </span><span style="color:#999999;">// this will blow up if x == None! +</span><span>} +</span></code></pre> +<p>Instead, you can write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">some_function</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(x) </span><span style="color:#3e999f;">=</span><span> x { +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x); +</span><span> </span><span style="color:#4271ae;">do_something</span><span>(x); +</span><span> } +</span><span>} +</span></code></pre> +<h2 id="question-mark-operator">Question mark operator</h2> +<p>In methods that return <code>Result</code> or <code>Option</code>, you can use the question mark operator to return early if the value is <code>None</code> or <code>Err</code>. +See: <a href="https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html">https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html</a></p> +<h2 id="logging">Logging</h2> +<p>You can use the <a href="https://crates.io/crates/log">log</a> crate to log messages. It's better than <code>println!</code> because it +can be easily turned off. It also allows you to use different severity levels (e.g. <code>info</code>, <code>warn</code>, <code>error</code>) and only +log messages above a certain level.</p> +<h2 id="string-vs-str">&amp;String vs &amp;str</h2> +<p>See <a href="https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters">https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters</a> +In general, if you want to pass a reference to a string, use <code>&amp;str</code> instead of <code>&amp;String</code>.</p> +<h2 id="use-current-versions-of-dependencies">Use current versions of dependencies</h2> +<p>You can use <a href="https://crates.io/crates/cargo-upgrades">cargo upgrades</a> to check for outdated dependencies.</p> +<h2 id="if-your-project-has-separate-binaries-use-multiple-binaries-or-a-workspace">If your project has separate binaries, use multiple binaries or a workspace</h2> +<p>You can have multiple binaries in a single cargo project. Simply place them in the <code>src/bin</code> directory. +You can run them with <code>cargo run --bin &lt;name&gt;</code>. Alternatively, you can setup a +<a href="https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html">workspace</a>.</p> +<h2 id="run-clippy-cargo-fmt">Run clippy &amp; cargo fmt</h2> +<p>This should have become a habit by now. You can disable clippy warnings for a single item with <code>#[allow(clippy::...)]</code>, +but in most cases you shouldn't do that.</p> +<h2 id="if-you-need-to-escape-characters-in-a-string-use-raw-strings">If you need to escape characters in a string, use raw strings</h2> +<p>See <a href="https://doc.rust-lang.org/reference/tokens.html#raw-string-literals">https://doc.rust-lang.org/reference/tokens.html#raw-string-literals</a></p> +<h2 id="how-to-handle-errors">How to handle errors?</h2> +<p>Short: <a href="https://kerkour.com/rust-error-handling">https://kerkour.com/rust-error-handling</a></p> +<p>Long: <a href="https://www.lpalmieri.com/posts/error-handling-rust/">https://www.lpalmieri.com/posts/error-handling-rust/</a></p> +<h2 id="don-t-pass-around-locked-mutex-s-contents">Don't pass around locked mutex's contents</h2> +<p>If you have a mutex, you can use <code>lock()</code> to get a guard that will unlock the mutex when it goes out of scope. +But don't pass the contents of the guard to functions that can block (unless the mutex <em>must</em> be locked for +the entire duration of the function). +Instead of:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Mutex; +</span><span style="color:#8959a8;">use </span><span>std::thread; +</span><span style="color:#8959a8;">use </span><span>std::time::Duration; +</span><span style="color:#8959a8;">use </span><span>std::time::Instant; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#f5871f;">counter</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut i32</span><span>) { +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> </span><span style="color:#3e999f;">*</span><span>counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= </span><span>Mutex::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> thread::scope(|</span><span style="color:#f5871f;">s</span><span>| { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= &amp;</span><span>counter; +</span><span> s.</span><span style="color:#4271ae;">spawn</span><span>(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> started&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> now </span><span style="color:#3e999f;">= </span><span>Instant::now(); +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">=</span><span> counter.</span><span style="color:#4271ae;">lock</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span> </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> counter); </span><span style="color:#999999;">// lock is held for 2 seconds +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> finished after </span><span style="color:#666969;">{}</span><span style="color:#718c00;">s&quot;</span><span>, now.</span><span style="color:#4271ae;">elapsed</span><span>().</span><span style="color:#4271ae;">as_secs</span><span>()); +</span><span> }); +</span><span> } +</span><span> }) +</span><span>} +</span></code></pre> +<p>You should do this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Mutex; +</span><span style="color:#8959a8;">use </span><span>std::thread; +</span><span style="color:#8959a8;">use </span><span>std::time::Duration; +</span><span style="color:#8959a8;">use </span><span>std::time::Instant; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">handle_function</span><span>(</span><span style="color:#f5871f;">counter</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>Mutex&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;) { </span><span style="color:#999999;">// &lt;-- changed +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">=</span><span> counter.</span><span style="color:#4271ae;">lock</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); </span><span style="color:#999999;">// &lt;-- changed +</span><span> </span><span style="color:#3e999f;">*</span><span>counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#999999;">// lock is held only for the duration of the block +</span><span> </span><span style="color:#999999;">// it is important to create a new scope here, otherwise the lock would be held for another second +</span><span> } +</span><span> thread::sleep(Duration::from_secs(</span><span style="color:#f5871f;">1</span><span>)); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= </span><span>Mutex::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> thread::scope(|</span><span style="color:#f5871f;">s</span><span>| { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> counter </span><span style="color:#3e999f;">= &amp;</span><span>counter; +</span><span> s.</span><span style="color:#4271ae;">spawn</span><span>(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> started&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> now </span><span style="color:#3e999f;">= </span><span>Instant::now(); +</span><span> </span><span style="color:#4271ae;">handle_function</span><span>(counter); </span><span style="color:#999999;">// &lt;-- changed! we don&#39;t lock here +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread </span><span style="color:#666969;">{i}</span><span style="color:#718c00;"> finished after </span><span style="color:#666969;">{}</span><span style="color:#718c00;">s&quot;</span><span>, now.</span><span style="color:#4271ae;">elapsed</span><span>().</span><span style="color:#4271ae;">as_secs</span><span>()); +</span><span> }); +</span><span> } +</span><span> }) +</span><span>} +</span><span> +</span></code></pre> +<p>Compare the output of the two programs. The first one will take 20 seconds to finish, while the second one will take 2 seconds.</p> +<p>First one:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>Thread 1 started +</span><span>Thread 0 started +</span><span>Thread 2 started +</span><span>Thread 3 started +</span><span>Thread 4 started +</span><span>Thread 5 started +</span><span>Thread 6 started +</span><span>Thread 7 started +</span><span>Thread 8 started +</span><span>Thread 9 started +</span><span>Thread 1 finished after 2s +</span><span>Thread 0 finished after 4s +</span><span>Thread 2 finished after 6s +</span><span>Thread 3 finished after 8s +</span><span>Thread 4 finished after 10s +</span><span>Thread 5 finished after 12s +</span><span>Thread 6 finished after 14s +</span><span>Thread 7 finished after 16s +</span><span>Thread 8 finished after 18s +</span><span>Thread 9 finished after 20s +</span><span> +</span></code></pre> +<p>Second one:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>Thread 0 started +</span><span>Thread 2 started +</span><span>Thread 1 started +</span><span>Thread 3 started +</span><span>Thread 4 started +</span><span>Thread 5 started +</span><span>Thread 6 started +</span><span>Thread 7 started +</span><span>Thread 8 started +</span><span>Thread 9 started +</span><span>Thread 1 finished after 2s +</span><span>Thread 2 finished after 2s +</span><span>Thread 0 finished after 2s +</span><span>Thread 3 finished after 2s +</span><span>Thread 4 finished after 2s +</span><span>Thread 5 finished after 2s +</span><span>Thread 6 finished after 2s +</span><span>Thread 7 finished after 2s +</span><span>Thread 8 finished after 2s +</span><span>Thread 9 finished after 2s +</span></code></pre> + + + + + Async: Part 1 + 2022-12-12T00:00:00+00:00 + 2022-12-12T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/11-async-1/ + + <h2 id="tokio">Tokio</h2> +<p>We'll use the <a href="https://tokio.rs/tokio/tutorial">Tokio tutorial</a> (chapters <code>Overview</code>-<code>Channels</code>).</p> +<h2 id="common-rust-lifetime-misconceptions">Common Rust Lifetime Misconceptions</h2> +<p>Please read <a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md">this blogpost</a>.</p> + + + + + Design patterns + 2022-12-05T00:00:00+00:00 + 2022-12-05T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/ + + <h2 id="object-oriented-programming-and-rust">Object-oriented programming and Rust</h2> +<p>The book has <a href="https://doc.rust-lang.org/stable/book/ch17-01-what-is-oo.html">a chapter dedicated to it</a>. +Especially the <a href="https://doc.rust-lang.org/stable/book/ch17-03-oo-design-patterns.html#encoding-states-and-behavior-as-types">&quot;typestate&quot;</a> pattern is very interesting. +You can read more about it <a href="http://cliffle.com/blog/rust-typestate/">here</a>.</p> +<h2 id="how-to-build-a-good-library">How to build a good library</h2> +<p><a href="https://rust-lang.github.io/api-guidelines/about.html">These guidelines</a> have been created by the Rust library team.</p> +<h2 id="how-to-handle-errors">How to handle errors</h2> +<p><a href="https://nick.groenen.me/posts/rust-error-handling/">This post</a> is from 2020, but the libraries it mentions (<code>anyhow</code> and <code>thiserror</code>) are still the most popular.</p> +<h2 id="serde">Serde</h2> +<p><a href="https://serde.rs/">Serde</a> is the most popular serialization library for Rust.</p> +<h2 id="assignment">Assignment</h2> +<p>This week's assignment is to write a &quot;distributed&quot; calculator. +You should base your solution on the <a href="https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html">final project from the book</a>.</p> + + + + + Design patterns + 2022-12-05T00:00:00+00:00 + 2022-12-05T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/10-design-patterns/ + + <h2 id="object-oriented-programming-and-rust">Object-oriented programming and Rust</h2> +<p>The book has <a href="https://doc.rust-lang.org/stable/book/ch17-01-what-is-oo.html">a chapter dedicated to it</a>. +Especially the <a href="https://doc.rust-lang.org/stable/book/ch17-03-oo-design-patterns.html#encoding-states-and-behavior-as-types">&quot;typestate&quot;</a> pattern is very interesting. +You can read more about it <a href="http://cliffle.com/blog/rust-typestate/">here</a>.</p> +<h2 id="how-to-build-a-good-library">How to build a good library</h2> +<p><a href="https://rust-lang.github.io/api-guidelines/about.html">These guidelines</a> have been created by the Rust library team.</p> +<h2 id="how-to-handle-errors">How to handle errors</h2> +<p><a href="https://nick.groenen.me/posts/rust-error-handling/">This post</a> is from 2020, but the libraries it mentions (<code>anyhow</code> and <code>thiserror</code>) are still the most popular.</p> +<h2 id="serde">Serde</h2> +<p><a href="https://serde.rs/">Serde</a> is the most popular serialization library for Rust.</p> +<h2 id="assignment">Assignment</h2> +<p>This week's assignment is to write a &quot;distributed&quot; calculator. +You should base your solution on the <a href="https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html">final project from the book</a>.</p> + + + + + Fearless concurrency + 2022-11-28T00:00:00+00:00 + 2022-11-28T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/09-concurrency/ + + <h2 id="parallelism-vs-concurrency">Parallelism vs Concurrency</h2> +<p>Concurrency is when tasks <strong>can make</strong> progress <strong>independently</strong> of each other.</p> +<p>Parallelism is when multiple tasks <strong>make</strong> progress <strong>at the same time</strong>.</p> +<h2 id="concurrency-models-in-rust">Concurrency models in Rust</h2> +<h3 id="threads">Threads</h3> +<p>Nothing unusual here.</p> +<p>Threads can be created with the <code>thread::spawn</code> function <a href="https://doc.rust-lang.org/std/thread/fn.spawn.html">docs - please read them!</a>.</p> +<p>This method returns a <code>JoinHandle&lt;T&gt;</code> which can be used to wait for the thread to finish. <code>T</code> is the type of the thread's return value.</p> +<h4 id="propagating-panics">Propagating panics</h4> +<p>In Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).</p> +<h4 id="closures">Closures</h4> +<p>Closures which are used to create threads must take ownership of any values they use. It can be forced with the <code>move</code> keyword.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::thread; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> v </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> handle </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Here&#39;s a vector: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, v); +</span><span> }); +</span><span> +</span><span> handle.</span><span style="color:#4271ae;">join</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span>} +</span></code></pre> +<p>Normal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!</p> +<p>But what if we need to share some state?</p> +<h3 id="message-passing">Message passing</h3> +<p>One possible way is to use message passing. We can use a blocking queue (called <code>mpsc</code> - <a href="https://doc.rust-lang.org/std/sync/mpsc/index.html">&quot;multi producer single consumer FIFO queue&quot;</a>) to do it. +We talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.</p> +<h3 id="mutexes">Mutexes</h3> +<p>In Rust, a mutex <em>wraps</em> a value and makes it thread-safe. +Because it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the <code>RefCell</code> type.</p> +<p><code>Arc</code> is a smart pointer like <code>Rc</code> but it can be shared between threads.</p> +<p>Please read more about them in <a href="https://doc.rust-lang.org/stable/book/ch16-03-shared-state.html">the book</a>.</p> +<p><a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html">The docs</a> also mention <code>poisoning</code>.</p> +<h3 id="rwlocks">RwLocks</h3> +<p><a href="https://doc.rust-lang.org/std/sync/struct.RwLock.html">RwLocks</a> are similar to mutexes, but they distinguish between read and write locks.</p> +<h2 id="send-and-sync">Send and Sync</h2> +<p>They are marker traits used to indicate that a type or a reference to it can be sent across threads. See the <a href="https://doc.rust-lang.org/nomicon/send-and-sync.html">nomicon</a> for more information.</p> +<h2 id="atomic-types">Atomic types</h2> +<p>Atomic types are described in <a href="https://doc.rust-lang.org/std/sync/atomic/">the docs</a>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Arc; +</span><span style="color:#8959a8;">use </span><span>std::sync::atomic::{AtomicUsize, Ordering}; +</span><span style="color:#8959a8;">use </span><span>std::{hint, thread}; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(AtomicUsize::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span> </span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> spinlock_clone.</span><span style="color:#4271ae;">store</span><span>(</span><span style="color:#f5871f;">0</span><span>, Ordering::SeqCst); +</span><span> }); +</span><span> +</span><span> </span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span> </span><span style="color:#8959a8;">while</span><span> spinlock.</span><span style="color:#4271ae;">load</span><span>(Ordering::SeqCst) </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Err</span><span>(panic) </span><span style="color:#3e999f;">=</span><span> thread.</span><span style="color:#4271ae;">join</span><span>() { +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread had an error: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, panic); +</span><span> } +</span><span>} +</span></code></pre> +<p>Note that <code>atomic</code> values don't have to be wrapped in a mutex when shared across threads.</p> +<h3 id="wait">Wait...</h3> +<p>If most types are <code>Sync + Send</code>, then what stops us from using a standard, non-atomic integer in the example above?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0594]: cannot assign to data in an `Arc` +</span><span> --&gt; src/main.rs:9:9 +</span><span> | +</span><span>9 | *spinlock_clone += 1; +</span><span> | ^^^^^^^^^^^^^^^^^^^^ cannot assign +</span><span> | +</span><span> = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc&lt;i32&gt;` +</span></code></pre> +<p>...so we would have to use a <code>RefCell</code> to be able to modify the value through a shared reference...</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(RefCell::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone.</span><span style="color:#4271ae;">borrow_mut</span><span>() </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock.</span><span style="color:#4271ae;">borrow</span><span>() </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<p>...but <code>RefCell</code> isn't <code>Sync</code>:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0277]: `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> --&gt; src/main.rs:9:18 +</span><span> | +</span><span>9 | let thread = thread::spawn(move|| { +</span><span> | ^^^^^^^^^^^^^ `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> | +</span><span> = help: the trait `Sync` is not implemented for `RefCell&lt;i32&gt;` +</span><span> = note: required because of the requirements on the impl of `Send` for `Arc&lt;RefCell&lt;i32&gt;&gt;` +</span><span> = note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]` +</span><span>note: required by a bound in `spawn` +</span></code></pre> +<p>And that bound mentioned in the last line looks like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">spawn</span><span>&lt;F, T&gt;(</span><span style="color:#f5871f;">f</span><span>: F) -&gt; JoinHandle&lt;T&gt; </span><span style="color:#8959a8;">where +</span><span> F: FnOnce() -&gt; T, +</span><span> F: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span><span> T: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span></code></pre> +<h4 id="exercise-for-the-reader">Exercise for the reader</h4> +<p>Why is it impossible to share a reference to a <code>Mutex</code> between threads?</p> +<h2 id="data-parallelism-with-rayon">Data parallelism with Rayon</h2> +<p><a href="https://docs.rs/rayon/latest/rayon/">Rayon</a> is a library for parallelization of data processing. +It can be used to parallelize the execution of functions over a collection of data by switching the standard <code>Iterator</code> to a <code>ParallelIterator</code>. +It works very similar to <a href="https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#executing_streams_in_parallel">Java's parallel streams</a>.</p> +<p>Why do that? Because thread synchronization is hard! <a href="https://doc.rust-lang.org/nomicon/races.html">Rust prevents data races</a>, but <a href="https://users.rust-lang.org/t/deadlock-is-it-a-bug-or-is-it-intentional/1544">logical races and deadlocks are impossible to prevent!</a>!</p> +<p><a href="https://github.com/rayon-rs/rayon/blob/master/FAQ.md">Rayon's FAQ</a> is worth reading.</p> +<h2 id="reading">Reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html">The Book</a></li> +<li><a href="http://archive.today/WFlZV">Safely writing code that isn't thread-safe</a></li> +</ul> + + + + + Smart Pointers + 2022-11-21T00:00:00+00:00 + 2022-11-21T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/ + + <h1 id="working-with-the-heap">Working with the heap</h1> +<p>So far we've only used heap allocated memory indirectly by working with containers such as vectors, maps or the <code>String</code> type, otherwise allocating our variables on the stack. We didn't really have to be aware of the fact that these collections used the heap, as all that memory management details were hidden away from us. In this lesson we'll take a closer look at what is really happening there and how we can do that ourselves.</p> +<p>To work with heap-allocated memory, Rust features <em>smart pointers</em>. You should have already heard this term as it is a very important feature in C++ and the concept is virtually the same here - they are wrappers around raw allocated memory that provide additional, safety-ensuring mechanism. What defines a smart pointer in Rust is generally the implementation of two traits: <code>Drop</code> and <code>Deref</code>.</p> +<p>The <code>Drop</code> trait is pretty straightforward as it consists of one method - <code>fn drop(&amp;mut self)</code> - that is, basically, the destructor, invoked during stack unwinding.</p> +<p>The <code>Deref</code> trait allows us to overload the dereference (<code>*</code>) operator.</p> +<h2 id="deref-coercion">Deref coercion</h2> +<p>Apart from enabling access to the underlying value, implementing the <code>Deref</code> trait enables Rust to perform <em>deref coercion</em> on the pointer - trying to remove as many levels of indirection as it can. What it means in practice is that we will be able to use it with any code working on plain references.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::ops::Deref; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>MyBox&lt;T&gt;(T); +</span><span> +</span><span style="color:#999999;">// We won&#39;t be allocating anything on the heap here as it is not important here. +</span><span style="color:#999999;">// We&#39;re only focusing on the dereference mechanisms. +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T) -&gt; MyBox&lt;T&gt; { +</span><span> MyBox(x) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Deref </span><span style="color:#8959a8;">for </span><span>MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Target </span><span style="color:#3e999f;">=</span><span> T; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">deref</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">Self::</span><span>Target { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0 +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#f5871f;">name</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> int_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(x); +</span><span> +</span><span> assert_eq!(</span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#3e999f;">*</span><span>int_box); +</span><span> +</span><span> </span><span style="color:#999999;">// String also implements the `Deref` trait. +</span><span> </span><span style="color:#999999;">// In fact, String actually is a smart pointer. +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;I&#39;m a smart pointer too&quot;</span><span>); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> +</span><span> </span><span style="color:#999999;">// Deref coercion can deal with multiple levels of indirection. +</span><span> </span><span style="color:#8959a8;">let</span><span> str_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Rust&quot;</span><span>)); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>str_box); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/deref_coercion.rs">deref_coercion.rs</a>)</sub></p> +<p>In general, there are three possible coercions that Rust can perform:</p> +<ul> +<li> +<p>From <code>&amp;T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;mut U</code> when <code>T: DerefMut&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +</ul> +<p>While the first two coercions are straightforward, the third one is possible because treating a mutable reference as an immutable one does not break the rules of ownership.</p> +<h1 id="box-simple-wrapper"><code>Box</code> - simple wrapper</h1> +<p>The <code>Box&lt;T&gt;</code> type is the most basic out of Rust's smart pointers, equivalent to C++'s <code>std::unique_ptr&lt;T&gt;</code>. It's a simple wrapper that makes sure the underlying memory gets allocated and freed properly.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">box_simple</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> b </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Box</span><span>::new(</span><span style="color:#f5871f;">5</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, b); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10 </span><span style="color:#3e999f;">+ *</span><span>b; +</span><span>} +</span><span> +</span><span style="color:#999999;">// `Box` gives us the indirection required to define +</span><span style="color:#999999;">// recursive types +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">enum </span><span>List { +</span><span> Cons(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#c99e00;">Box</span><span>&lt;List&gt;), +</span><span> Nil, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#4271ae;">box_simple</span><span>(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/box.rs">box.rs</a>)</sub></p> +<h1 id="reference-counting">Reference counting</h1> +<p>The <code>Rc&lt;T&gt;</code> type is the equivalent of <code>std::shared_ptr&lt;T&gt;</code> from C++. There is one caveat to this though - because we're creating multiple references to the same object, those references have to be immutable in accordance with the ownership rules.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> outer_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> inner_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// strong_count represents the number of owning references pointing +</span><span> </span><span style="color:#999999;">// to data +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> outer_ref </span><span style="color:#3e999f;">= </span><span>Rc::clone(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref); +</span><span> +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref)); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> still lives!&quot;</span><span>, outer_ref.</span><span style="color:#f5871f;">0</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/ref_count.rs">ref_count.rs</a>)</sub></p> +<p>Rust also provides a non-owning pointer in the form of <code>Weak&lt;T&gt;</code> (equivalent to <code>std::weak_ptr&lt;T&gt;</code>) that can be obtained from an instance of <code>Rc&lt;T&gt;</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> weak_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> shared_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// weak_count keeps track of the non-owning reference to the data +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">0</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// `downgrade()` obtains a weak pointer to Rc&#39;s data +</span><span> weak_ref </span><span style="color:#3e999f;">= </span><span>Rc::downgrade(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref); +</span><span> +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// In order to use the the data underneath the weak pointer +</span><span> </span><span style="color:#999999;">// we need to obtain a new shared pointer from it. +</span><span> </span><span style="color:#999999;">// The `upgrade()` method returns `Option&lt;Rc&lt;T&gt;&gt;`. +</span><span> </span><span style="color:#8959a8;">let</span><span> temp </span><span style="color:#3e999f;">=</span><span> weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>(); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;The value is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, temp.</span><span style="color:#4271ae;">unwrap</span><span>().</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The value should be deallocated by now.&quot;</span><span>); +</span><span> assert!(weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>().</span><span style="color:#4271ae;">is_none</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/weak_ref.rs">weak_ref.rs</a>)</sub></p> +<h1 id="mutating-the-immutable">Mutating the immutable</h1> +<p>Good examples and explanation of the interior mutability pattern and runtime borrow checking can be found in the <a href="https://doc.rust-lang.org/book/ch15-05-interior-mutability.html">book</a>.</p> +<p>Alongside the <code>RefCell&lt;T&gt;</code> type described above, there is an analogous <a href="https://doc.rust-lang.org/std/cell/struct.Cell.html"><code>Cell&lt;T&gt;</code></a> type that operates on values instead of references.</p> +<h1 id="convenient-handling-of-dyn-objects">Convenient handling of <code>dyn</code> objects</h1> +<p>In previous labs you learned about dynamic dispatch and its strengths. The largest drawback you noticed is most likely that they are <em>unsized</em> (<code>!Sized</code>, where <code>!</code> being syntax signifying lack of trait implementation).</p> +<p>When storing an object on a heap, however, we can use it as a <code>dyn</code> object seamlessly.</p> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch15-00-smart-pointers.html">The Book, chapter 15</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">std::borrow::Cow</a>, a versatile copy-on-write smart pointer</p> +</li> +</ul> +<h1 id="additional-reading">Additional reading</h1> +<ul> +<li><a href="https://www.fpcomplete.com/blog/rust-asref-asderef/">On wrapped references</a></li> +<li><a href="https://dev.to/zhanghandong/rust-concept-clarification-deref-vs-asref-vs-borrow-vs-cow-13g6"><code>Deref</code> vs <code>AsRef</code> vs <code>Borrow</code></a></li> +</ul> +<h2 id="assignment-5-graded">Assignment 5 (graded)</h2> +<p><a href="https://classroom.github.com/a/QlO3aCCP">Corporations</a></p> +<p>Deadline: 13.11.2024 23:59</p> + + + + + Feedback #2 + 2022-11-21T00:00:00+00:00 + 2022-11-21T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/ + + <h2 id="feedback">Feedback</h2> +<h3 id="conditional-implementation">Conditional implementation</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">const</span><span> N: </span><span style="color:#8959a8;">usize</span><span>&gt; Shape </span><span style="color:#8959a8;">for </span><span>SphereN&lt;N&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Volume </span><span style="color:#3e999f;">= </span><span>VolumeN&lt;N&gt;; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">volume</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Volume { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> volume: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) </span><span style="color:#3e999f;">* </span><span style="color:#666969;">PI</span><span>) </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> N </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">3 </span><span>{ +</span><span> volume </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#666969;">PI +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">4.0_</span><span style="color:#8959a8;">f64 +</span><span> </span><span style="color:#3e999f;">/ </span><span style="color:#f5871f;">3.0_</span><span style="color:#8959a8;">f64</span><span>) </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32</span><span>; +</span><span> } +</span><span> </span><span style="color:#8959a8;">Self</span><span>::Volume::new(volume) +</span><span> } +</span><span>} +</span></code></pre> +<p>Instead of checking <code>N == 3</code>, you can provide different impls for <code>SphereN&lt;2&gt;</code> and +<code>SphereN&lt;3&gt;</code> (as they are different types).</p> +<h3 id="u32-and-u64">u32 and u64</h3> +<p>They <em>are</em> different types, but because you can easily cast one to another, +it was not sufficient to make the implementation type-safe.</p> + + + + + Smart Pointers + 2022-11-21T00:00:00+00:00 + 2022-11-21T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/ + + <h1 id="working-with-the-heap">Working with the heap</h1> +<p>So far we've only used heap allocated memory indirectly by working with containers such as vectors, maps or the <code>String</code> type, otherwise allocating our variables on the stack. We didn't really have to be aware of the fact that these collections used the heap, as all that memory management details were hidden away from us. In this lesson we'll take a closer look at what is really happening there and how we can do that ourselves.</p> +<p>To work with heap-allocated memory, Rust features <em>smart pointers</em>. You should have already heard this term as it is a very important feature in C++ and the concept is virtually the same here - they are wrappers around raw allocated memory that provide additional, safety-ensuring mechanism. What defines a smart pointer in Rust is generally the implementation of two traits: <code>Drop</code> and <code>Deref</code>.</p> +<p>The <code>Drop</code> trait is pretty straightforward as it consists of one method - <code>fn drop(&amp;mut self)</code> - that is, basically, the destructor, invoked during stack unwinding.</p> +<p>The <code>Deref</code> trait allows us to overload the dereference (<code>*</code>) operator.</p> +<h2 id="deref-coercion">Deref coercion</h2> +<p>Apart from enabling access to the underlying value, implementing the <code>Deref</code> trait enables Rust to perform <em>deref coercion</em> on the pointer - trying to remove as many levels of indirection as it can. What it means in practice is that we will be able to use it with any code working on plain references.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::ops::Deref; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>MyBox&lt;T&gt;(T); +</span><span> +</span><span style="color:#999999;">// We won&#39;t be allocating anything on the heap here as it is not important here. +</span><span style="color:#999999;">// We&#39;re only focusing on the dereference mechanisms. +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T) -&gt; MyBox&lt;T&gt; { +</span><span> MyBox(x) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Deref </span><span style="color:#8959a8;">for </span><span>MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Target </span><span style="color:#3e999f;">=</span><span> T; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">deref</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">Self::</span><span>Target { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0 +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#f5871f;">name</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> int_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(x); +</span><span> +</span><span> assert_eq!(</span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#3e999f;">*</span><span>int_box); +</span><span> +</span><span> </span><span style="color:#999999;">// String also implements the `Deref` trait. +</span><span> </span><span style="color:#999999;">// In fact, String actually is a smart pointer. +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;I&#39;m a smart pointer too&quot;</span><span>); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> +</span><span> </span><span style="color:#999999;">// Deref coercion can deal with multiple levels of indirection. +</span><span> </span><span style="color:#8959a8;">let</span><span> str_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Rust&quot;</span><span>)); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>str_box); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/deref_coercion.rs">deref_coercion.rs</a>)</sub></p> +<p>In general, there are three possible coercions that Rust can perform:</p> +<ul> +<li> +<p>From <code>&amp;T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;mut U</code> when <code>T: DerefMut&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +</ul> +<p>While the first two coercions are straightforward, the third one is possible because treating a mutable reference as an immutable one does not break the rules of ownership.</p> +<h1 id="box-simple-wrapper"><code>Box</code> - simple wrapper</h1> +<p>The <code>Box&lt;T&gt;</code> type is the most basic out of Rust's smart pointers, equivalent to C++'s <code>std::unique_ptr&lt;T&gt;</code>. It's a simple wrapper that makes sure the underlying memory gets allocated and freed properly.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">box_simple</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> b </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Box</span><span>::new(</span><span style="color:#f5871f;">5</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, b); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10 </span><span style="color:#3e999f;">+ *</span><span>b; +</span><span>} +</span><span> +</span><span style="color:#999999;">// `Box` gives us the indirection required to define +</span><span style="color:#999999;">// recursive types +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">enum </span><span>List { +</span><span> Cons(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#c99e00;">Box</span><span>&lt;List&gt;), +</span><span> Nil, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#4271ae;">box_simple</span><span>(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/box.rs">box.rs</a>)</sub></p> +<h1 id="reference-counting">Reference counting</h1> +<p>The <code>Rc&lt;T&gt;</code> type is the equivalent of <code>std::shared_ptr&lt;T&gt;</code> from C++. There is one caveat to this though - because we're creating multiple references to the same object, those references have to be immutable in accordance with the ownership rules.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> outer_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> inner_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// strong_count represents the number of owning references pointing +</span><span> </span><span style="color:#999999;">// to data +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> outer_ref </span><span style="color:#3e999f;">= </span><span>Rc::clone(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref); +</span><span> +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref)); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> still lives!&quot;</span><span>, outer_ref.</span><span style="color:#f5871f;">0</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/ref_count.rs">ref_count.rs</a>)</sub></p> +<p>Rust also provides a non-owning pointer in the form of <code>Weak&lt;T&gt;</code> (equivalent to <code>std::weak_ptr&lt;T&gt;</code>) that can be obtained from an instance of <code>Rc&lt;T&gt;</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> weak_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> shared_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// weak_count keeps track of the non-owning reference to the data +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">0</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// `downgrade()` obtains a weak pointer to Rc&#39;s data +</span><span> weak_ref </span><span style="color:#3e999f;">= </span><span>Rc::downgrade(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref); +</span><span> +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// In order to use the the data underneath the weak pointer +</span><span> </span><span style="color:#999999;">// we need to obtain a new shared pointer from it. +</span><span> </span><span style="color:#999999;">// The `upgrade()` method returns `Option&lt;Rc&lt;T&gt;&gt;`. +</span><span> </span><span style="color:#8959a8;">let</span><span> temp </span><span style="color:#3e999f;">=</span><span> weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>(); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;The value is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, temp.</span><span style="color:#4271ae;">unwrap</span><span>().</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The value should be deallocated by now.&quot;</span><span>); +</span><span> matches!(weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>(), </span><span style="color:#c99e00;">None</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/weak_ref.rs">weak_ref.rs</a>)</sub></p> +<h1 id="mutating-the-immutable">Mutating the immutable</h1> +<p>Good examples and explanation of the inferior mutability pattern and runtime borrow checking can be found in the <a href="https://doc.rust-lang.org/book/ch15-05-interior-mutability.html">book</a>.</p> +<p>Alongisde the <code>RefCell&lt;T&gt;</code> type described above, there is an analogous <a href="https://doc.rust-lang.org/std/cell/struct.Cell.html"><code>Cell&lt;T&gt;</code></a> type that operates on values instead of references.</p> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch15-00-smart-pointers.html">The Book, chapter 15</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">std::borrow::Cow</a>, a versatile copy-on-write smart pointer</p> +</li> +</ul> +<h1 id="additional-reading">Additional reading</h1> +<ul> +<li><a href="https://www.fpcomplete.com/blog/rust-asref-asderef/">On wrapped references</a></li> +</ul> + + + + + Feedback #2 + 2022-11-21T00:00:00+00:00 + 2022-11-21T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/08-feedback-2/ + + <h2 id="feedback">Feedback</h2> +<h3 id="conditional-implementation">Conditional implementation</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">const</span><span> N: </span><span style="color:#8959a8;">usize</span><span>&gt; Shape </span><span style="color:#8959a8;">for </span><span>SphereN&lt;N&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Volume </span><span style="color:#3e999f;">= </span><span>VolumeN&lt;N&gt;; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">volume</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Volume { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> volume: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) </span><span style="color:#3e999f;">* </span><span style="color:#666969;">PI</span><span>) </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> N </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">3 </span><span>{ +</span><span> volume </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#8959a8;">f64</span><span>::from(</span><span style="color:#c82829;">self</span><span>.radius) +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#666969;">PI +</span><span> </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">4.0_</span><span style="color:#8959a8;">f64 +</span><span> </span><span style="color:#3e999f;">/ </span><span style="color:#f5871f;">3.0_</span><span style="color:#8959a8;">f64</span><span>) </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32</span><span>; +</span><span> } +</span><span> </span><span style="color:#8959a8;">Self</span><span>::Volume::new(volume) +</span><span> } +</span><span>} +</span></code></pre> +<p>Instead of checking <code>N == 3</code>, you can provide different impls for <code>SphereN&lt;2&gt;</code> and +<code>SphereN&lt;3&gt;</code> (as they are different types).</p> +<h3 id="u32-and-u64">u32 and u64</h3> +<p>They <em>are</em> different types, but because you can easily cast one to another, +it was not sufficient to make the implementation type-safe.</p> + + + + + Closures and Iterators + 2022-11-14T00:00:00+00:00 + 2022-11-14T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/06-closures-iterators/ + + <h1 id="closures">Closures</h1> +<p>Closures (Polish: &quot;domknięcia&quot;) are anonymous functions that can access variables from the scope in which they were defined.</p> +<p>We'll go through the examples from <a href="https://doc.rust-lang.org/rust-by-example/fn/closures.html">Rust by Example</a>.</p> +<h1 id="iterators">Iterators</h1> +<p>In Rust, there is no hierarchy of types for collections (because there is no inheritance in general). +Instead, what makes a collection is that it can be iterated over.</p> +<p>We'll go through the official <a href="https://doc.rust-lang.org/stable/std/iter/">docs</a>. +Most methods are defined in the <a href="https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html">Iterator trait</a>.</p> +<h1 id="reading">Reading</h1> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch12-00-an-io-project.html">The Book, chapter 12 (that's a project!)</a></li> +<li><a href="https://doc.rust-lang.org/book/ch13-00-functional-features.html">The Book, chapter 13</a></li> +<li><a href="https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html">The Book, chapter 14</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-05-advanced-functions-and-closures.html">The Book, Advanced Functions and Closures</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html">The Book, Advanced Traits</a></li> +</ul> + + + + + Reasoning About Types + 2022-11-07T00:00:00+00:00 + 2022-11-07T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/ + + <h1 id="type-traits">Type traits</h1> +<p>Traits are a way to defined common behavior between different types. They can be compared to <em>interfaces</em> from many other mainstream languages or to typeclasses from Haskell, however, Rust is not an object-oriented language and there are some notable differences between type traits and Java interfaces.</p> +<p>The way we describe behavior in Rust is through methods. Traits consist of a set of these methods which then should be implemented by a type. We've already encountered examples of these, like the <code>Clone</code> trait which specified that the <code>clone()</code> method can be called on some given type. Now, let's take a deeper look and try defining our own trait.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.username, </span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/basic_trait.rs">basic_trait.rs</a>)</sub></p> +<h2 id="default-implementations">Default implementations</h2> +<p>Trait definitions can also be provided with default implementations of behaviors.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Upload { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">struct </span><span>Photo { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span> </span><span style="color:#c82829;">width</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">height</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Description { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;No description available.&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// All default implementations +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Upload {} +</span><span> +</span><span style="color:#999999;">// Default implementations can be overwritten +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> x </span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.filename, </span><span style="color:#c82829;">self</span><span>.width, </span><span style="color:#c82829;">self</span><span>.height) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Default implementations can rely on methods with no defaults +</span><span style="color:#8959a8;">trait </span><span>Size { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">size</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">width</span><span>() </span><span style="color:#3e999f;">* </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">height</span><span>() +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Size </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.width +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.height +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Using default impl of `size()` +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> upload </span><span style="color:#3e999f;">=</span><span> Upload { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;notes.txt&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Upload: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, upload.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> photo </span><span style="color:#3e999f;">=</span><span> Photo { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;stock_crustacean.png&quot;</span><span>), +</span><span> width: </span><span style="color:#f5871f;">100</span><span>, +</span><span> height: </span><span style="color:#f5871f;">150</span><span>, +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Photo: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;Size: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">size</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/trait_default.rs">trait_default.rs</a>)</sub></p> +<h2 id="what-about-derive">What about <em>derive</em>?</h2> +<p>There is a trait-related thing we have used quite extensively and not explained yet, namely the <code>#[derive]</code> attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.</p> +<p>Derivable traits:</p> +<ul> +<li> +<p>Equality traits: <code>Eq</code>, <code>PartialEq</code> and comparison traits: <code>Ord</code> and <code>PartialOrd</code>. The <code>Partial-</code> versions exist because there are types which don't fulfill the reflexivity requirement of equality (<code>NaN != NaN</code>) or do not form a total order (<code> NaN &lt; 0.0 == false</code> and <code>NaN &gt;= 0.0 == false</code>).</p> +</li> +<li> +<p>Data duplication traits: <code>Clone</code> and <code>Copy</code></p> +</li> +<li> +<p><code>Hash</code> - allows using values of that type as keys in a hashmap</p> +</li> +<li> +<p><code>Default</code> - provides a zero-arg constructor function</p> +</li> +<li> +<p><code>Debug</code> - provides a formatting of the value which can be used in debugging context. It should <em>NOT</em> be implemented manually. In general, if it's possible to derive the <code>Debug</code>, there are no reasons against doing it.</p> +</li> +</ul> +<h3 id="when-is-it-possible-to-derive-a-trait">When is it possible to derive a trait?</h3> +<p>When all fields of a struct/variants of an enum implement that trait.</p> +<h3 id="should-all-traits-always-be-derived-if-it-is-possible">Should all traits always be derived if it is possible?</h3> +<p>No. Although it may be tempting to just slap <code>#[derive(Clone, Copy)]</code> everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.</p> +<h1 id="generics">Generics</h1> +<p>Suppose we want to find the largest element in a sequence and return it. Very much on purpose, we didn't specify what type these elements would be - ideally, we would love it to work on all types that have a defined notion of a <em>largest</em> element. However, to make things simpler for now, let's focus only on two primitive types: <code>i32</code> and <code>char</code>. Let's try to write the code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>]) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">char</span><span>]) -&gt; </span><span style="color:#8959a8;">char </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/non_generic.rs">non_generic.rs</a>)</sub></p> +<p>Perfect, it works! Now only twenty more types to go...</p> +<p>Fortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>Cleaner already - we merged possibly very many implementations into one. But, when we try to compile this:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0369]: binary operation `&gt;` cannot be applied to type `T` +</span><span> --&gt; src/main.rs:5:17 +</span><span> | +</span><span>5 | if item &gt; largest { +</span><span> | ---- ^ ------- T +</span><span> | | +</span><span> | T +</span><span> | +</span><span>help: consider restricting type parameter `T` +</span><span> | +</span><span>1 | fn largest&lt;T: std::cmp::PartialOrd&gt;(list: &amp;[T]) -&gt; T { +</span><span> | ++++++++++++++++++++++ +</span></code></pre> +<p>Since <code>T</code> can be of absolutely any type now, the compiler cannot be sure that operator <code>&gt;</code> is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>We call this a <em>trait bound</em>, a way to provide constraints on what kind of types we are talking about in a given context. This implementation almost works now. Let's look at the new error.</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0508]: cannot move out of type `[T]`, a non-copy slice +</span><span> --&gt; src/main.rs:2:23 +</span><span> | +</span><span>2 | let mut largest = list[0]; +</span><span> | ^^^^^^^ +</span><span> | | +</span><span> | cannot move out of here +</span><span> | move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider borrowing here: `&amp;list[0]` +</span><span> +</span><span>error[E0507]: cannot move out of a shared reference +</span><span> --&gt; src/main.rs:4:18 +</span><span> | +</span><span>4 | for &amp;item in list { +</span><span> | ----- ^^^^ +</span><span> | || +</span><span> | |data moved here +</span><span> | |move occurs because `item` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider removing the `&amp;`: `item` +</span></code></pre> +<p>Our function attempts to take ownership, but, again, the compiler doesn't know whether <code>T</code> can just be trivially copied. Rust allows us to combine multiple trait bounds together:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/generic_largest.rs">generic_largest.rs</a>)</sub></p> +<h2 id="a-powerful-tool">A powerful tool</h2> +<p>There's a lot more that we can do with generics:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Debug; +</span><span> +</span><span style="color:#999999;">// generic enums +</span><span style="color:#8959a8;">enum </span><span>OurOption&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic structs +</span><span style="color:#8959a8;">struct </span><span>Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: U, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T, U&gt; Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T, </span><span style="color:#f5871f;">y</span><span>: U) -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> </span><span style="color:#8959a8;">Self </span><span>{ x, y } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Pair&lt;T&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: T, +</span><span>} +</span><span> +</span><span style="color:#999999;">// conditional implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt; Pair&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&gt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// alternative syntax +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Pair&lt;T&gt; +</span><span style="color:#8959a8;">where +</span><span> T: PartialOrd + Copy, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">smallest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&lt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Here information about the concrete underlying type is erased +</span><span style="color:#999999;">// We can only either format or clone the result +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">cloning_machine</span><span>(</span><span style="color:#f5871f;">item</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>(</span><span style="color:#f5871f;">impl Clone</span><span> + </span><span style="color:#f5871f;">Debug</span><span>)) -&gt; impl Clone </span><span style="color:#3e999f;">+</span><span> Debug { +</span><span> item.</span><span style="color:#4271ae;">clone</span><span>() +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> _opt </span><span style="color:#3e999f;">= </span><span>OurOption::Some(</span><span style="color:#f5871f;">10</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _p1 </span><span style="color:#3e999f;">=</span><span> Tuple2 { x: </span><span style="color:#f5871f;">5</span><span>, y: </span><span style="color:#f5871f;">10 </span><span>}; +</span><span> </span><span style="color:#8959a8;">let</span><span> _p2 </span><span style="color:#3e999f;">= </span><span>Tuple2::new(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2.5</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> </span><span style="color:#8959a8;">let</span><span> arr2 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">cloning_machine</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>arr); +</span><span> </span><span style="color:#999999;">// arr2[0]; // won&#39;t compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, arr2) +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/generics.rs">generics.rs</a>)</sub></p> +<p>A bit more involved example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::fmt::{Display, Formatter}; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>DefaultishablyPrintable&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">defaultish_print</span><span>() +</span><span> </span><span style="color:#8959a8;">where +</span><span> T: Display + Default, +</span><span> { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, T::default()) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Foo; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Bar; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#718c00;">&quot;this is a bar&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Default </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">default</span><span>() -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> Bar </span><span style="color:#999999;">// well, we have no other choice +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;Bar&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#3e999f;">&lt;</span><span>Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;</span><span style="color:#3e999f;">&gt;</span><span>::defaultish_print(); +</span><span> &lt;Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;Bar&gt;&gt;::defaultish_print(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/generics_fun.rs">generics_fun.rs</a>)</sub></p> +<h2 id="static-vs-dynamic-dispatch">Static vs dynamic dispatch</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>Speak { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Dog; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Dog { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hau hau&quot; </span><span style="color:#999999;">// it&#39;s a Polish dog! +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Human; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Human { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hello world&quot; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// It works like templates in C++ +</span><span style="color:#999999;">// A different function will be generated for each T during compilation +</span><span style="color:#999999;">// This process is called &quot;monomorphization&quot; +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">static_dispatch</span><span>&lt;T: Speak&gt;(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>T) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#999999;">// Only one copy of that function will exist in the compiled binary +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>dyn Speak) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> dog </span><span style="color:#3e999f;">=</span><span> Dog; +</span><span> </span><span style="color:#8959a8;">let</span><span> human </span><span style="color:#3e999f;">=</span><span> Human; +</span><span> +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#999999;">// The observable behavior is identical +</span><span> </span><span style="color:#999999;">// Static dispatch in general is a bit faster, +</span><span> </span><span style="color:#999999;">// because there is no need to perform a &quot;vtable lookup&quot;. +</span><span> </span><span style="color:#999999;">// But it can also result in bigger binary sizes. +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/static_dynamic_dispatch.rs">static_dynamic_dispatch.rs</a>)</sub></p> +<h1 id="lifetimes">Lifetimes</h1> +<p>Going back to the lesson about ownership, if we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); +</span><span>} +</span></code></pre> +<p>we should expect to get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `x` does not live long enough +</span><span> --&gt; src/main.rs:7:17 +</span><span> | +</span><span>7 | r = &amp;x; +</span><span> | ^^ borrowed value does not live long enough +</span><span>8 | } +</span><span> | - `x` dropped here while still borrowed +</span><span>9 | +</span><span>10 | println!(&quot;r: {}&quot;, r); +</span><span> | - borrow later used here +</span></code></pre> +<p>Courtesy of the borrow checker, we didn't end up with a dangling reference. But what exactly is happening behind the scenes? Rust introduces a concept of annotated lifetimes, where the lifetime of each value is being marked and tracked by the checker. Let's look at some examples:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; </span><span style="color:#999999;">// ---------+-- &#39;a +</span><span> </span><span style="color:#999999;">// | +</span><span> { </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// -+-- &#39;b | +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// | | +</span><span> } </span><span style="color:#999999;">// -+ | +</span><span> </span><span style="color:#999999;">// | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | +</span><span>} </span><span style="color:#999999;">// ---------+ +</span></code></pre> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// ----------+-- &#39;b +</span><span> </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// --+-- &#39;a | +</span><span> </span><span style="color:#999999;">// | | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | | +</span><span> </span><span style="color:#999999;">// --+ | +</span><span>} </span><span style="color:#999999;">// ----------+ +</span></code></pre> +<h2 id="annotations">Annotations</h2> +<p>Let's consider the following code finding the longer out of two strings:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;abcd&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;xyz&quot;</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span></code></pre> +<p>If we try to compile this, we will get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:9:33 +</span><span> | +</span><span>9 | fn longest(x: &amp;str, y: &amp;str) -&gt; &amp;str { +</span><span> | ---- ---- ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` +</span><span>help: consider introducing a named lifetime parameter +</span><span> | +</span><span>9 | fn longest&lt;&#39;a&gt;(x: &amp;&#39;a str, y: &amp;&#39;a str) -&gt; &amp;&#39;a str { +</span><span> | ++++ ++ ++ ++ +</span></code></pre> +<p>This is because Rust doesn't know which of the two provided strings (<code>x</code> or <code>y</code>) will be returned from the function. And because they potentially have different lifetimes, the lifetime of what we are returning remains unclear to the compiler - it needs our help.</p> +<p>Rust provides syntax for specifying lifetimes. The lifetime parameter name from the example (<code>a</code>) doesn't have any concrete meaning - it's just an arbitrary name for this one lifetime.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#999999;">// a reference +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a i32 </span><span style="color:#999999;">// a reference with an explicit lifetime +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a mut i32 </span><span style="color:#999999;">// a mutable reference with an explicit lifetime +</span></code></pre> +<p>So, knowing this, let's address the compiler's demands.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span></code></pre> +<p>When working with lifetimes, our work will usually revolve around specifying relationships between lifetimes of different values so that the compiler can successfully reason about the program's safety. In the context of the example above, this signature means that both of the function's arguments and its output will live at least as long as lifetime <code>'a</code>. In practice, this means that the output's lifetime will be equal to the smaller of the two inputs' lifetimes.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">first</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">second</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> first.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> second.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> first +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> second +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;long string is long&quot;</span><span>); +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;xyz&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2.</span><span style="color:#4271ae;">as_str</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// This doesn&#39;t compile - incorrect lifetimes +</span><span> </span><span style="color:#999999;">// +</span><span> </span><span style="color:#999999;">// let string1 = String::from(&quot;long string is long&quot;); +</span><span> </span><span style="color:#999999;">// let result; +</span><span> </span><span style="color:#999999;">// { +</span><span> </span><span style="color:#999999;">// let string2 = String::from(&quot;xyz&quot;); +</span><span> </span><span style="color:#999999;">// result = longest(string1.as_str(), string2.as_str()); +</span><span> </span><span style="color:#999999;">// } +</span><span> </span><span style="color:#999999;">// println!(&quot;The longest string is {}&quot;, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/lifetimes_basic.rs">lifetimes_basic.rs</a>)</sub></p> +<p>Trying to compile the second variant displeases the compiler (just like we hoped).</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `string2` does not live long enough +</span><span> --&gt; src/main.rs:6:44 +</span><span> | +</span><span>6 | result = longest(string1.as_str(), string2.as_str()); +</span><span> | ^^^^^^^^^^^^^^^^ borrowed value does not live long enough +</span><span>7 | } +</span><span> | - `string2` dropped here while still borrowed +</span><span>8 | println!(&quot;The longest string is {}&quot;, result); +</span><span> | ------ borrow later used here +</span></code></pre> +<h2 id="lifetime-elision">Lifetime elision</h2> +<p>We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this <em>lifetime elision</em>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span><span> </span><span style="color:#8959a8;">if</span><span> seq.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> seq +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">2</span><span>] +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> seq </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>]; +</span><span> +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;First two elements of the sequence: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, +</span><span> </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span>]) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/lifetimes_elision.rs">lifetimes_elision.rs</a>)</sub></p> +<p>The above works, even though we didn't specify any lifetime parameters at all. The reason lies in the rules we mentioned, which are as follows (where input lifetimes are lifetimes on parameters and output lifetimes are lifetimes on return values):</p> +<ul> +<li> +<p>Each parameter that is a reference gets its own lifetime parameter.</p> +</li> +<li> +<p>If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.</p> +</li> +<li> +<p>If there are multiple input lifetime parameters, but one of them is <code>&amp;self</code> or <code>&amp;mut self</code>, the lifetime of <code>self</code> is assigned to all output lifetime parameters.</p> +</li> +</ul> +<p>Let's try to understand how the compiler inferred the lifetimes of our <code>first_two</code> functions. We start with the following signature:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Then, we apply the first rule:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Next, we check the second rule. It applies here as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>With that, we arrive at a state where all lifetimes are specified.</p> +<h2 id="static-lifetime">Static lifetime</h2> +<p>There exists one special lifetime called <code>'static</code>, which means that a reference can live for the entire duration of the program. All string literals are annotated with this lifetime as they are stored directly in the program's binary. Full type annotation of a string literal in Rust is therefore as follows:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;I have a static lifetime.&quot;</span><span>; +</span></code></pre> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch10-00-generics.html">The Book, chapter 10</a></p> +</li> +<li> +<p><a href="https://oswalt.dev/2021/06/polymorphism-in-rust/">Polymorphism in Rust</a></p> +</li> +</ul> + + + + + Feedback #1 + 2022-10-31T00:00:00+00:00 + 2022-10-31T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/ + + <h2 id="feedback">Feedback</h2> +<h3 id="unwrapping">Unwrapping</h3> +<p>Instead of this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.favorite_color.</span><span style="color:#4271ae;">is_some</span><span>() { +</span><span> </span><span style="color:#c82829;">self</span><span>.favorite_color.</span><span style="color:#4271ae;">as_mut</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>().</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>do this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#8959a8;">ref mut</span><span> color) </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.favorite_color { +</span><span> color.</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>or</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(color) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.favorite_color { +</span><span> color.</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>(unwrapping is a code smell)</p> +<h3 id="spot-the-overflow">Spot the overflow</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; *</span><span>b </span><span style="color:#3e999f;">&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">+ *</span><span>g) </span><span style="color:#3e999f;">/ </span><span style="color:#f5871f;">2</span><span>, +</span></code></pre> +<h3 id="1-3">1/3</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">3 </span><span style="color:#3e999f;">&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">+ </span><span>(</span><span style="color:#3e999f;">*</span><span>g </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">+ </span><span>(</span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>), +</span></code></pre> +<p>No need to cast to u16. If b accounts for 1/3 of the sum, it's enough to check that it's bigger than both r and g.</p> +<h3 id="format">Format</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Named(</span><span style="color:#8959a8;">ref mut</span><span> name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;light &quot;</span><span>.</span><span style="color:#4271ae;">to_string</span><span>() </span><span style="color:#3e999f;">+</span><span> name, +</span></code></pre> +<p>There's a <code>format!</code> macro for this.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Named(</span><span style="color:#8959a8;">ref mut</span><span> name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span>format!(</span><span style="color:#718c00;">&quot;light </span><span style="color:#666969;">{name}</span><span style="color:#718c00;">&quot;</span><span>), +</span></code></pre> +<h3 id="from-vs-into-vs-as">From vs Into vs as</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>&lt;</span><span style="color:#8959a8;">u8 </span><span style="color:#3e999f;">as </span><span style="color:#c99e00;">Into</span><span>&lt;</span><span style="color:#8959a8;">u32</span><span>&gt;&gt;::into(</span><span style="color:#3e999f;">*</span><span>c) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>This could be written as</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#3e999f;">*</span><span>c).</span><span style="color:#4271ae;">into</span><span>() </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>or even simpler (note the omission of the type annotation):</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1 </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">u32</span><span>::from(</span><span style="color:#3e999f;">*</span><span>c) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>However in most cases of numeric conversion you can just use <code>as</code>:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1 </span><span style="color:#3e999f;">= *</span><span>c </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p><a href="https://doc.rust-lang.org/std/convert/trait.Into.html">Into trait docs</a></p> +<h3 id="saturating-addition">Saturating addition</h3> +<p>There's a <code>saturating_add</code> method on <code>u8</code> which does exactly what we wanted. +But it was fun watching you struggle with it :)</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">lighten</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#c82829;">self </span><span>{ +</span><span> Color::Named(name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;light &quot;</span><span>.</span><span style="color:#4271ae;">to_string</span><span>() </span><span style="color:#3e999f;">+</span><span> name, +</span><span> Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">=</span><span> r.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> </span><span style="color:#3e999f;">*</span><span>g </span><span style="color:#3e999f;">=</span><span> g.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> </span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">=</span><span> b.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="exchange">Exchange</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">exchange_items</span><span>(</span><span style="color:#f5871f;">robot1</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> Robot, </span><span style="color:#f5871f;">robot2</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> Robot) { +</span><span> mem::swap(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> robot1.held_item, </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> robot2.held_item); +</span><span>} +</span></code></pre> +<p>Swap is the preferred way to exchange the contents of two variables.</p> +<h3 id="regex-nope">Regex? Nope</h3> +<p>There's no need to use a regex here. String has a <code>contains</code> method.</p> +<p>If you <strong>really</strong> want to use a regex, +you can use the <code>lazy_static</code> crate to avoid recompiling the regex every time you call the function.</p> + + + + + Feedback #1 + 2022-10-31T00:00:00+00:00 + 2022-10-31T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/04-feedback-1/ + + <h2 id="feedback">Feedback</h2> +<h3 id="unwrapping">Unwrapping</h3> +<p>Instead of this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.favorite_color.</span><span style="color:#4271ae;">is_some</span><span>() { +</span><span> </span><span style="color:#c82829;">self</span><span>.favorite_color.</span><span style="color:#4271ae;">as_mut</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>().</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>do this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#8959a8;">ref mut</span><span> color) </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.favorite_color { +</span><span> color.</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>or</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(color) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.favorite_color { +</span><span> color.</span><span style="color:#4271ae;">lighten</span><span>(); +</span><span>} +</span></code></pre> +<p>(unwrapping is a code smell)</p> +<h3 id="spot-the-overflow">Spot the overflow</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; *</span><span>b </span><span style="color:#3e999f;">&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">+ *</span><span>g) </span><span style="color:#3e999f;">/ </span><span style="color:#f5871f;">2</span><span>, +</span></code></pre> +<h3 id="1-3">1/3</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">3 </span><span style="color:#3e999f;">&gt; </span><span>(</span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">+ </span><span>(</span><span style="color:#3e999f;">*</span><span>g </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>) </span><span style="color:#3e999f;">+ </span><span>(</span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u16</span><span>), +</span></code></pre> +<p>No need to cast to u16. If b accounts for 1/3 of the sum, it's enough to check that it's bigger than both r and g.</p> +<h3 id="format">Format</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Named(</span><span style="color:#8959a8;">ref mut</span><span> name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;light &quot;</span><span>.</span><span style="color:#4271ae;">to_string</span><span>() </span><span style="color:#3e999f;">+</span><span> name, +</span></code></pre> +<p>There's a <code>format!</code> macro for this.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Color::Named(</span><span style="color:#8959a8;">ref mut</span><span> name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span>format!(</span><span style="color:#718c00;">&quot;light </span><span style="color:#666969;">{name}</span><span style="color:#718c00;">&quot;</span><span>), +</span></code></pre> +<h3 id="from-vs-into-vs-as">From vs Into vs as</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>&lt;</span><span style="color:#8959a8;">u8 </span><span style="color:#3e999f;">as </span><span style="color:#c99e00;">Into</span><span>&lt;</span><span style="color:#8959a8;">u32</span><span>&gt;&gt;::into(</span><span style="color:#3e999f;">*</span><span>c) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>This could be written as</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span>(</span><span style="color:#3e999f;">*</span><span>c).</span><span style="color:#4271ae;">into</span><span>() </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>or even simpler (note the omission of the type annotation):</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1 </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">u32</span><span>::from(</span><span style="color:#3e999f;">*</span><span>c) </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p>However in most cases of numeric conversion you can just use <code>as</code>:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> tmp1 </span><span style="color:#3e999f;">= *</span><span>c </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>; +</span></code></pre> +<p><a href="https://doc.rust-lang.org/std/convert/trait.Into.html">Into trait docs</a></p> +<h3 id="saturating-addition">Saturating addition</h3> +<p>There's a <code>saturating_add</code> method on <code>u8</code> which does exactly what we wanted. +But it was fun watching you struggle with it :)</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">lighten</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#c82829;">self </span><span>{ +</span><span> Color::Named(name) </span><span style="color:#3e999f;">=&gt; *</span><span>name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;light &quot;</span><span>.</span><span style="color:#4271ae;">to_string</span><span>() </span><span style="color:#3e999f;">+</span><span> name, +</span><span> Color::Rgb(r, g, b) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>r </span><span style="color:#3e999f;">=</span><span> r.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> </span><span style="color:#3e999f;">*</span><span>g </span><span style="color:#3e999f;">=</span><span> g.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> </span><span style="color:#3e999f;">*</span><span>b </span><span style="color:#3e999f;">=</span><span> b.</span><span style="color:#4271ae;">saturating_add</span><span>(</span><span style="color:#f5871f;">10</span><span>); +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="exchange">Exchange</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">exchange_items</span><span>(</span><span style="color:#f5871f;">robot1</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> Robot, </span><span style="color:#f5871f;">robot2</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> Robot) { +</span><span> mem::swap(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> robot1.held_item, </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> robot2.held_item); +</span><span>} +</span></code></pre> +<p>Swap is the preferred way to exchange the contents of two variables.</p> +<h3 id="regex-nope">Regex? Nope</h3> +<p>There's no need to use a regex here. String has a <code>contains</code> method.</p> +<p>If you <strong>really</strong> want to use a regex, +you can use the <code>lazy_static</code> crate to avoid recompiling the regex every time you call the function.</p> + + + + + Data Types + 2022-10-24T00:00:00+00:00 + 2022-10-24T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/ + + <h2 id="aggregating-data">Aggregating data</h2> +<p>Below is a compact overview of Rust's structs</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Copy, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Position(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>); </span><span style="color:#999999;">// tuple struct +</span><span> +</span><span style="color:#999999;">// Could Hero derive the Copy trait? +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Hero { +</span><span> </span><span style="color:#c82829;">name</span><span>: String, +</span><span> </span><span style="color:#c82829;">level</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">experience</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">position</span><span>: Position, +</span><span>} +</span><span> +</span><span style="color:#999999;">// we can add methods to structs using the &#39;impl&#39; keyword +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// static method (in Rust nomenclature: &quot;associated function&quot;) +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">name</span><span>: String) -&gt; Hero { +</span><span> Hero { +</span><span> name, +</span><span> level: </span><span style="color:#f5871f;">1</span><span>, +</span><span> experience: </span><span style="color:#f5871f;">0</span><span>, +</span><span> position: Position(</span><span style="color:#f5871f;">0</span><span>, </span><span style="color:#f5871f;">0</span><span>), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// multiple impl blocks are possible for one struct +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// instance method, first argument (self) is the calling instance +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">distance</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">pos</span><span>: Position) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#999999;">// shorthand to: `self: &amp;Self` +</span><span> </span><span style="color:#999999;">// field `i` of a tuple or a tuple struct can be accessed through &#39;tuple.i&#39; +</span><span> (pos.</span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">0</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() </span><span style="color:#3e999f;">+ </span><span>(pos.</span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">1</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// mutable borrow of self allows to change instance fields +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">level_up</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#999999;">// shorthand to: `self: &amp;mut Self` +</span><span> </span><span style="color:#c82829;">self</span><span>.experience </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#c82829;">self</span><span>.level </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// &#39;self&#39; is not borrowed here and will be moved into the method +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">die</span><span>(</span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#999999;">// shorthand to: `self: Self` +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Here lies </span><span style="color:#666969;">{}</span><span style="color:#718c00;">, a hero who reached level </span><span style="color:#666969;">{}</span><span style="color:#718c00;">. RIP.&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.name, </span><span style="color:#c82829;">self</span><span>.level +</span><span> ); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Calling associated functions requires scope (`::`) operator. +</span><span> </span><span style="color:#8959a8;">let mut</span><span> hero: Hero </span><span style="color:#3e999f;">= </span><span>Hero::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Ferris&quot;</span><span>)); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); </span><span style="color:#999999;">// &#39;self&#39; is always passed implicitly +</span><span> +</span><span> </span><span style="color:#999999;">// fields other than &#39;name&#39; will be the same as in &#39;hero&#39; +</span><span> </span><span style="color:#8959a8;">let</span><span> steve </span><span style="color:#3e999f;">=</span><span> Hero { +</span><span> name: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Steve The Normal Guy&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">..</span><span>hero +</span><span> }; +</span><span> +</span><span> assert_eq!(hero.level, steve.level); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> twin </span><span style="color:#3e999f;">=</span><span> hero.</span><span style="color:#4271ae;">clone</span><span>(); +</span><span> +</span><span> </span><span style="color:#999999;">// we can compare Hero objects because it derives the PartialEq trait +</span><span> assert_eq!(hero, twin); +</span><span> twin.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_ne!(hero, twin); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_eq!(hero, twin); +</span><span> +</span><span> </span><span style="color:#999999;">// we can print out a the struct&#39;s debug string with &#39;{:?}&#39; +</span><span> println!(</span><span style="color:#718c00;">&quot;print to stdout: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, hero); +</span><span> +</span><span> hero.</span><span style="color:#4271ae;">die</span><span>(); </span><span style="color:#999999;">// &#39;hero&#39; is not usable after this invocation, see the method&#39;s definiton +</span><span> +</span><span> </span><span style="color:#999999;">// the dbg! macro prints debug strings to stderr along with file and line number +</span><span> </span><span style="color:#999999;">// dbg! takes its arguments by value, so better borrow them not to have them +</span><span> </span><span style="color:#999999;">// moved into dbg! and consumed. +</span><span> dbg!(</span><span style="color:#718c00;">&quot;print to stderr: {}&quot;</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>twin); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> pos </span><span style="color:#3e999f;">=</span><span> Position(</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">0</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> dist </span><span style="color:#3e999f;">=</span><span> steve.</span><span style="color:#4271ae;">distance</span><span>(pos); </span><span style="color:#999999;">// no clone here as Position derives the Copy trait +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, pos); +</span><span> assert_eq!(dist, </span><span style="color:#f5871f;">42</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/data_types.rs">data_types.rs</a>)</sub></p> +<h2 id="enums">Enums</h2> +<p>It is often the case that we want to define a variable that can only take +a certain set of values and the values are known up front. In C you can an <code>enum</code> for this.</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;stdio.h&gt; +</span><span> +</span><span style="color:#8959a8;">enum </span><span>shirt_size { +</span><span> small, +</span><span> medium, +</span><span> large, +</span><span> xlarge +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_size</span><span>(</span><span style="color:#8959a8;">enum</span><span> shirt_size </span><span style="color:#f5871f;">size</span><span>) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;my size is &quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">switch </span><span>(size) { +</span><span> </span><span style="color:#8959a8;">case</span><span> small: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;small&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> medium: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;medium&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> large: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;large&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> xlarge: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;xlarge&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">default</span><span>: +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;unknown&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">=</span><span> medium; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/enums.c">enums.c</a>)</sub></p> +<p>However, in C enums are just integers. Nothing prevents us from writing</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">666</span><span>; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span></code></pre> +<p>C++ introduces enum classes which are type-safe. Legacy enums are also somewhat safer than in C (same code as above):</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>&lt;source&gt;:27:31: error: invalid conversion from &#39;int&#39; to &#39;shirt_size&#39; [-fpermissive] +</span><span> 27 | enum shirt_size my_size = 666; +</span><span> | ^~~ +</span><span> | | +</span><span> | int +</span></code></pre> +<p>Some programming languages (especially functional ones) allow programmers to define +enums which carry additional information. Such types are usually called <code>tagged unions</code> +or <code>algebraic data types</code>.</p> +<p>In C++ we can use <code>union</code> with an <code>enum</code> tag to define it:</p> +<pre data-lang="cpp" style="background-color:#ffffff;color:#4d4d4c;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;iostream&gt; +</span><span> +</span><span style="color:#999999;">// Taken from: https://en.cppreference.com/w/cpp/language/union +</span><span> +</span><span style="color:#999999;">// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +</span><span style="color:#999999;">// and three variant members (c, i, d) +</span><span style="color:#8959a8;">struct </span><span>S +</span><span>{ +</span><span> </span><span style="color:#8959a8;">enum</span><span>{</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#c99e00;">INT</span><span>, DOUBLE} tag; +</span><span> </span><span style="color:#8959a8;">union +</span><span> { +</span><span> </span><span style="color:#8959a8;">char</span><span> c; +</span><span> </span><span style="color:#8959a8;">int</span><span> i; +</span><span> </span><span style="color:#8959a8;">double</span><span> d; +</span><span> }; +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_s</span><span>(</span><span style="color:#8959a8;">const</span><span> S</span><span style="color:#3e999f;">&amp; </span><span style="color:#f5871f;">s</span><span>) +</span><span>{ +</span><span> </span><span style="color:#8959a8;">switch</span><span>(s.</span><span style="color:#c82829;">tag</span><span>) +</span><span> { +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">CHAR</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">c </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">INT</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::DOUBLE: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">d </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() +</span><span>{ +</span><span> S s </span><span style="color:#3e999f;">= </span><span>{S::</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>}; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span> s.</span><span style="color:#c82829;">tag </span><span style="color:#3e999f;">=</span><span> S::</span><span style="color:#c99e00;">INT</span><span>; +</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">123</span><span>; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/tagged_union.cpp">tagged_union.cpp</a>)</sub></p> +<p>C++17 introduced a new feature called <code>variant</code> which generalizes this concept. +You can read more about it <a href="https://en.cppreference.com/w/cpp/utility/variant">here</a>.</p> +<p>Java has a more or less analogous feature called <code>sealed classes</code> +since <a href="https://docs.oracle.com/en/java/javase/17/language/sealed-classes-and-interfaces.html.">version 17</a>.</p> +<h2 id="enums-in-rust">Enums in Rust</h2> +<p>Let's see how they are defined in Rust.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>NamedSize { +</span><span> Small, +</span><span> Medium, +</span><span> Large, +</span><span> </span><span style="color:#666969;">XL</span><span>, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>ShirtSize { +</span><span> Named(NamedSize), +</span><span> Numeric(</span><span style="color:#8959a8;">u32</span><span>), +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Isn&#39;t it strange that some clothes&#39; sizes are adjectives like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">,&quot;</span><span>, +</span><span> ShirtSize::Named(NamedSize::Small) +</span><span> ); +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;but sometimes they are numbers like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">?&quot;</span><span>, +</span><span> ShirtSize::Numeric(</span><span style="color:#f5871f;">42</span><span>) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/enums.rs">enums.rs</a>)</sub></p> +<p>In Rust, enums are a core feature of the language. +You may have heard that one of Rust's defining characteristics is +the absence of <a href="https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions">&quot;the billion dollar mistake&quot;</a>. +So what can we do to say that a value is missing if there is no <code>null</code>?</p> +<p>In Rust, we can use the <code>Option</code> type to represent the absence of a value.</p> +<p>Option is defined as:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">enum </span><span>Option&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span></code></pre> +<p>The <code>&lt;T&gt;</code> part is called the &quot;type parameter&quot; and it causes Option to be generic. +We won't go deeper into this for now.</p> +<p>The fact that variables which could be <code>null</code> in other languages have a different type in Rust is +the solution to the billion dollar mistake!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> not_null: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> not_null </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">43</span><span>; +</span><span> </span><span style="color:#999999;">// not_null = None; // this won&#39;t compile because it&#39;s a different type! +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">43</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// such construction is rare, but possible +</span><span> </span><span style="color:#8959a8;">let mut</span><span> double_nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// assert_ne!(double_nullable, Some(42)); // this won&#39;t even compile because it&#39;s a different type! +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// None and Some(None) are different! +</span><span> assert_ne!(double_nullable, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now recall that division by 0 *panics* +</span><span> </span><span style="color:#999999;">// A panic is an unrecoverable error +</span><span> </span><span style="color:#999999;">// It is not an exception! +</span><span> </span><span style="color:#999999;">// And in Rust there are no exceptions, so there are no try/catch blocks +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine that we want to divide one number by another +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> dividend </span><span style="color:#3e999f;">/</span><span> divisor +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We get the divisor from the user, so it can be 0 +</span><span> </span><span style="color:#999999;">// We want to handle this situation gracefully - we don&#39;t want to crash the program! +</span><span> </span><span style="color:#999999;">// We can do this by using the Option&lt;T&gt; type +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">safe_divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">if</span><span> divisor </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#c99e00;">None +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c99e00;">Some</span><span>(dividend </span><span style="color:#3e999f;">/</span><span> divisor) +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Fortunately, such a function is already included in the standard library +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> </span><span style="color:#999999;">// We need to specify the type explicitly +</span><span> </span><span style="color:#999999;">// because checked_div is implemented for all integer types +</span><span> </span><span style="color:#999999;">// and Rust won&#39;t know which type we want to use +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">21</span><span>)); +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine we search for a value in an array. +</span><span> </span><span style="color:#8959a8;">let</span><span> numbers </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>, </span><span style="color:#f5871f;">5</span><span>]; +</span><span> </span><span style="color:#8959a8;">let</span><span> three </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(three, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> </span><span style="color:#8959a8;">let</span><span> seven </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">7</span><span>); +</span><span> assert_eq!(seven, </span><span style="color:#c99e00;">None</span><span>); +</span><span> </span><span style="color:#999999;">// We won&#39;t delve deeper into the details of how iterators work for now, +</span><span> </span><span style="color:#999999;">// but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust +</span><span> +</span><span> </span><span style="color:#999999;">// Usually there are two kinds of methods: +</span><span> </span><span style="color:#999999;">// ones that will panic if the argument is incorrect, +</span><span> </span><span style="color:#999999;">// numbers[8]; // this will panic! +</span><span> </span><span style="color:#999999;">// and `checked` ones that return an Option +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// We can use `unwrap` to get the value out of an Option +</span><span> </span><span style="color:#999999;">// but we must be absolutely sure that the Option is Some, otherwise we&#39;ll get a panic +</span><span> </span><span style="color:#999999;">// numbers.get(8).unwrap(); // this will panic! +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>).</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">unwrap_or</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#f5871f;">0</span><span>); </span><span style="color:#999999;">// or we can provide a default value +</span><span> +</span><span> </span><span style="color:#999999;">// Usually instead of unwrapping we use pattern matching, we&#39;ll get to this in a minute +</span><span> </span><span style="color:#999999;">// but first let&#39;s see what else we can do with an option +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> </span><span style="color:#999999;">// We can use `map` to transform the value inside an Option +</span><span> </span><span style="color:#8959a8;">let</span><span> doubled </span><span style="color:#3e999f;">=</span><span> number.</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>); +</span><span> assert_eq!(doubled, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">84</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use flatten to reduce one level of nesting +</span><span> </span><span style="color:#8959a8;">let</span><span> nested </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> assert_eq!(nested.</span><span style="color:#4271ae;">flatten</span><span>(), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use `and_then` to chain multiple options +</span><span> </span><span style="color:#999999;">// This operation is called `flatmap` in some languages +</span><span> </span><span style="color:#8959a8;">let</span><span> chained </span><span style="color:#3e999f;">=</span><span> number +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>)) +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(chained, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// The last two things we&#39;ll cover here are `take` and `replace` +</span><span> </span><span style="color:#999999;">// They are important when dealing with non-Copy types +</span><span> </span><span style="color:#999999;">// `take` will return the value inside an Option and leave a None in its place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> option: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#999999;">// Again, we need to specify the type +</span><span> </span><span style="color:#999999;">// Even though we want to say that there is no value inside the Option, +</span><span> </span><span style="color:#999999;">// this absent value must have a concrete type! +</span><span> assert_eq!(option.</span><span style="color:#4271ae;">take</span><span>(), </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(option, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> y </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(y, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// `replace` can be used to swap the value inside an Option +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">None</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/option.rs">option.rs</a>)</sub></p> +<h2 id="pattern-matching">Pattern matching</h2> +<p>Pattern matching is a powerful feature of Rust and many functional languages, but it's slowly making +its way into imperative languages like Java and Python as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Pattern matching is basically a switch on steroids. +</span><span> </span><span style="color:#8959a8;">let</span><span> number </span><span style="color:#3e999f;">= </span><span>rand::random::&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;(); +</span><span> </span><span style="color:#8959a8;">match</span><span> number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7 </span><span>{ +</span><span> </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is *almost* divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is not divisible by 7&quot;</span><span>), +</span><span> } +</span><span> +</span><span> #[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span> </span><span style="color:#8959a8;">enum </span><span>Color { +</span><span> Pink, +</span><span> Brown, +</span><span> Lime, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> color </span><span style="color:#3e999f;">= </span><span>Color::Lime; +</span><span> </span><span style="color:#8959a8;">match</span><span> color { +</span><span> Color::Pink </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color!&quot;</span><span>), </span><span style="color:#999999;">// _ is a wildcard +</span><span> </span><span style="color:#999999;">// Rust will statically check that we covered all cases or included a default case. +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can also use pattern matching to match on multiple values. +</span><span> </span><span style="color:#8959a8;">match </span><span>(color, number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7</span><span>) { +</span><span> (Color::Pink, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color and number!&quot;</span><span>), +</span><span> (Color::Pink, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite number!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color or number!&quot;</span><span>), +</span><span> } +</span><span> </span><span style="color:#999999;">// (This is not special syntax, we&#39;re just pattern matching tuples.) +</span><span> +</span><span> </span><span style="color:#999999;">// But we can also *destructure* the value +</span><span> </span><span style="color:#8959a8;">struct </span><span>Human { +</span><span> </span><span style="color:#c82829;">age</span><span>: </span><span style="color:#8959a8;">u8</span><span>, +</span><span> </span><span style="color:#c82829;">favorite_color</span><span>: Color, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> john </span><span style="color:#3e999f;">=</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> }; +</span><span> +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#3e999f;">&amp;</span><span>john { +</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Okay, that&#39;s John!&quot;</span><span>), +</span><span> Human { +</span><span> favorite_color: Color::Pink, +</span><span> </span><span style="color:#3e999f;">.. +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not John, but still his favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Somebody else?&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Note two things: +</span><span> </span><span style="color:#999999;">// 1. Color is *not* Eq, so we can&#39;t use == to compare it, but pattern matching is fine. +</span><span> </span><span style="color:#999999;">// 2. We *borrowed* the value, so we can use it after the match. +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;John is </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> years old and still kicking!&quot;</span><span>, john.age); +</span><span> +</span><span> </span><span style="color:#999999;">// To save some time, we can use `if let` to match against only one thing +</span><span> </span><span style="color:#999999;">// We could also use `while let ... {}` in the same way +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;He&#39;s also a man of great taste&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can match ranges... +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old!&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use match and capture the value at the same time. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use guards to check for multiple conditions. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">12</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *odd* teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *even* man, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is normal&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Finally, let&#39;s look at some references now +</span><span> </span><span style="color:#8959a8;">let</span><span> reference: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= &amp;</span><span style="color:#f5871f;">4</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">match</span><span> reference { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>val </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Value under reference is: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, val), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// `ref` can be used to create a reference when destructuring +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// `john` is still valid, because we borrowed using `ref` +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;John still has his color - </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">!&quot;</span><span>, favorite_color); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> john </span><span style="color:#3e999f;">=</span><span> john; +</span><span> +</span><span> </span><span style="color:#999999;">// `ref mut` borrows mutably +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref mut</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// We use `*` to dereference +</span><span> </span><span style="color:#3e999f;">*</span><span>favorite_color </span><span style="color:#3e999f;">= </span><span>Color::Brown; +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Tastes do change with time and John likes </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;"> now.&quot;</span><span>, +</span><span> john.favorite_color +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/pattern_matching.rs">pattern_matching.rs</a>)</sub></p> +<h2 id="result">Result</h2> +<p>We said there are no exceptions in Rust and panics mean errors which cannot be caught. +So how do we handle situations which can fail? That's where the <code>Result</code> type comes in.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fs::File; +</span><span style="color:#8959a8;">use </span><span>std::io; +</span><span style="color:#8959a8;">use </span><span>std::io::Read; +</span><span> +</span><span style="color:#999999;">// Let&#39;s try reading from a file. +</span><span style="color:#999999;">// Obviously this can fail. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_try</span><span>() -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(</span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">match</span><span> file { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#8959a8;">mut</span><span> file) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#999999;">// We got a file! +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> </span><span style="color:#999999;">// Matching each result quickly become tedious... +</span><span> </span><span style="color:#8959a8;">match</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c99e00;">Err</span><span>(error), +</span><span> } +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#999999;">// This is needed in order to change the type from `io::Result&lt;File&gt;` to `io::Result&lt;()&gt;` +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// The &#39;?&#39; operator allows us to return early in case of an error +</span><span style="color:#999999;">// (it automatically converts the error type) +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">second_try</span><span>(</span><span style="color:#f5871f;">filename</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>) -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(filename)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> filenames </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/null&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/cpu&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/fuse&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;there_certainly_is_no_such_file&quot;</span><span>, +</span><span> ]; +</span><span> </span><span style="color:#8959a8;">for</span><span> filename </span><span style="color:#3e999f;">in</span><span> filenames { +</span><span> println!(</span><span style="color:#718c00;">&quot;Trying to read from &#39;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&#39;&quot;</span><span>, filename); +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#4271ae;">second_try</span><span>(filename) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, gibberish), +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Error: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, error), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/result.rs">result.rs</a>)</sub></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li>The Book, chapters <a href="https://doc.rust-lang.org/book/ch05-00-structs.html">5</a>, +<a href="https://doc.rust-lang.org/stable/book/ch06-00-enums.html">6</a>, +<a href="https://doc.rust-lang.org/stable/book/ch08-00-common-collections.html">8</a> +and <a href="https://doc.rust-lang.org/stable/book/ch09-00-error-handling.html">9</a></li> +<li><a href="https://doc.rust-lang.org/std/option/">Option docs</a></li> +<li><a href="https://doc.rust-lang.org/std/result/">Result docs</a></li> +</ul> +<h2 id="assignment-1-graded">Assignment #1 (graded)</h2> +<p>(TBA)</p> + + + + + Ownership Model + 2022-10-17T00:00:00+00:00 + 2022-10-17T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/02-ownership/ + + <h2 id="why-all-the-fuss">Why all the fuss?</h2> +<p>Even if you've never seen Rust code before, chances are you still heard the term <em>borrow checker</em> or something about Rust's ownership. Indeed, Rust's ownership model lies at the very core of its uniqueness. But to fully understand it and appreciate it, let's first take a look at how memory management is handled in most popular languages.</p> +<ul> +<li> +<p><strong>Garbage Collection</strong> - in many high-level programming languages, like Java, Haskell or Python, memory management is done fully by the language, relieving the programmer from this burden. This prevents memory leaks and memory related errors (like <em>use after free</em>), but does come at a cost - there is a runtime overhead, both memory and performance wise, caused by the constantly running garbage collection algorithms and the programmer usually has very little control over when the garbage collection takes place.</p> +</li> +<li> +<p><strong>Mind your own memory</strong> - in low-level languages and specific ones like C++, performance comes first so we cannot really afford to run expansive bookkeeping and cleaning algorithms. Most of these languages compile directly to machine code and have no language-specific runtime environment. That means that the only place where memory management can happen is in the produced code. While compilers insert these construction and destruction calls for stack allocated memory, it generally requires a lot of discipline from the programmer to adhere to good practices and patterns to avoid as many memory related issues as possible and one such bug can be quite deadly to the program and a nightmare to find and fix. These languages basically live by the <em>&quot;your memory, your problem&quot;</em> mantra.</p> +</li> +</ul> +<p>And then we have Rust. Rust is a systems programming language and in many ways it's akin to C++ - it's basically low-level with many high-level additions. But unlike C++, it doesn't exactly fall into either of the categories described above, though it's way closer to the second one. It performs no additional management at runtime, but instead imposes a set of rules on the code, making it easier to reason about and thus check for its safety and correctness at compile time - these rules make up Rust's <strong>ownership model</strong>.</p> +<p>In a way, programming in Rust is like pair-programming with a patient and very experienced partner. Rust's compiler will make sure you follow all the good patterns and practices (by having them ingrained in the language itself) and very often even tell you how to fix the issues it finds.</p> +<p><em><strong>Disclaimer:</strong> when delving deeper into Rust below we will make heavy use of concepts like scopes, moving data, stack and heap, which should have been introduced as part of the JNP1 C++ course. If you need a refresher of any of these, it's best to do so now, before reading further.</em></p> +<h2 id="start-with-the-basics-ownership">Start with the basics - ownership</h2> +<p>In the paragraph above we mentioned a set of rules that comprise Rust's ownership model. <a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules">The book</a> starts off with the following three as its very foundation:</p> +<ol> +<li> +<p>Each value in Rust is tied to a specific variable - we call that variable its <strong>owner</strong>.</p> +</li> +<li> +<p>There can only be one owner at a time.</p> +</li> +<li> +<p>When the owner goes out of scope, the value will be destroyed (or in Rust terms - <em>dropped</em>).</p> +</li> +</ol> +<p>The third point might make you think about C++ and its automatic storage duration. We will later see that, while very similar at first, Rust expands on these mechanics quite a bit. The following code illustrates the basic version of this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> a: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// allocation on the stack, &#39;a&#39; becomes an owner +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;a&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;a&#39;, the owner, goes out of scope and the value is dropped +</span></code></pre> +<p>So far, so good. Variables are pushed onto the stack when they enter the scope and destroyed during stack unwinding that happens upon leaving their scope. However, allocating and deallocating simple integers doesn't impress anybody. Let's try something more complex:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is allocated on the stack, while its contents (&quot;a string&quot;) +</span><span> </span><span style="color:#999999;">// are allocated on the heap. &#39;s&#39; is the owner of this String object. +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;s&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;s&#39;, the owner, goes out of scope and the String is dropped, its heap allocated memory freed +</span></code></pre> +<p>If you recall the RAII (Resource Acquisition Is Initialization) pattern from C++, the above is basically the same thing. We go two for two now in the similarity department, so... is Rust really any different then? There is a part of these examples that we skipped over - actually doing something with the values.</p> +<h2 id="moving-around-is-fun">Moving around is fun</h2> +<p>Let's expand on the last example. The scoping is not really important for that one, so we don't include it here.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// same thing, &#39;s&#39; is now an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s; </span><span style="color:#999999;">// easy, &#39;s2&#39; becomes another owner... right? +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// this doesn&#39;t work, can you guess why? +</span></code></pre> +<p>At first glance everything looks great. If we write this code (well, an equivalent of it) in basically any other popular language, it will compile no issue - but it does not here and there's a good reason why.</p> +<p>To understand what's happening, we have to consult the rules again, rule 2 in particular. It says that there can only be one owner of any value at a given time. So, <code>s</code> and <code>s2</code> cannot own the same object. Okay, makes sense, but what is happening in this line then - <code>let s2 = s;</code>? Experience probably tells you that <code>s</code> just gets copied into <code>s2</code>, creating a new String object. That would result in each variable owning its very own instance of the string and each instance having exactly one owner. Sounds like everyone should be happy now, but wait - in that case the last line should work no issue, right? But it doesn't, so can't be a copy. Let's see now what the compiler actually has to say:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0382]: borrow of moved value: `s` +</span><span> --&gt; src/main.rs:6:42 +</span><span> | +</span><span>2 | let s = String::from(&quot;a string&quot;); +</span><span> | - move occurs because `s` has type `String`, which does not implement the `Copy` trait +</span><span>3 | +</span><span>4 | let s2 = s; +</span><span> | - value moved here +</span><span>5 | +</span><span>6 | println!(&quot;And the contents are: {}&quot;, s); +</span><span> | ^ value borrowed here after move +</span></code></pre> +<p><em>&quot;value moved here&quot;</em> - gotcha! So <code>s</code> is being moved to <code>s2</code>, which also means that <code>s2</code> now becomes the new owner of the string being moved and <code>s</code> cannot be used anymore. In Rust, the default method of passing values around is by move, not by copy. While it may sound a bit odd at first, it actually has some very interesting implications. But before we get to them, let's fix our code, so it compiles now. To do so, we have to explicitly tell Rust to make a copy by invoking the <code>clone</code> method:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">clone</span><span>(); </span><span style="color:#999999;">// &#39;s2&#39; now contains its own copy +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// success! +</span></code></pre> +<p>The compiler is happy now and so are we. The implicit move takes some getting used to, but the compiler is here to help us. Now, let's put the good, old C++ on the table again and compare the two lines:</p> +<div style="text-align: center"> +<p><code>let s2 = s;</code> is equivalent to <code>auto s2 = std::move(s);</code></p> +<p><code>let s2 = s.clone()</code> is equivalent to <code>auto s2 = s</code></p> +</div> +<p>There are a few important things to note here:</p> +<ul> +<li> +<p>Making a copy is oftentimes not cheap. Memory needs to be allocated and copied, and a call to the system has to be made. We should prefer to move things as much as possible to avoid this cost - in C++ we have a myriad of language features like <code>std::move</code> and <em>r-references</em> to achieve this. Every programmer worth their salt needs to be well versed in all of them to write efficient C++ code and simply forgetting one move can lead to significant performance loss (and this happens to even the most senior devs ever existing, let's not pretend). On the contrary, in Rust you need to make an effort to make a copy and that makes you very aware of the cost you're paying - something that we'll see quite a lot of in the language. Also, if you forget a clone there's no harm done - it just won't compile!</p> +</li> +<li> +<p>Hidden in all of this is another nice thing Rust gives us. In C++, nothing prevents you from using variables after they've been moved from, leading to unexpected errors in a more complex code. In Rust, that variable (in our case <code>s</code>) simply becomes invalid and the compiler gives us a nice error about it.</p> +</li> +</ul> +<h3 id="but-what-about-ints">But what about ints?</h3> +<p>A good question to ask. Copying primitives is cheap. And it's not convenient for the programmer to have to always write <code>.clone()</code> after every primitive. If we take a look at the error from the previous example:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>move occurs because `s` has type `String`, which does not implement the `Copy` trait` +</span></code></pre> +<p>It says that <code>s</code> was moved because the <code>String</code> type doesn't have the <code>Copy</code> trait. We will talk about traits more in depth in the future lessons, but what this basically means is that <code>String</code> is not specified to be copied by default. All primitive types (<code>i32</code>, <code>bool</code>, <code>f64</code>, <code>char</code>, etc.) and tuples consisting only of primitive types implement the <code>Copy</code> trait.</p> +<h3 id="exercise">Exercise</h3> +<p>How to fix that code?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); +</span><span>} +</span></code></pre> +<h2 id="let-s-borrow-some-books">Let's borrow some books</h2> +<p>We now know how to move things around and how to clone them if moving is not possible. But what if making a copy is unnecessary - maybe we just want to let someone look at our resource and keep on holding onto it once their done. Consider the following example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(book.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Cloning is pretty excessive here. Imagine recommending a book to your friend and instead of lending it to them for the weekend, you scan it and print an exact copy. Not the best way to go about it, is it? Thankfully, Rust allows us to access a resource without becoming an owner through the use of references and the <code>&amp;</code> operator. This is called a borrow.</p> +<p>The adjusted code should look like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>book); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>As with everything, references are too, by default, immutable, which means that the <code>read_book</code> function is not able to modify that book passed into it. We can also borrow something mutably by specifying it both in the receiving function signature and the place it gets called. Maybe you want to have your book signed by its author?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">push_str</span><span>(</span><span style="color:#718c00;">&quot; ~ Arthur Author&quot;</span><span>); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// note that the book has to be marked as mutable in the first place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// it&#39;s always clear when a parameter might get modified +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); </span><span style="color:#999999;">// book is now signed +</span><span>} +</span></code></pre> +<p>Pretty neat, but doesn't seem that safe right now. Let's try to surprise our friend:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">clear</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>book; </span><span style="color:#999999;">// an immutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// a mutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(r); </span><span style="color:#999999;">// would be pretty sad to open a blank book when it was not +</span><span> </span><span style="color:#999999;">// what we borrowed initially +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Fortunately for us (and our poor friend just wanting to read), the compiler steps in and doesn't let us do that, printing the following message:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0502]: cannot borrow `book` as mutable because it is also borrowed as immutable +</span><span> --&gt; src/main.rs:14:14 +</span><span> | +</span><span>12 | let r = &amp;book; // an immutable borrow +</span><span> | ----- immutable borrow occurs here +</span><span>13 | +</span><span>14 | erase_book(&amp;mut book); // a mutable borrow +</span><span> | ^^^^^^^^^ mutable borrow occurs here +</span><span>15 | +</span><span>16 | read_book(r); // would be pretty sad to open a blank book when it was not +</span><span> | - immutable borrow later used here +</span></code></pre> +<p>This is where the famous borrow checker comes in. To keep things super safe, Rust clearly states what can and cannot be done with references and tracks their lifetimes. Exactly one of the following is always true for references to a given resource:</p> +<ul> +<li> +<p>There exists only one mutable reference and no immutable references, <strong>or</strong></p> +</li> +<li> +<p>There is any number of immutable references and no mutable ones.</p> +</li> +</ul> +<p>You may notice a parallel to the <em>readers - writers</em> problem from concurrent programming. In fact, the way Rust's borrow checker is designed lends itself incredibly well to preventing data race related issues.</p> +<h3 id="dangling-references">Dangling references</h3> +<p>Rust also checks for dangling references. If we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> reference_to_nothing </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">dangle</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dangle</span><span>() -&gt; </span><span style="color:#3e999f;">&amp;</span><span>String { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;hello&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>s +</span><span>} +</span></code></pre> +<p>we will get an adequate error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:5:16 +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;String { +</span><span> | ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but there is no value for it to be borrowed from +</span><span>help: consider using the `&#39;static` lifetime +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;&#39;static String { +</span><span> | ^^^^^^^^ +</span></code></pre> +<p>The message above suggests specifing a lifetime for the returned string. In Rust, the lifetime of each variable is also a part of its type, but we will talk more about it later.</p> +<h3 id="exercise-1">Exercise</h3> +<p>Our previous solution using <code>clone()</code> was pretty inefficient. How should this code look now?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); </span><span style="color:#999999;">// we could&#39;ve ommitted the clone() here. Why? +</span><span>} +</span></code></pre> +<h2 id="everyone-gets-a-slice">Everyone gets a slice</h2> +<p>The last part of working with references that we will cover in this lesson are slices. A <em>slice</em> in Rust is a view over continuous data. Let us start with a string slice - the <code>&amp;str</code> type.</p> +<p><em><strong>Note:</strong> for the purposes of these examples we assume we are working with ASCII strings. More comprehensive articles on handling strings are linked at the end of this lesson.</em></p> +<p>To create a string slice from the <code>String</code> object <code>s</code>, we can simply write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// creates a slice of length 2, starting with the character at index 1 +</span></code></pre> +<p>This makes use of the <code>&amp;</code> operator and Rust's range notation to specify the beginning and end of the slice. Thus, we can also write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">2</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// everything from index 2 till the end +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">1</span><span>]; </span><span style="color:#999999;">// only the first byte +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// the whole string as a slice +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">as_str</span><span>(); </span><span style="color:#999999;">// also the whole string +</span></code></pre> +<p>You might have noticed that we always built <code>String</code> values using the <code>from()</code> method and never actually used the string literals directly. What type is a string literal then? Turns out it's the new string slice we just learned about!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;string literal&quot;</span><span>; +</span></code></pre> +<p>In fact, it makes a lot sense - string literals, after all, are not allocated on the heap, but rather placed in a special section of the resulting binary. It's only natural we just reference that place with a slice.</p> +<p>Slices can also be taken from arrays:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> array: [</span><span style="color:#8959a8;">i32</span><span>; </span><span style="color:#f5871f;">4</span><span>] </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">10</span><span>, </span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#f5871f;">2</span><span>]; </span><span style="color:#999999;">// creates an array of four 32 bit integers +</span><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>] </span><span style="color:#3e999f;">= &amp;</span><span>array[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// results in a slice [10, 5] +</span></code></pre> +<h3 id="exercise-2">Exercise</h3> +<p>Can this code still be improved from the previous version utilizing references? Think about the signature of <code>count_animals</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span>} +</span></code></pre> +<h3 id="further-reading">Further reading</h3> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/std/primitive.char.html">Char documentation</a></p> +</li> +<li> +<p><a href="https://fasterthanli.me/articles/working-with-strings-in-rust">Working with strings in Rust</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html">The Book, chapter 4</a></p> +</li> +</ul> + + + + + 2021L Project showcase + 2022-10-17T00:00:00+00:00 + 2022-10-17T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/project-showcase/ + + <h1 id="some-projects-from-the-previous-semester">(Some) projects from the previous semester</h1> +<h2 id="chat-app">Chat App</h2> +<p><a href="https://github.com/barteksad/Chat-App">GitHub</a></p> +<p>Bartłomiej Sadlej (GitHub: @barteksad email: sadlejbartek@gmail.com)</p> +<p>&quot;Chat app written in Rust with Postgress db. Allows users to create new accounts and channels and communicate within channel. All unseed messages from last login are delivered once logged in again.</p> +<p>By default server stats with one user named ADMIN with password ADMIN&quot;</p> +<p>tokio, serde, anyhow, thiserror, dashmap</p> +<h2 id="algorithm-visualizer">Algorithm Visualizer</h2> +<p><a href="https://github.com/algorithm-visualzier/algorithm-visualizer">GitHub</a></p> +<p>Mikołaj Piróg (mikolajpirog@gmail.com, GitHub: @aetn23), Mikołaj Wasiak (wasiak.mikolaj1@gmail.com, GitHub: @RudyMis)</p> +<p>Graph editor with algorithms visualization. Create and modify the graph using GUI, move it around using WSAD. To see an algorithm being run, simply click on a node, and select the desired algorithm. See video and/or GitHub page for further details.</p> +<p>Petgraph, egui-tetra, dyn_partial_eq</p> +<video src="/lessons/project-showcase/graph_vis_demo.h264" controls width="320" height="240"> +</video> +<h2 id="rustal-combat">Rustal Combat</h2> +<p><a href="https://github.com/Emilo77/RUSTAL-COMBAT">GitHub</a></p> +<p>Kamil Bugała (GiHub: @Emilo77)</p> +<p>Simple game in the style of Mortal Combat. Two players fight each other by using dashes. It is the 1 v 1 version, so far it is possible to play on a one computer.</p> +<p>Bevy</p> +<h2 id="rusty-dungeon">Rusty dungeon</h2> +<p><a href="https://github.com/Rusty-Studios/rusty-dungeon">GitHub</a></p> +<p>Barbara Rosiak (GitHub: @barosiak, email basiarosiak.7@gmail.com), Tomasz Kubica (GitHub: @Tomasz-Kubica, email: tomaszkubica4@gmail.com), Dawid Mędrek (GitHub: @dawidmd)</p> +<p>A 2D game written using Rust and Bevy Game Engine, in which the player has to shoot and defeat enemies on their way to the final boss, inspired by the Binding of Isaac.</p> +<p>Bevy</p> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/project-showcase/rusty-dungeon.png" alt="" /></p> +<h2 id="mariomim">MarioMIM</h2> +<p><a href="https://github.com/KatKlo/rust-MarioMIM">GitHub</a></p> +<p>Katarzyna Kloc (GitHub: @KatKlo, email: kk429317@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/katarzyna-kloc-7a7503209/), +Patryk Bundyra (GitHub: PBundyra, email: pb429159@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/pbundyra/)</p> +<p>Since the moment CLion has shown us the first segfaults, we wanted to create a computer game inspired by the student’s adventure of pursuing a Computer Science degree. MarioMIM is a platform game whose main goal is to... get a degree. We’ve implemented a game inspired by Super Mario Bros, but in the special University of Warsaw edition. In order to overcome bugs, the student can strengthen himself by drinking coffee or learning the best programming language in the world - Rust.</p> +<p>Bevy, Rapier, Kira</p> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/project-showcase/fail-menu.png" alt="" /> +<img src="https://mimuw-jnp2-rust.github.io/lessons/project-showcase/game.png" alt="" /></p> +<h2 id="sendino">Sendino</h2> +<p><a href="https://github.com/grzenow4/project-sendino">GitHub</a></p> +<p>Grzegorz Nowakowski (Github: @grzenow4, email: g.nowakowski@student.uw.edu.pl) with @izmael7 on Github</p> +<p>One room chat application with client-server model of communication. Many users can communicate at one time.</p> +<p>Tokio, serde, crossterm</p> +<h2 id="chatter">Chatter</h2> +<p><a href="https://github.com/kfernandez31/JNP2-Chatter">GitHub</a></p> +<p>Kacper Kramarz-Fernandez (GitHub: @kfernandez31, email: kacper.fernandez@gmail.com), +Jan Zembowicz (GitHub: @JWZ1996, email: janzembowicz@gmail.com)</p> +<p>Chatter is a simple multi-room command-line chat application that uses a two-protocol (HTTP + WS) communication style for high-efficiency.</p> +<p>Tokio, Warp, Serde, Hyper, among others</p> + + + + + Oragnizational lesson + 2022-10-10T00:00:00+00:00 + 2022-10-10T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/00-organizational/ + + <h1 id="jnp-3-rust">JNP 3: Rust</h1> +<p>We will be using <a href="https://classroom.github.com">Github Classroom</a> for task submission and <a href="https://discord.gg/j2JFAsj7">Discord</a> for discussions.</p> +<p>Our main learning/teaching resource will be <a href="https://doc.rust-lang.org/stable/book/">&quot;The Book&quot;</a>.</p> +<h2 id="grading">Grading</h2> +<ul> +<li>1/3 of the grade is based on small tasks. There will be approximately 1 task every two weeks and each task will be graded on a scale of 0 to 3.</li> +<li>2/3 of the grade is based on a big project. You can choose a topic yourself, but it must be accepted by me. The project has to be split into two parts. It can be done in groups of two (or bigger).</li> +<li>The grade may be increased by a bonus. You can get a bonus for: +<ul> +<li>Making a presentation about some advanced topic (const generics, futures, macros, etc.) or about architecture of a selected Rust open-source library</li> +<li>Contributing to a selected Rust open-source library</li> +<li>Contributing to this course's materials</li> +<li>Quizzes, homeworks, etc.</li> +</ul> +</li> +</ul> +<h2 id="project-deadlines">Project Deadlines</h2> +<ol> +<li>2022-11-11: Project ideas should be presented to me for further refining. If you wish to pair up with someone, now is the time to tell me.</li> +<li>2022-11-18: Final project ideas should be accepted by now.</li> +<li>2022-12-16: Deadline for submitting the first part of the project.</li> +<li>2023-01-13: Deadline for submitting the second and final part of the project.</li> +</ol> + + + + + Introduction to Rust + 2022-10-10T00:00:00+00:00 + 2022-10-10T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/ + + <p><img src="https://www.rust-lang.org/logos/rust-logo-blk.svg" alt="Logo" /></p> +<h1 id="a-language-empowering-everyone-to-build-reliable-and-efficient-software">A language empowering everyone to build reliable and efficient software.</h1> +<p>(<a href="https://rustacean.net/">unofficial logo</a>)</p> +<h2 id="why-use-rust">Why use Rust?</h2> +<ul> +<li>It is <strong>safe</strong> (compared to C++ for example, as we will see in a minute)</li> +<li>It is <strong>fast</strong> (because it is compiled to machine code)</li> +<li>It is ergonomic and pleasant to use (static typing, expressive type system, helpful compiler +warnings)</li> +<li>It +is <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">loved by programmers</a></li> +<li>It provides excellent tooling</li> +</ul> +<h2 id="why-learn-rust">Why learn Rust?</h2> +<p>Even if you don't end up using Rust, learning it expands your horizons</p> +<ul> +<li>it helps especially with the awareness of what you can and can't do in concurrent applications</li> +<li>it helps you understand memory management and learn its good practices</li> +</ul> +<h2 id="why-not-to-learn-rust">Why not to learn Rust?</h2> +<ul> +<li>Some people say Rust is too hard to learn because of the borrow checker</li> +<li>Once you get to know Cargo you won't ever want to use a language without a built-in package +manager ;)</li> +<li>You will start hating C++</li> +</ul> +<h2 id="demos">Demos</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/cpp_meme.jpg" alt="Meme" /></p> +<p>Let's compare the same code written in <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/errors_demo.c">C</a>, <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/errors_demo.cpp">C++</a> +and <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/errors_demo.rs">Rust</a>.</p> +<h2 id="code-you-sent-me">Code you sent me!</h2> +<h3 id="aleksander-tudruj">Aleksander Tudruj</h3> +<pre data-lang="cpp" style="background-color:#ffffff;color:#4d4d4c;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;iostream&gt; +</span><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;unordered_map&gt; +</span><span> +</span><span style="color:#8959a8;">using </span><span>name </span><span style="color:#3e999f;">=</span><span> std::string; +</span><span style="color:#8959a8;">using </span><span>age </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">int</span><span>; +</span><span style="color:#8959a8;">using </span><span>person </span><span style="color:#3e999f;">=</span><span> std::pair&lt;name, age&gt;; +</span><span style="color:#8959a8;">using </span><span>address </span><span style="color:#3e999f;">=</span><span> std::string; +</span><span style="color:#8959a8;">using </span><span>address_book </span><span style="color:#3e999f;">=</span><span> std::unordered_map&lt;person, address&gt;; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_address_book</span><span>(</span><span style="color:#8959a8;">const</span><span> address_book </span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">book</span><span>) +</span><span>{ +</span><span> </span><span style="color:#8959a8;">for </span><span>(</span><span style="color:#8959a8;">const auto </span><span style="color:#3e999f;">&amp;</span><span>[person, address] </span><span style="color:#3e999f;">:</span><span> book) +</span><span> { +</span><span> std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> person.</span><span style="color:#c82829;">first </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&quot; is &quot; </span><span style="color:#3e999f;">&lt;&lt;</span><span> person.</span><span style="color:#c82829;">second </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&quot; years old and lives at &quot; </span><span style="color:#3e999f;">&lt;&lt;</span><span> address </span><span style="color:#3e999f;">&lt;&lt;</span><span> std::endl; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() +</span><span>{ +</span><span> +</span><span> address_book </span><span style="color:#c82829;">people</span><span style="color:#4271ae;">{}</span><span>; +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;John&quot;</span><span>, </span><span style="color:#f5871f;">20</span><span>}, </span><span style="color:#718c00;">&quot;221B Baker Street, London&quot;</span><span>}); +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;Mary&quot;</span><span>, </span><span style="color:#f5871f;">30</span><span>}, </span><span style="color:#718c00;">&quot;Avenue des Champs-Élysées, Paris&quot;</span><span>}); +</span><span> people.</span><span style="color:#c82829;">insert</span><span>({{</span><span style="color:#718c00;">&quot;Jack&quot;</span><span>, </span><span style="color:#f5871f;">73</span><span>}, </span><span style="color:#718c00;">&quot;Wall Street, New York&quot;</span><span>}); +</span><span> </span><span style="color:#c82829;">print_address_book</span><span style="color:#4271ae;">(people)</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#f5871f;">0</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/tudruj.cpp">tudruj.cpp</a>)</sub></p> +<h3 id="krystyna-gasinska">Krystyna Gasińska</h3> +<pre data-lang="python" style="background-color:#ffffff;color:#4d4d4c;" class="language-python "><code class="language-python" data-lang="python"><span style="color:#999999;"># sample 1 - different ways of removing elements from the list while iterating +</span><span>list1 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list1)</span><span>: +</span><span> </span><span style="color:#8959a8;">del </span><span>item +</span><span>list1 +</span><span> +</span><span style="color:#999999;"># [1, 2, 3, 4] +</span><span> +</span><span>list2 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list2)</span><span>: +</span><span> </span><span style="color:#4271ae;">list2.</span><span style="color:#c82829;">remove</span><span style="color:#4271ae;">(item) +</span><span>list2 +</span><span> +</span><span style="color:#999999;"># [2, 4] +</span><span> +</span><span>list3 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list3[:])</span><span>: +</span><span> </span><span style="color:#4271ae;">list3.</span><span style="color:#c82829;">remove</span><span style="color:#4271ae;">(item) +</span><span>list3 +</span><span> +</span><span style="color:#999999;"># [] +</span><span> +</span><span>list4 </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>] +</span><span style="color:#8959a8;">for </span><span>idx, item </span><span style="color:#8959a8;">in </span><span style="color:#4271ae;">enumerate(list4)</span><span>: +</span><span> </span><span style="color:#4271ae;">list4.</span><span style="color:#c82829;">pop</span><span style="color:#4271ae;">(idx) +</span><span>list4 +</span><span> +</span><span style="color:#999999;"># [2, 4] +</span><span> +</span><span style="color:#999999;"># sample 2 - string interning +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc&quot; +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc&quot; +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&#39;&#39;</span><span style="color:#4271ae;">.</span><span style="color:#c82829;">join</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">&#39;a&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;b&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;c&#39;</span><span style="color:#4271ae;">]) +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&#39;&#39;</span><span style="color:#4271ae;">.</span><span style="color:#c82829;">join</span><span style="color:#4271ae;">([</span><span style="color:#718c00;">&#39;a&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;b&#39;</span><span style="color:#4271ae;">, </span><span style="color:#718c00;">&#39;c&#39;</span><span style="color:#4271ae;">]) +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc!&quot; +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;abc!&quot; +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#999999;"># sample 3 - chained operations +</span><span>(</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">False</span><span>) </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>] +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span>(</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>]) +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">False </span><span style="color:#3e999f;">in </span><span>[</span><span style="color:#f5871f;">False</span><span>] </span><span style="color:#999999;"># unexpected... +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span style="color:#999999;"># sample 4 - is operator +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">256 +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">256 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span>a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257 +</span><span>b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># False +</span><span> +</span><span>a, b </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">257</span><span>, </span><span style="color:#f5871f;">257 +</span><span>a </span><span style="color:#3e999f;">is </span><span>b +</span><span> +</span><span style="color:#999999;"># True +</span><span> +</span><span style="color:#f5871f;">257 </span><span style="color:#3e999f;">is </span><span style="color:#f5871f;">257 +</span><span> +</span><span style="color:#999999;"># &lt;&gt;:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># &lt;&gt;:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;? +</span><span style="color:#999999;"># 257 is 257 +</span><span> +</span><span style="color:#999999;"># sample 5 - local variables +</span><span style="color:#8959a8;">def </span><span style="color:#4271ae;">f</span><span>(</span><span style="color:#f5871f;">trufel</span><span>): +</span><span> </span><span style="color:#8959a8;">if </span><span>trufel: +</span><span> y </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">1 +</span><span> y </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1 +</span><span> +</span><span style="color:#c82829;">f</span><span style="color:#4271ae;">(</span><span style="color:#f5871f;">True</span><span style="color:#4271ae;">) </span><span style="color:#999999;"># everything is fine +</span><span> +</span><span style="color:#c82829;">f</span><span style="color:#4271ae;">(</span><span style="color:#f5871f;">False</span><span style="color:#4271ae;">) </span><span style="color:#999999;"># gives error: local variable &#39;y&#39; referenced before assignment +</span><span> +</span><span style="color:#999999;"># --------------------------------------------------------------------------- +</span><span style="color:#999999;"># UnboundLocalError Traceback (most recent call last) +</span><span style="color:#999999;"># Input In [17], in &lt;cell line: 1&gt;() +</span><span style="color:#999999;"># ----&gt; 1 f(False) +</span><span> +</span><span style="color:#999999;"># Input In [15], in f(trufel) +</span><span style="color:#999999;"># 3 if trufel: +</span><span style="color:#999999;"># 4 y = 1 +</span><span style="color:#999999;"># ----&gt; 5 y += 1 +</span><span> +</span><span style="color:#999999;"># UnboundLocalError: local variable &#39;y&#39; referenced before assignment +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/gasinska.py">gasinska.py</a>)</sub></p> +<h3 id="antoni-koszowski">Antoni Koszowski</h3> +<pre data-lang="go" style="background-color:#ffffff;color:#4d4d4c;" class="language-go "><code class="language-go" data-lang="go"><span style="color:#999999;">// mutowalność jest wbudowana w język +</span><span> +</span><span style="color:#8959a8;">type </span><span>S </span><span style="color:#8959a8;">struct </span><span>{ +</span><span> </span><span style="color:#c82829;">A </span><span style="color:#c99e00;">string +</span><span> </span><span style="color:#c82829;">B </span><span>[]</span><span style="color:#c99e00;">string +</span><span>} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#c82829;">x </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">S</span><span>{</span><span style="color:#718c00;">&quot;x-A&quot;</span><span>, []</span><span style="color:#c99e00;">string</span><span>{</span><span style="color:#718c00;">&quot;x-B&quot;</span><span>}} +</span><span> </span><span style="color:#c82829;">y </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">x </span><span style="color:#999999;">// copy the struct +</span><span> </span><span style="color:#c82829;">y</span><span>.</span><span style="color:#c82829;">A </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;y-A&quot; +</span><span> </span><span style="color:#c82829;">y</span><span>.</span><span style="color:#c82829;">B</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;y-B&quot; +</span><span> +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Println</span><span>(</span><span style="color:#c82829;">x</span><span>, </span><span style="color:#c82829;">y</span><span>) +</span><span> </span><span style="color:#999999;">// Outputs &quot;{x-A [y-B]} {y-A [y-B]}&quot; -- x was modified! +</span><span>} +</span><span> +</span><span style="color:#999999;">// slices i kwestia append +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">doStuff</span><span>(</span><span style="color:#f5871f;">value </span><span>[]</span><span style="color:#c99e00;">string</span><span>) { +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>) +</span><span> +</span><span> </span><span style="color:#c82829;">value2 </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">value</span><span>[:] +</span><span> </span><span style="color:#c82829;">value2 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">append</span><span>(</span><span style="color:#c82829;">value2</span><span>, </span><span style="color:#718c00;">&quot;b&quot;</span><span>) +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#718c00;">, value2=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>, </span><span style="color:#c82829;">value2</span><span>) +</span><span> +</span><span> </span><span style="color:#c82829;">value2</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;z&quot; +</span><span> </span><span style="color:#c82829;">fmt</span><span>.</span><span style="color:#c82829;">Printf</span><span>(</span><span style="color:#718c00;">&quot;value=</span><span style="color:#666969;">%v</span><span style="color:#718c00;">, value2=</span><span style="color:#666969;">%v</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">value</span><span>, </span><span style="color:#c82829;">value2</span><span>) +</span><span>} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#c82829;">slice1 </span><span style="color:#3e999f;">:= </span><span>[]</span><span style="color:#c99e00;">string</span><span>{</span><span style="color:#718c00;">&quot;a&quot;</span><span>} </span><span style="color:#999999;">// length 1, capacity 1 +</span><span> +</span><span> </span><span style="color:#c82829;">doStuff</span><span>(</span><span style="color:#c82829;">slice1</span><span>) +</span><span> </span><span style="color:#999999;">// Output: +</span><span> </span><span style="color:#999999;">// value=[a] -- ok +</span><span> </span><span style="color:#999999;">// value=[a], value2=[a b] -- ok: value unchanged, value2 updated +</span><span> </span><span style="color:#999999;">// value=[a], value2=[z b] -- ok: value unchanged, value2 updated +</span><span> +</span><span> </span><span style="color:#c82829;">slice10 </span><span style="color:#3e999f;">:= </span><span style="color:#4271ae;">make</span><span>([]</span><span style="color:#c99e00;">string</span><span>, </span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">10</span><span>) </span><span style="color:#999999;">// length 1, capacity 10 +</span><span> </span><span style="color:#c82829;">slice10</span><span>[</span><span style="color:#f5871f;">0</span><span>] </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;a&quot; +</span><span> +</span><span> </span><span style="color:#c82829;">doStuff</span><span>(</span><span style="color:#c82829;">slice10</span><span>) +</span><span> </span><span style="color:#999999;">// Output: +</span><span> </span><span style="color:#999999;">// value=[a] -- ok +</span><span> </span><span style="color:#999999;">// value=[a], value2=[a b] -- ok: value unchanged, value2 updated +</span><span> </span><span style="color:#999999;">// value=[z], value2=[z b] -- WTF?!? value changed??? +</span><span>} +</span><span> +</span><span style="color:#999999;">// error handling +</span><span> +</span><span style="color:#c82829;">len</span><span>, </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">:= </span><span style="color:#c82829;">reader</span><span>.</span><span style="color:#c82829;">Read</span><span>(</span><span style="color:#c82829;">bytes</span><span>) +</span><span style="color:#8959a8;">if </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">nil </span><span>{ +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">err </span><span style="color:#3e999f;">== </span><span style="color:#c82829;">io</span><span>.</span><span style="color:#c82829;">EOF </span><span>{ +</span><span> </span><span style="color:#999999;">// All good, end of file +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#8959a8;">return </span><span style="color:#c82829;">err +</span><span> } +</span><span>} +</span><span> +</span><span> +</span><span style="color:#999999;">// interfejs nil +</span><span> +</span><span style="color:#8959a8;">type </span><span>Explodes </span><span style="color:#8959a8;">interface </span><span>{ +</span><span> </span><span style="color:#4271ae;">Bang</span><span>() +</span><span> </span><span style="color:#4271ae;">Boom</span><span>() +</span><span>} +</span><span> +</span><span style="color:#999999;">// Type Bomb implements Explodes +</span><span style="color:#8959a8;">type </span><span>Bomb </span><span style="color:#8959a8;">struct </span><span>{} +</span><span style="color:#8959a8;">func </span><span>(</span><span style="color:#3e999f;">*</span><span style="color:#8959a8;">Bomb</span><span>) </span><span style="color:#4271ae;">Bang</span><span>() {} +</span><span style="color:#8959a8;">func </span><span>(</span><span style="color:#8959a8;">Bomb</span><span>) </span><span style="color:#4271ae;">Boom</span><span>() {} +</span><span> +</span><span style="color:#8959a8;">func </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">var </span><span style="color:#c82829;">bomb </span><span style="color:#3e999f;">*</span><span style="color:#8959a8;">Bomb </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">nil +</span><span> </span><span style="color:#8959a8;">var </span><span style="color:#c82829;">explodes </span><span style="color:#8959a8;">Explodes </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">bomb +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#c82829;">bomb</span><span>, </span><span style="color:#c82829;">explodes</span><span>) </span><span style="color:#999999;">// &#39;0x0 (0x10a7060,0x0)&#39; +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">explodes </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">nil </span><span>{ +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#718c00;">&quot;Not nil!&quot;</span><span>) </span><span style="color:#999999;">// &#39;Not nil!&#39; What are we doing here?!?! +</span><span> </span><span style="color:#c82829;">explodes</span><span>.</span><span style="color:#c82829;">Bang</span><span>() </span><span style="color:#999999;">// works fine +</span><span> </span><span style="color:#c82829;">explodes</span><span>.</span><span style="color:#c82829;">Boom</span><span>() </span><span style="color:#999999;">// panic: value method main.Bomb.Boom called using nil *Bomb pointer +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#4271ae;">println</span><span>(</span><span style="color:#718c00;">&quot;nil!&quot;</span><span>) </span><span style="color:#999999;">// why don&#39;t we end up here? +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{} +</span><span style="color:#999999;">// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany. +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/koszowski.go">koszowski.go</a>)</sub></p> +<h3 id="mieszko-grodzicki">Mieszko Grodzicki</h3> +<pre data-lang="python" style="background-color:#ffffff;color:#4d4d4c;" class="language-python "><code class="language-python" data-lang="python"><span style="color:#8959a8;">def </span><span style="color:#4271ae;">add_contents</span><span>(</span><span style="color:#f5871f;">input_list</span><span>, </span><span style="color:#f5871f;">contents</span><span style="color:#3e999f;">=</span><span>[]): +</span><span> </span><span style="color:#8959a8;">for </span><span>val </span><span style="color:#8959a8;">in </span><span>input_list: +</span><span> </span><span style="color:#4271ae;">contents.</span><span style="color:#c82829;">append</span><span style="color:#4271ae;">(val) +</span><span> </span><span style="color:#8959a8;">return </span><span>contents +</span><span> +</span><span style="color:#4271ae;">print(</span><span style="color:#c82829;">add_contents</span><span style="color:#4271ae;">([</span><span style="color:#f5871f;">1</span><span style="color:#4271ae;">])) </span><span style="color:#999999;"># [1] +</span><span style="color:#4271ae;">print(</span><span style="color:#c82829;">add_contents</span><span style="color:#4271ae;">([</span><span style="color:#f5871f;">2</span><span style="color:#4271ae;">])) </span><span style="color:#999999;"># [1, 2] +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/grodzicki.py">grodzicki.py</a>)</sub></p> +<h2 id="installing-rust">Installing Rust</h2> +<ul> +<li><a href="https://rustup.rs/">Rustup</a></li> +<li>Setup an IDE +<ul> +<li><a href="https://www.jetbrains.com/clion/">CLion</a> (you can get +it <a href="https://www.jetbrains.com/community/education/">for free</a>) +and <a href="https://intellij-rust.github.io/">Intellij-Rust</a></li> +<li><a href="https://code.visualstudio.com/">VSCode</a> +and <a href="https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer">rust-analyzer</a></li> +<li>rust-analyzer also works +with <a href="https://rust-analyzer.github.io/manual.html#installation">other IDEs</a></li> +</ul> +</li> +</ul> +<h2 id="useful-tools">Useful tools</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/clippy.jpg" alt="Clippy" /></p> +<ul> +<li><code>cargo clippy</code> (for static analysis)</li> +<li>there's also <code>cargo check</code>, but it's less powerful than clippy</li> +<li><code>cargo fmt</code> (for code formatting)</li> +</ul> +<h3 id="rust-playground">Rust Playground</h3> +<ul> +<li><a href="https://play.rust-lang.org/">online Rust compiler</a></li> +</ul> +<h2 id="hello-world">Hello world</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;World&quot;</span><span>; +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); </span><span style="color:#999999;">// using the println! macro +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/hello_world.rs">hello_world.rs</a>)</sub></p> +<h3 id="variables">Variables</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// inferred type +</span><span> </span><span style="color:#8959a8;">let</span><span> y: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; </span><span style="color:#999999;">// specified type +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40 </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">2</span><span>; </span><span style="color:#999999;">// shadowing +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x); </span><span style="color:#999999;">// prints 42 +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// x = 0; // compilation error, variables are by default immutable +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// declare as mutable +</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#999999;">// now we can reassign +</span><span> +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; </span><span style="color:#999999;">// x = x + 1 +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/variables.rs">variables.rs</a>)</sub></p> +<h3 id="conditionals">Conditionals</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">42 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 42&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">43 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 43&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is not 42 or 43&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can also use ifs as expressions +</span><span> </span><span style="color:#8959a8;">let</span><span> a_or_b </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;a&quot; </span><span style="color:#999999;">// notice no semicolon at the end +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;b&quot; +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/conditionals.rs">conditionals.rs</a>)</sub></p> +<h3 id="loops">Loops</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;i is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, i); </span><span style="color:#999999;">// i in [0, 10) +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">while</span><span> x </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">50 </span><span>{ +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> y </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> iterations </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> iterations </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> iterations </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#8959a8;">continue</span><span>; +</span><span> } +</span><span> y </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> y </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can use labels to refer to a specific loop +</span><span> </span><span style="color:#8959a8;">let mut</span><span> count </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> &#39;counting_up: </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> remaining </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> remaining </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">9 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> </span><span style="color:#8959a8;">if</span><span> count </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break &#39;counting_up</span><span>; </span><span style="color:#999999;">// ends the outer loop +</span><span> } +</span><span> remaining </span><span style="color:#3e999f;">-= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> count </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use break with a value. +</span><span> </span><span style="color:#999999;">// Because loops are expressions too, +</span><span> </span><span style="color:#999999;">// the value we break with will be returned from the functions +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> value </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> counter </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break </span><span style="color:#f5871f;">32</span><span>; +</span><span> } +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/loops.rs">loops.rs</a>)</sub></p> +<h3 id="functions">Functions</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">get_5</span><span>() -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#f5871f;">5 </span><span style="color:#999999;">// we could also write &quot;return 5;&quot; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">print_sum</span><span>(</span><span style="color:#f5871f;">a</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">b</span><span>: </span><span style="color:#8959a8;">u32</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;a + b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, a </span><span style="color:#3e999f;">+</span><span> b); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; +</span><span> </span><span style="color:#4271ae;">print_sum</span><span>(a, </span><span style="color:#4271ae;">get_5</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/functions.rs">functions.rs</a>)</sub></p> +<h2 id="test-assignment-not-graded">Test assignment (not graded)</h2> +<p>Click <a href="https://classroom.github.com/a/P_z-gHH-">here</a></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/book/">The Book, chapters 1-3</a></li> +</ul> +<h2 id="additional-reading">Additional reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/rust-by-example/">Rust By Example</a></li> +</ul> + + + + + Macros + 2022-06-07T00:00:00+00:00 + 2022-06-07T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/14-macros/ + + <h2 id="reading">Reading</h2> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch19-06-macros.html">The Book, Chapter 19.5</a></p> +</li> +<li> +<p><a href="https://danielkeep.github.io/tlborm/book/README.html">Little Book of Macros</a></p> +</li> +<li> +<p><a href="https://github.com/dtolnay/proc-macro-workshop">Macro Workshop</a></p> +</li> +</ul> + + + + + Macros + 2022-06-07T00:00:00+00:00 + 2022-06-07T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/16-macros/ + + <h2 id="reading">Reading</h2> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch19-06-macros.html">The Book, Chapter 19.5</a></p> +</li> +<li> +<p><a href="https://danielkeep.github.io/tlborm/book/README.html">Little Book of Macros</a></p> +</li> +<li> +<p><a href="https://github.com/dtolnay/proc-macro-workshop">Macro Workshop</a></p> +</li> +</ul> + + + + + [Bonus] Rusty graphs + 2022-06-07T00:00:00+00:00 + 2022-06-07T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/b1-rusty-graphs/ + + <h2 id="introduction">Introduction</h2> +<p>We all had to deal with graphs in Rust, thanks to the task &quot;corpos&quot;. Handling the graphs the way that was intended - implementing adjacency list by using <code>Rc&lt;RefCell&lt;Node&gt;&gt;</code> - had its drawbacks: an endless stream of derefing, a risk of creating a reference cycle and this mysterious &quot;interior mutability&quot; mechanism.</p> +<h2 id="rc-madness">Rc madness</h2> +<p>To quickly recap why and how this works: <code>Rc&lt;&gt;</code> is a smart pointer that utilizes reference counting, similar to <code>shared_ptr&lt;&gt;</code> from C++. However, due to Rust's ownerships rules it does not allow us to mutate the data behind the pointer. That's why we don't actually point to the node itself - we hide it behind <code>RefCell&lt;&gt;</code> allowing us to mutate the data, by utilizing interior mutability pattern.</p> +<p>There are serval caveats, however. First, we lose compiler's help - it won't scream at us when we try to do something illegal. Speaking technically, when using <code>RefCell&lt;&gt;</code> Rust can no longer help us statically - it employs runtime checks to ensure ownership rules. The program will panic if something goes wrong, which is somewhat orthogonal to Rust's mission of catching bugs at compile time. Even if our program is bug-less, we still have to pay performance penalty, since runtime checks are not free.</p> +<p>Opinion: working with <code>Rc&lt;RefCell&lt;&gt;&gt;</code> in Rust feels very off, since it abandons some of the core designs goals of Rust - catching bugs at compile time and introducing runtime cost that other languages avoid. Not to mention the syntax which at time can be daunting. Can we do better?</p> +<h2 id="owning-the-graph">Owning the graph</h2> +<p>The problems encountered above arise from the same source: C/C++-like pointer structures don't go well with Rust. Sure, they can be done, but at what cost? Maybe instead of trying to force Rust into doing something it clearly does not want us to do, we should try different approach?</p> +<p>We could reason somewhat like this: if multiple mutable ownership is the problem, maybe let's abandon it altogether? What about a single object owning the whole graph?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">struct </span><span>Graph { +</span><span> </span><span style="color:#c82829;">nodes</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Node&gt;, +</span><span> </span><span style="color:#c82829;">edges</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Edge&gt;, +</span><span> </span><span style="color:#c82829;">highest_idx</span><span>: NodeIdx, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Node { +</span><span> </span><span style="color:#c82829;">idx</span><span>: NodeIdx, +</span><span> </span><span style="color:#c82829;">data</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Edge { +</span><span> </span><span style="color:#c82829;">idx1</span><span>: NodeIdx, +</span><span> </span><span style="color:#c82829;">idx2</span><span>: NodeIdx, +</span><span>} +</span></code></pre> +<p>Here we implement a graph as a single, non-recursive struct, that owns the whole graph. The nodes are identified by their index. This implementation has its disadvantages when compared to adjacency list, but it has one major advantage: it plays nice with Rust as a language.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub type </span><span>NodeIdx </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">usize</span><span>; +</span><span> +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Graph { +</span><span> </span><span style="color:#c82829;">nodes</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Node&gt;, +</span><span> </span><span style="color:#c82829;">edges</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Edge&gt;, +</span><span> </span><span style="color:#c82829;">highest_idx</span><span>: NodeIdx, +</span><span>} +</span><span> +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Node { +</span><span> </span><span style="color:#c82829;">idx</span><span>: NodeIdx, +</span><span> </span><span style="color:#c82829;">data</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Edge { +</span><span> </span><span style="color:#c82829;">idx1</span><span>: NodeIdx, +</span><span> </span><span style="color:#c82829;">idx2</span><span>: NodeIdx, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Graph { +</span><span> </span><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">new</span><span>() -&gt; Graph { +</span><span> Graph { nodes: </span><span style="color:#c99e00;">Vec</span><span>::new(), edges: </span><span style="color:#c99e00;">Vec</span><span>::new(), highest_idx: </span><span style="color:#f5871f;">0 </span><span>} +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">add_node</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">data</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) { +</span><span> </span><span style="color:#c82829;">self</span><span>.nodes.</span><span style="color:#4271ae;">push</span><span>(Node { idx: </span><span style="color:#c82829;">self</span><span>.highest_idx </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">1</span><span>, data: data.</span><span style="color:#4271ae;">to_string</span><span>() }); +</span><span> </span><span style="color:#c82829;">self</span><span>.highest_idx </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">add_edge</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">idx1</span><span>: NodeIdx, </span><span style="color:#f5871f;">idx2</span><span>: NodeIdx) { +</span><span> </span><span style="color:#c82829;">self</span><span>.edges.</span><span style="color:#4271ae;">push</span><span>(Edge{idx1, idx2}); +</span><span> } +</span><span>} +</span></code></pre> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> graph </span><span style="color:#3e999f;">= </span><span>Graph::new(); +</span><span> +</span><span> graph.</span><span style="color:#4271ae;">add_node</span><span>(</span><span style="color:#718c00;">&quot;123&quot;</span><span>); +</span><span> +</span><span> graph.</span><span style="color:#4271ae;">add_edge</span><span>(</span><span style="color:#f5871f;">0</span><span>, </span><span style="color:#f5871f;">0</span><span>); +</span><span> +</span><span>} +</span></code></pre> +<p>This implementation has its problems of algorithmic nature (problematic removal and so on), but it plays nice with Rust. There is no runtime penalty and we enable compiler to help us. It also is not a syntactic nightmare.</p> +<h2 id="real-world-solution">Real-world solution</h2> +<p>If one tries to implement graphs in Rust and embarks on a Google journey how to exactly do it, they will find that the go-to answer is: it's a nightmare, use Petgraph. It's a sensible route to choose - Petgraph really simplifies things. In fact my team has done exactly that with our final program. Let's dive deeper into how Petgraph does its magic:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>Graph&lt;N, E, Ty = Directed, Ix = DefaultIx&gt; { +</span><span> </span><span style="color:#c82829;">nodes</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Node&lt;N, Ix&gt;&gt;, +</span><span> </span><span style="color:#c82829;">edges</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Edge&lt;E, Ix&gt;&gt;, +</span><span> </span><span style="color:#c82829;">ty</span><span>: PhantomData&lt;Ty&gt;, +</span><span>} +</span></code></pre> +<p>Huh, this is isomorphic to what's been discussed above. Maybe the next one will be different?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span> </span><span style="color:#8959a8;">pub struct </span><span>GraphMap&lt;N, E, Ty&gt; { +</span><span> </span><span style="color:#c82829;">nodes</span><span>: IndexMap&lt;N, </span><span style="color:#c99e00;">Vec</span><span>&lt;(N, CompactDirection)&gt;&gt;, +</span><span> </span><span style="color:#c82829;">edges</span><span>: IndexMap&lt;(N, N), E&gt;, +</span><span> </span><span style="color:#c82829;">ty</span><span>: PhantomData&lt;Ty&gt;, +</span><span>} +</span></code></pre> +<p>At first glance it is, but if we learn that <code>IndexMap</code> is a crate that essentially provides a map, we see that this example also is similar to the previous one (in a sense of using arena allocation [region-based memory management]).</p> +<p>One last representation to go:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>MatrixGraph&lt;N, E, Ty = Directed, Null: Nullable&lt;Wrapped = E&gt; = </span><span style="color:#c99e00;">Option</span><span>&lt;E&gt;, Ix = DefaultIx&gt; +</span><span>{ +</span><span> </span><span style="color:#c82829;">node_adjacencies</span><span>: </span><span style="color:#c99e00;">Vec</span><span>&lt;Null&gt;, +</span><span> </span><span style="color:#c82829;">node_capacity</span><span>: </span><span style="color:#8959a8;">usize</span><span>, +</span><span> </span><span style="color:#c82829;">nodes</span><span>: IdStorage&lt;N&gt;, +</span><span> </span><span style="color:#c82829;">nb_edges</span><span>: </span><span style="color:#8959a8;">usize</span><span>, +</span><span> </span><span style="color:#c82829;">ty</span><span>: PhantomData&lt;Ty&gt;, +</span><span> </span><span style="color:#c82829;">ix</span><span>: PhantomData&lt;Ix&gt;, +</span><span>} +</span></code></pre> +<p>This representations uses flattened 2D array to store the graph. No pointers in sight.</p> +<p>Of course, those representations are not ideal: they have their issues. To name a few: <code>Graph</code> is problematic when it comes to frequent removals of nodes or edges - since they are stored in <code>Vec</code>, one has to either put some kind of placeholder in place of node/edge (which is not very Rusty) or copy the whole array (which is slow). <code>GraphMap</code> mitigates this issues by using hash map to store the graph, but that introduces some requirements on the <code>Node</code> type: it must implement <code>Ord</code> as well as <code>Copy</code> and <code>Eq + Hash</code> - which may be suitable for integers, but not exactly for more complicated types. <code>MatrixGraph</code> takes up much space and, similarly to <code>Graph</code>, has its problems when it comes to removing nodes.</p> +<p>Yet, all this problems are not Rusty in nature - they hold across every programming language.</p> +<p>The point of talking about all this is not to argue that representations X is better in some algorithmic sense than Y; it is to see that pointer based approach, which may work well in other language, is the hard one in Rust and results in a nightmarish code. The proof by Petgraph example shows that real world implementations avoid pointers - for a good reason.</p> +<p>Here's some Petgraph code in action:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dfs_helper</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">graph</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> Graph, </span><span style="color:#f5871f;">node_index</span><span>: NodeIndex) { +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">add_step</span><span>(AlgorithmStep::Node(NodeStep::new( +</span><span> node_index, +</span><span> NodeState::Queued, +</span><span> ))); +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(node) </span><span style="color:#3e999f;">=</span><span> graph.</span><span style="color:#4271ae;">node_weight_mut</span><span>(node_index) { +</span><span> node.</span><span style="color:#4271ae;">set_state</span><span>(NodeState::Queued) +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> walker </span><span style="color:#3e999f;">=</span><span> graph +</span><span> .</span><span style="color:#4271ae;">neighbors_directed</span><span>(node_index, Direction::Outgoing) +</span><span> .</span><span style="color:#4271ae;">detach</span><span>(); +</span><span> +</span><span> </span><span style="color:#8959a8;">while let </span><span style="color:#c99e00;">Some</span><span>((edge_idx, other_node_idx)) </span><span style="color:#3e999f;">=</span><span> walker.</span><span style="color:#4271ae;">next</span><span>(graph) { +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(other_state) </span><span style="color:#3e999f;">=</span><span> graph +</span><span> .</span><span style="color:#4271ae;">node_weight</span><span>(other_node_idx) +</span><span> .</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">node</span><span>| node.</span><span style="color:#4271ae;">get_state</span><span>()) +</span><span> { +</span><span> </span><span style="color:#8959a8;">if </span><span>matches!(other_state, NodeState::NotVisited) { +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">add_step</span><span>(AlgorithmStep::Edge(EdgeStep::new(edge_idx))); +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">dfs_helper</span><span>(graph, other_node_idx); +</span><span> } +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">add_step</span><span>(AlgorithmStep::Node(NodeStep::new( +</span><span> node_index, +</span><span> NodeState::Visited, +</span><span> ))); +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(node) </span><span style="color:#3e999f;">=</span><span> graph.</span><span style="color:#4271ae;">node_weight_mut</span><span>(node_index) { +</span><span> node.</span><span style="color:#4271ae;">set_state</span><span>(NodeState::Visited) +</span><span> } +</span><span>} +</span></code></pre> +<p>Sources:</p> +<p><a href="https://docs.rs/petgraph/latest/petgraph/index.html">Petgraph</a></p> +<p><a href="https://github.com/petgraph/petgraph">Petgraph's source code</a></p> +<p><a href="https://github.com/nrc/r4cppp/blob/master/graphs/README.md">Rust's dev post about graphs</a></p> +<p><a href="https://smallcultfollowing.com/babysteps/blog/2015/04/06/modeling-graphs-in-rust-using-vector-indices/">Another blog post about graphs</a></p> +<p>Author: Mikołaj Piróg</p> + + + + + Async: Part 2 + 2022-05-23T00:00:00+00:00 + 2022-05-23T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/13-async-2/ + + <h2 id="reinventing-futures">Reinventing futures</h2> +<p>We recently got our feet wet with the async/await functionality of Rust by using the Tokio library. With this basic understanding of what we expect out of <code>futures</code>, let's try to come up with their details ourselves.</p> +<p>We know that, when asked, a future can either give us a ready value or still be waiting for it. Asking about the future's result is called <em>polling</em>. Our future could look something like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>SimpleFuture { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">enum </span><span>Poll&lt;T&gt; { +</span><span> Ready(T), +</span><span> Pending, +</span><span>} +</span></code></pre> +<p>The <code>poll</code> method can be called to check for the result of the future. There is a flaw in this however - whatever is coordinating our future-based computations will have to constantly poll each of them in hope they are ready to do some work.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>SimpleFuture { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span><span> +</span></code></pre> +<p>We can solve this by attaching a callback to our polling. The <code>wake</code> function passed to <code>poll</code> can be used to notify whoever issued the poll that the future is ready to make some progress and should be polled.</p> +<p>Let's picture a quick example of how our <code>SimpleFuture</code> could be used.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>SocketRead&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> </span><span style="color:#c82829;">socket</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> Socket, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>SimpleFuture </span><span style="color:#8959a8;">for </span><span>SocketRead&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Vec</span><span>&lt;</span><span style="color:#8959a8;">u8</span><span>&gt;; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">has_data_to_read</span><span>() { +</span><span> </span><span style="color:#999999;">// The socket has data -- read it into a buffer and return it. +</span><span> Poll::Ready(</span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">read_buf</span><span>()) +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#999999;">// The socket does not yet have data. +</span><span> </span><span style="color:#999999;">// +</span><span> </span><span style="color:#999999;">// Arrange for `wake` to be called once data is available. +</span><span> </span><span style="color:#999999;">// When data becomes available, `wake` will be called, and the +</span><span> </span><span style="color:#999999;">// user of this `Future` will know to call `poll` again and +</span><span> </span><span style="color:#999999;">// receive data. +</span><span> </span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">set_readable_callback</span><span>(wake); +</span><span> Poll::Pending +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="combining-futures">Combining futures</h3> +<p>With the <code>SimpleFuture</code> at our disposal we can easily model more advanced concurrent computations.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#999999;">/// Concurrency is achieved via the fact that calls to `poll` each future +</span><span style="color:#999999;">/// may be interleaved, allowing each future to advance itself at its own pace. +</span><span style="color:#8959a8;">pub struct </span><span>Join&lt;FutureA, FutureB&gt; { +</span><span> </span><span style="color:#999999;">// Each field may contain a future that should be run to completion. +</span><span> </span><span style="color:#999999;">// If the future has already completed, the field is set to `None`. +</span><span> </span><span style="color:#999999;">// This prevents us from polling a future after it has completed, which +</span><span> </span><span style="color:#999999;">// would violate the contract of the `Future` trait. +</span><span> </span><span style="color:#c82829;">a</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureA&gt;, +</span><span> </span><span style="color:#c82829;">b</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureB&gt;, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;FutureA, FutureB&gt; SimpleFuture </span><span style="color:#8959a8;">for </span><span>Join&lt;FutureA, FutureB&gt; +</span><span style="color:#8959a8;">where +</span><span> FutureA: SimpleFuture&lt;Output = ()&gt;, +</span><span> FutureB: SimpleFuture&lt;Output = ()&gt;, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>(); +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#999999;">// Attempt to complete future `a`. +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(a) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.a { +</span><span> </span><span style="color:#8959a8;">if let </span><span>Poll::Ready(()) </span><span style="color:#3e999f;">=</span><span> a.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#c82829;">self</span><span>.a.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Attempt to complete future `b`. +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(b) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.b { +</span><span> </span><span style="color:#8959a8;">if let </span><span>Poll::Ready(()) </span><span style="color:#3e999f;">=</span><span> b.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#c82829;">self</span><span>.b.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.a.</span><span style="color:#4271ae;">is_none</span><span>() </span><span style="color:#3e999f;">&amp;&amp; </span><span style="color:#c82829;">self</span><span>.b.</span><span style="color:#4271ae;">is_none</span><span>() { +</span><span> </span><span style="color:#999999;">// Both futures have completed -- we can return successfully +</span><span> Poll::Ready(()) +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#999999;">// One or both futures returned `Poll::Pending` and still have +</span><span> </span><span style="color:#999999;">// work to do. They will call `wake()` when progress can be made. +</span><span> Poll::Pending +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<p>We can also queue futures like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>AndThenFut&lt;FutureA, FutureB&gt; { +</span><span> </span><span style="color:#c82829;">first</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureA&gt;, +</span><span> </span><span style="color:#c82829;">second</span><span>: FutureB, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;FutureA, FutureB&gt; SimpleFuture </span><span style="color:#8959a8;">for </span><span>AndThenFut&lt;FutureA, FutureB&gt; +</span><span style="color:#8959a8;">where +</span><span> FutureA: SimpleFuture&lt;Output = ()&gt;, +</span><span> FutureB: SimpleFuture&lt;Output = ()&gt;, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>(); +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(first) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.first { +</span><span> </span><span style="color:#8959a8;">match</span><span> first.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#999999;">// We&#39;ve completed the first future -- remove it and start on +</span><span> </span><span style="color:#999999;">// the second! +</span><span> Poll::Ready(()) </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c82829;">self</span><span>.first.</span><span style="color:#4271ae;">take</span><span>(), +</span><span> </span><span style="color:#999999;">// We couldn&#39;t yet complete the first future. +</span><span> Poll::Pending </span><span style="color:#3e999f;">=&gt; </span><span style="color:#8959a8;">return </span><span>Poll::Pending, +</span><span> }; +</span><span> } +</span><span> </span><span style="color:#999999;">// Now that the first future is done, attempt to complete the second. +</span><span> </span><span style="color:#c82829;">self</span><span>.second.</span><span style="color:#4271ae;">poll</span><span>(wake) +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="exercise">Exercise</h3> +<p>The last example assumes that both futures are already constructed. In practice, however, we often want to chain futures that use the results of their predecessors, like this - <code>get_breakfast().and_then(|food| eat(food));</code>. Try implementing this behavior by adding a new method to the <code>SimpleFuture</code> trait called <code>and_then</code> and something that models this sequential computation (like the previous <code>AndThenFut</code> future).</p> +<h3 id="the-real-deal">The real deal</h3> +<p>We weren't far from the actual way Rust's futures are structured. The <code>Future</code> trait looks as follows:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>Future { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>( +</span><span> </span><span style="color:#999999;">// Note the change from `&amp;mut self` to `Pin&lt;&amp;mut Self&gt;`: +</span><span> </span><span style="color:#f5871f;">self</span><span>: Pin&lt;</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut Self</span><span>&gt;, +</span><span> </span><span style="color:#999999;">// and the change from `wake: fn()` to `cx: &amp;mut Context&lt;&#39;_&gt;`: +</span><span> </span><span style="color:#f5871f;">cx</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>Context&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;, +</span><span> ) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span></code></pre> +<p>There are two differences here. Firstly, we use a context instead of a standalone <code>wake</code> method. Since this callback was just a simple function pointer, there was no way for it to hold any data pertaining to which future called it. +Secondly, we take <code>self</code> as a <code>Pin&lt;&gt;</code>. This enables us to create immovable futures - we will go into it later.</p> +<h2 id="coordinating-futures-waker-executor">Coordinating futures - waker &amp; executor</h2> +<h3 id="using-wakers-and-context">Using wakers and context</h3> +<p>We will follow the <a href="https://rust-lang.github.io/async-book/02_execution/03_wakeups.html">steps</a> in the book to make a future that runs a separate thread that sleeps for a given duration and only then returns a result.</p> +<h3 id="executor">Executor</h3> +<p>We will follow the <a href="https://rust-lang.github.io/async-book/02_execution/04_executor.html">steps</a> in the book to create our own executor to run our futures on.</p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://rust-lang.github.io/async-book/04_pinning/01_chapter.html">Pinning in detail</a></li> +<li><a href="https://doc.rust-lang.org/nightly/std/pin/index.html">Pinning in even more detail</a></li> +<li><a href="https://tokio.rs/tokio/tutorial/async">Async in depth</a></li> +</ul> +<h2 id="additional-reading">Additional reading</h2> +<ul> +<li><a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/">What color is your function</a></li> +<li><a href="https://fasterthanli.me/articles/pin-and-suffering">Pin and suffering</a></li> +<li><a href="https://fasterthanli.me/articles/understanding-rust-futures-by-going-way-too-deep">Understanding Rust futures by going way too deep</a></li> +<li><a href="https://eventhelix.com/rust/rust-to-assembly-async-await/">Desugaring and assembly of Rust async/await</a></li> +</ul> +<h2 id="assignment-7-graded">Assignment 7 (graded)</h2> +<p><a href="https://classroom.github.com/a/5g7J3TNh">Calculator</a></p> +<p>Deadline: 18.12.2024 23:59</p> + + + + + Async: Part 2 + 2022-05-23T00:00:00+00:00 + 2022-05-23T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/15-async-2/ + + <h2 id="reinventing-futures">Reinventing futures</h2> +<p>We recently got our feet wet with the async/await functionality of Rust by using the Tokio library. With this basic understanding of what we expect out of <code>futures</code>, let's try to come up with their details ourselves.</p> +<p>We know that, when asked, a future can either give us a ready value or still be waiting for it. Asking about the future's result is called <em>polling</em>. Our future could look something like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>SimpleFuture { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">enum </span><span>Poll&lt;T&gt; { +</span><span> Ready(T), +</span><span> Pending, +</span><span>} +</span></code></pre> +<p>The <code>poll</code> method can be called to check for the result of the future. There is a flaw in this however - whatever is coordinating our future-based computations will have to constantly poll each of them in hope they are ready to do some work.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>SimpleFuture { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span><span> +</span></code></pre> +<p>We can solve this by attaching a callback to our polling. The <code>wake</code> function passed to <code>poll</code> can be used to notify whoever issued the poll that the future is ready to make some progress and should be polled.</p> +<p>Let's picture a quick example of how our <code>SimpleFuture</code> could be used.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>SocketRead&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> </span><span style="color:#c82829;">socket</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> Socket, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>SimpleFuture </span><span style="color:#8959a8;">for </span><span>SocketRead&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Vec</span><span>&lt;</span><span style="color:#8959a8;">u8</span><span>&gt;; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">has_data_to_read</span><span>() { +</span><span> </span><span style="color:#999999;">// The socket has data -- read it into a buffer and return it. +</span><span> Poll::Ready(</span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">read_buf</span><span>()) +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#999999;">// The socket does not yet have data. +</span><span> </span><span style="color:#999999;">// +</span><span> </span><span style="color:#999999;">// Arrange for `wake` to be called once data is available. +</span><span> </span><span style="color:#999999;">// When data becomes available, `wake` will be called, and the +</span><span> </span><span style="color:#999999;">// user of this `Future` will know to call `poll` again and +</span><span> </span><span style="color:#999999;">// receive data. +</span><span> </span><span style="color:#c82829;">self</span><span>.socket.</span><span style="color:#4271ae;">set_readable_callback</span><span>(wake); +</span><span> Poll::Pending +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="combining-futures">Combining futures</h3> +<p>With the <code>SimpleFuture</code> at our disposal we can easily model more advanced concurrent computations.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#999999;">/// Concurrency is achieved via the fact that calls to `poll` each future +</span><span style="color:#999999;">/// may be interleaved, allowing each future to advance itself at its own pace. +</span><span style="color:#8959a8;">pub struct </span><span>Join&lt;FutureA, FutureB&gt; { +</span><span> </span><span style="color:#999999;">// Each field may contain a future that should be run to completion. +</span><span> </span><span style="color:#999999;">// If the future has already completed, the field is set to `None`. +</span><span> </span><span style="color:#999999;">// This prevents us from polling a future after it has completed, which +</span><span> </span><span style="color:#999999;">// would violate the contract of the `Future` trait. +</span><span> </span><span style="color:#c82829;">a</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureA&gt;, +</span><span> </span><span style="color:#c82829;">b</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureB&gt;, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;FutureA, FutureB&gt; SimpleFuture </span><span style="color:#8959a8;">for </span><span>Join&lt;FutureA, FutureB&gt; +</span><span style="color:#8959a8;">where +</span><span> FutureA: SimpleFuture&lt;Output = ()&gt;, +</span><span> FutureB: SimpleFuture&lt;Output = ()&gt;, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>(); +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#999999;">// Attempt to complete future `a`. +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(a) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.a { +</span><span> </span><span style="color:#8959a8;">if let </span><span>Poll::Ready(()) </span><span style="color:#3e999f;">=</span><span> a.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#c82829;">self</span><span>.a.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Attempt to complete future `b`. +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(b) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.b { +</span><span> </span><span style="color:#8959a8;">if let </span><span>Poll::Ready(()) </span><span style="color:#3e999f;">=</span><span> b.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#c82829;">self</span><span>.b.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.a.</span><span style="color:#4271ae;">is_none</span><span>() </span><span style="color:#3e999f;">&amp;&amp; </span><span style="color:#c82829;">self</span><span>.b.</span><span style="color:#4271ae;">is_none</span><span>() { +</span><span> </span><span style="color:#999999;">// Both futures have completed -- we can return successfully +</span><span> Poll::Ready(()) +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#999999;">// One or both futures returned `Poll::Pending` and still have +</span><span> </span><span style="color:#999999;">// work to do. They will call `wake()` when progress can be made. +</span><span> Poll::Pending +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<p>We can also queue futures like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>AndThenFut&lt;FutureA, FutureB&gt; { +</span><span> </span><span style="color:#c82829;">first</span><span>: </span><span style="color:#c99e00;">Option</span><span>&lt;FutureA&gt;, +</span><span> </span><span style="color:#c82829;">second</span><span>: FutureB, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;FutureA, FutureB&gt; SimpleFuture </span><span style="color:#8959a8;">for </span><span>AndThenFut&lt;FutureA, FutureB&gt; +</span><span style="color:#8959a8;">where +</span><span> FutureA: SimpleFuture&lt;Output = ()&gt;, +</span><span> FutureB: SimpleFuture&lt;Output = ()&gt;, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>(); +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">wake</span><span>: </span><span style="color:#8959a8;">fn</span><span>()) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt; { +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Some</span><span>(first) </span><span style="color:#3e999f;">= &amp;</span><span style="color:#8959a8;">mut </span><span style="color:#c82829;">self</span><span>.first { +</span><span> </span><span style="color:#8959a8;">match</span><span> first.</span><span style="color:#4271ae;">poll</span><span>(wake) { +</span><span> </span><span style="color:#999999;">// We&#39;ve completed the first future -- remove it and start on +</span><span> </span><span style="color:#999999;">// the second! +</span><span> Poll::Ready(()) </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c82829;">self</span><span>.first.</span><span style="color:#4271ae;">take</span><span>(), +</span><span> </span><span style="color:#999999;">// We couldn&#39;t yet complete the first future. +</span><span> Poll::Pending </span><span style="color:#3e999f;">=&gt; </span><span style="color:#8959a8;">return </span><span>Poll::Pending, +</span><span> }; +</span><span> } +</span><span> </span><span style="color:#999999;">// Now that the first future is done, attempt to complete the second. +</span><span> </span><span style="color:#c82829;">self</span><span>.second.</span><span style="color:#4271ae;">poll</span><span>(wake) +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="exercise">Exercise</h3> +<p>The last example assumes that both futures are already constructed. In practice, however, we often want to chain futures that use the results of their predecessors, like this - <code>get_breakfast().and_then(|food| eat(food));</code>. Try implementing this behavior by adding a new method to the <code>SimpleFuture</code> trait called <code>and_then</code> and something that models this sequential computation (like the previous <code>AndThenFut</code> future).</p> +<h3 id="the-real-deal">The real deal</h3> +<p>We weren't far from the actual way Rust's futures are structured. The <code>Future</code> trait looks as follows:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>Future { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">poll</span><span>( +</span><span> </span><span style="color:#999999;">// Note the change from `&amp;mut self` to `Pin&lt;&amp;mut Self&gt;`: +</span><span> </span><span style="color:#f5871f;">self</span><span>: Pin&lt;</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut Self</span><span>&gt;, +</span><span> </span><span style="color:#999999;">// and the change from `wake: fn()` to `cx: &amp;mut Context&lt;&#39;_&gt;`: +</span><span> </span><span style="color:#f5871f;">cx</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>Context&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;, +</span><span> ) -&gt; Poll&lt;</span><span style="color:#8959a8;">Self::</span><span>Output&gt;; +</span><span>} +</span></code></pre> +<p>There are two differences here. Firstly, we use a context instead of a standalone <code>wake</code> method. Since this callback was just a simple function pointer, there was no way for it to hold any data pertaining to which future called it. +Secondly, we take <code>self</code> as a <code>Pin&lt;&gt;</code>. This enables us to create immovable futures - we will go into it later.</p> +<h2 id="coordinating-futures-waker-executor">Coordinating futures - waker &amp; executor</h2> +<h3 id="using-wakers-and-context">Using wakers and context</h3> +<p>We will follow the <a href="https://rust-lang.github.io/async-book/02_execution/03_wakeups.html">steps</a> in the book to make a future that runs a separate thread that sleeps for a given duration and only then returns a result.</p> +<h3 id="executor">Executor</h3> +<p>We will follow the <a href="https://rust-lang.github.io/async-book/02_execution/04_executor.html">steps</a> in the book to create our own executor to run our futures on.</p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://rust-lang.github.io/async-book/04_pinning/01_chapter.html">Pinning in detail</a></li> +<li><a href="https://doc.rust-lang.org/nightly/std/pin/index.html">Pinning in even more detail</a></li> +<li><a href="https://tokio.rs/tokio/tutorial/async">Async in depth</a></li> +</ul> +<h2 id="additional-reading">Additional reading</h2> +<ul> +<li><a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/">What color is your function</a></li> +<li><a href="https://fasterthanli.me/articles/pin-and-suffering">Pin and suffering</a></li> +<li><a href="https://fasterthanli.me/articles/understanding-rust-futures-by-going-way-too-deep">Understanding Rust futures by going way too deep</a></li> +</ul> + + + + + Async: Part 1 + 2022-05-16T00:00:00+00:00 + 2022-05-16T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/14-async-1/ + + <h2 id="tokio">Tokio</h2> +<p>We'll use the <a href="https://tokio.rs/tokio/tutorial">Tokio tutorial</a> (chapters <code>Overview</code>-<code>Channels</code>).</p> +<h2 id="common-rust-lifetime-misconceptions">Common Rust Lifetime Misconceptions</h2> +<p>Please read <a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md">this blogpost</a>.</p> + + + + + Design patterns + 2022-05-09T00:00:00+00:00 + 2022-05-09T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/13-design-patterns/ + + <h2 id="object-oriented-programming-and-rust">Object-oriented programming and Rust</h2> +<p>The book has <a href="https://doc.rust-lang.org/stable/book/ch17-01-what-is-oo.html">a chapter dedicated to it</a>. +Especially the <a href="https://doc.rust-lang.org/stable/book/ch17-03-oo-design-patterns.html#encoding-states-and-behavior-as-types">&quot;typestate&quot;</a> pattern is very interesting. +You can read more about it <a href="http://cliffle.com/blog/rust-typestate/">here</a>.</p> +<h2 id="how-to-build-a-good-library">How to build a good library</h2> +<p><a href="https://rust-lang.github.io/api-guidelines/about.html">These guidelines</a> have been created by the Rust library team.</p> +<h2 id="how-to-handle-errors">How to handle errors</h2> +<p><a href="https://nick.groenen.me/posts/rust-error-handling/">This post</a> is from 2020, but the libraries it mentions (<code>anyhow</code> and <code>thiserror</code>) are still the most popular.</p> +<h2 id="serde">Serde</h2> +<p><a href="https://serde.rs/">Serde</a> is the most popular serialization library for Rust.</p> +<h2 id="assignment">Assignment</h2> +<p>This week's assignment is to write a distributed calculator. +The details will be announced later, but you will have to base your solution on the <a href="https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html">final project from the book</a>.</p> + + + + + Fearless concurrency + 2022-04-25T00:00:00+00:00 + 2022-04-25T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/12-concurrency/ + + <h2 id="parallelism-vs-concurrency">Parallelism vs Concurrency</h2> +<p>Concurrency is when tasks <strong>can make</strong> progress <strong>independently</strong> of each other.</p> +<p>Parallelism is when multiple tasks <strong>make</strong> progress <strong>at the same time</strong>.</p> +<h2 id="concurrency-models-in-rust">Concurrency models in Rust</h2> +<h3 id="threads">Threads</h3> +<p>Nothing unusual here.</p> +<p>Threads can be created with the <code>thread::spawn</code> function <a href="https://doc.rust-lang.org/std/thread/fn.spawn.html">docs - please read them!</a>.</p> +<p>This method returns a <code>JoinHandle&lt;T&gt;</code> which can be used to wait for the thread to finish. <code>T</code> is the type of the thread's return value.</p> +<h4 id="propagating-panics">Propagating panics</h4> +<p>In Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).</p> +<h4 id="closures">Closures</h4> +<p>Closures which are used to create threads must take ownership of any values they use. It can be forced with the <code>move</code> keyword.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::thread; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> v </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> handle </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move </span><span style="color:#3e999f;">|| </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;Here&#39;s a vector: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, v); +</span><span> }); +</span><span> +</span><span> handle.</span><span style="color:#4271ae;">join</span><span>().</span><span style="color:#4271ae;">unwrap</span><span>(); +</span><span>} +</span></code></pre> +<p>Normal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!</p> +<p>But what if we need to share some state?</p> +<h3 id="message-passing">Message passing</h3> +<p>One possible way is to use message passing. We can use a blocking queue (called <code>mpsc</code> - <a href="https://doc.rust-lang.org/std/sync/mpsc/index.html">&quot;multi producer single consumer FIFO queue&quot;</a>) to do it. +We talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.</p> +<h3 id="mutexes">Mutexes</h3> +<p>In Rust, a mutex <em>wraps</em> a value and makes it thread-safe. +Because it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the <code>RefCell</code> type.</p> +<p><code>Arc</code> is a smart pointer like <code>Rc</code> but it can be shared between threads.</p> +<p>Please read more about them in <a href="https://doc.rust-lang.org/stable/book/ch16-03-shared-state.html">the book</a>.</p> +<p><a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html">The docs</a> also mention <code>poisoning</code>.</p> +<h3 id="rwlocks">RwLocks</h3> +<p><a href="https://doc.rust-lang.org/std/sync/struct.RwLock.html">RwLocks</a> are similar to mutexes, but they distinguish between read and write locks.</p> +<h2 id="send-and-sync">Send and Sync</h2> +<p>They are marker traits used to indicate that a type or a reference to it can be sent across threads. See the <a href="https://doc.rust-lang.org/nomicon/send-and-sync.html">nomicon</a> for more information.</p> +<h2 id="atomic-types">Atomic types</h2> +<p>Atomic types are described in <a href="https://doc.rust-lang.org/std/sync/atomic/">the docs</a>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::sync::Arc; +</span><span style="color:#8959a8;">use </span><span>std::sync::atomic::{AtomicUsize, Ordering}; +</span><span style="color:#8959a8;">use </span><span>std::{hint, thread}; +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(AtomicUsize::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span> </span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> spinlock_clone.</span><span style="color:#4271ae;">store</span><span>(</span><span style="color:#f5871f;">0</span><span>, Ordering::SeqCst); +</span><span> }); +</span><span> +</span><span> </span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span> </span><span style="color:#8959a8;">while</span><span> spinlock.</span><span style="color:#4271ae;">load</span><span>(Ordering::SeqCst) </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">if let </span><span style="color:#c99e00;">Err</span><span>(panic) </span><span style="color:#3e999f;">=</span><span> thread.</span><span style="color:#4271ae;">join</span><span>() { +</span><span> println!(</span><span style="color:#718c00;">&quot;Thread had an error: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, panic); +</span><span> } +</span><span>} +</span></code></pre> +<p>Note that <code>atomic</code> values don't have to be wrapped in a mutex when shared across threads.</p> +<h3 id="wait">Wait...</h3> +<p>If most types are <code>Sync + Send</code>, then what stops us from using a standard, non-atomic integer in the example above?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0594]: cannot assign to data in an `Arc` +</span><span> --&gt; src/main.rs:9:9 +</span><span> | +</span><span>9 | *spinlock_clone += 1; +</span><span> | ^^^^^^^^^^^^^^^^^^^^ cannot assign +</span><span> | +</span><span> = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc&lt;i32&gt;` +</span></code></pre> +<p>...so we would have to use a <code>RefCell</code> to be able to modify the value through a shared reference...</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> spinlock </span><span style="color:#3e999f;">= </span><span>Arc::new(RefCell::new(</span><span style="color:#f5871f;">1</span><span>)); +</span><span> +</span><span style="color:#8959a8;">let</span><span> spinlock_clone </span><span style="color:#3e999f;">= </span><span>Arc::clone(</span><span style="color:#3e999f;">&amp;</span><span>spinlock); +</span><span style="color:#8959a8;">let</span><span> thread </span><span style="color:#3e999f;">= </span><span>thread::spawn(</span><span style="color:#8959a8;">move</span><span style="color:#3e999f;">|| </span><span>{ +</span><span> </span><span style="color:#3e999f;">*</span><span>spinlock_clone.</span><span style="color:#4271ae;">borrow_mut</span><span>() </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>}); +</span><span> +</span><span style="color:#999999;">// Wait for the other thread to release the lock +</span><span style="color:#8959a8;">while </span><span style="color:#3e999f;">*</span><span>spinlock.</span><span style="color:#4271ae;">borrow</span><span>() </span><span style="color:#3e999f;">!= </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> hint::spin_loop(); +</span><span>} +</span></code></pre> +<p>...but <code>RefCell</code> isn't <code>Sync</code>:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0277]: `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> --&gt; src/main.rs:9:18 +</span><span> | +</span><span>9 | let thread = thread::spawn(move|| { +</span><span> | ^^^^^^^^^^^^^ `RefCell&lt;i32&gt;` cannot be shared between threads safely +</span><span> | +</span><span> = help: the trait `Sync` is not implemented for `RefCell&lt;i32&gt;` +</span><span> = note: required because of the requirements on the impl of `Send` for `Arc&lt;RefCell&lt;i32&gt;&gt;` +</span><span> = note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]` +</span><span>note: required by a bound in `spawn` +</span></code></pre> +<p>And that bound mentioned in the last line looks like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">spawn</span><span>&lt;F, T&gt;(</span><span style="color:#f5871f;">f</span><span>: F) -&gt; JoinHandle&lt;T&gt; </span><span style="color:#8959a8;">where +</span><span> F: FnOnce() -&gt; T, +</span><span> F: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span><span> T: Send + </span><span style="color:#8959a8;">&#39;static</span><span>, +</span></code></pre> +<h4 id="exercise-for-the-reader">Exercise for the reader</h4> +<p>Why is it impossible to share a reference to a <code>Mutex</code> between threads?</p> +<h2 id="data-parallelism-with-rayon">Data parallelism with Rayon</h2> +<p><a href="https://docs.rs/rayon/latest/rayon/">Rayon</a> is a library for parallelization of data processing. +It can be used to parallelize the execution of functions over a collection of data by switching the standard <code>Iterator</code> to a <code>ParallelIterator</code>. +It works very similar to <a href="https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#executing_streams_in_parallel">Java's parallel streams</a>.</p> +<p>Why do that? Because thread synchronization is hard! <a href="https://doc.rust-lang.org/nomicon/races.html">Rust prevents data races</a>, but <a href="https://users.rust-lang.org/t/deadlock-is-it-a-bug-or-is-it-intentional/1544">logical races and deadlocks are impossible to prevent!</a>!</p> +<p><a href="https://github.com/rayon-rs/rayon/blob/master/FAQ.md">Rayon's FAQ</a> is worth reading.</p> +<h2 id="assignment-5-graded">Assignment #5 (graded)</h2> +<p><a href="https://classroom.github.com/a/lKaeLu8O">Here</a> you can find the fifth graded assignment. The deadline for submissions is 04.05.2022.</p> + + + + + Small Task Feedback #3 + 2022-04-11T00:00:00+00:00 + 2022-04-11T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/11-feedback3/ + + <h2 id="iterators">Iterators</h2> +<h3 id="too-many-bools">Too many bools</h3> +<p>Many people implemented the InterleaveIterator like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">pub struct </span><span>InterleaveIterator&lt;I: </span><span style="color:#c99e00;">Iterator</span><span>, J: </span><span style="color:#c99e00;">Iterator</span><span>&gt; { +</span><span> </span><span style="color:#c82829;">iter1</span><span>: I, +</span><span> </span><span style="color:#c82829;">iter2</span><span>: J, +</span><span> </span><span style="color:#c82829;">not_finished1</span><span>: </span><span style="color:#8959a8;">bool</span><span>, +</span><span> </span><span style="color:#c82829;">not_finished2</span><span>: </span><span style="color:#8959a8;">bool</span><span>, +</span><span> </span><span style="color:#c82829;">turn_of_first</span><span>: </span><span style="color:#8959a8;">bool</span><span>, +</span><span>} +</span></code></pre> +<p>There's no need to use <code>bool</code>s to keep track of whether the iterators are finished. The contract of +the <code>Iterator</code> trait specifies that <code>next()</code></p> +<blockquote> +<p>returns None when iteration is finished. Individual iterator implementations may choose to resume iteration, +and so calling next() again may or may not eventually start returning Some(Item) again at some point.</p> +</blockquote> +<p>If you want to make sure that once the iterator returns None, it will always return None, you can +use the <code>fuse()</code> method.</p> +<h3 id="or-else">or_else</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;I, J&gt; Iterator </span><span style="color:#8959a8;">for </span><span>InterleaveIterator&lt;I, J&gt; +</span><span style="color:#999999;">// where etc. +</span><span>{ +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">next</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">Self::</span><span>Item&gt; { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> ret_val; +</span><span> +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#c82829;">self</span><span>.next_from_a { +</span><span> </span><span style="color:#f5871f;">true </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> ret_val </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.i.</span><span style="color:#4271ae;">next</span><span>(); +</span><span> </span><span style="color:#8959a8;">if</span><span> ret_val.</span><span style="color:#4271ae;">is_none</span><span>() { +</span><span> ret_val </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.j.</span><span style="color:#4271ae;">next</span><span>() +</span><span> } +</span><span> } +</span><span> </span><span style="color:#f5871f;">false </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> ret_val </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.j.</span><span style="color:#4271ae;">next</span><span>(); +</span><span> </span><span style="color:#8959a8;">if</span><span> ret_val.</span><span style="color:#4271ae;">is_none</span><span>() { +</span><span> ret_val </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self</span><span>.i.</span><span style="color:#4271ae;">next</span><span>() +</span><span> } +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#c82829;">self</span><span>.next_from_i </span><span style="color:#3e999f;">= !</span><span style="color:#c82829;">self</span><span>.next_from_i; +</span><span> +</span><span> ret_val +</span><span> } +</span><span>} +</span></code></pre> +<p>Even though in this definition we don't have the excessive <code>bool</code>s, +it can still be written a lot more concisely using <code>or_else</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;I, J&gt; Iterator </span><span style="color:#8959a8;">for </span><span>InterleaveIterator&lt;I, J&gt; +</span><span style="color:#999999;">// where etc. +</span><span>{ +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">next</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">Self::</span><span>Item&gt; { +</span><span> </span><span style="color:#c82829;">self</span><span>.next_from_i </span><span style="color:#3e999f;">= !</span><span style="color:#c82829;">self</span><span>.next_from_i; +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.next_from_i { +</span><span> </span><span style="color:#c82829;">self</span><span>.i.</span><span style="color:#4271ae;">next</span><span>().</span><span style="color:#4271ae;">or_else</span><span>(|| </span><span style="color:#c82829;">self</span><span>.j.</span><span style="color:#4271ae;">next</span><span>()) +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.j.</span><span style="color:#4271ae;">next</span><span>().</span><span style="color:#4271ae;">or_else</span><span>(|| </span><span style="color:#c82829;">self</span><span>.i.</span><span style="color:#4271ae;">next</span><span>()) +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="why-not-or">Why not <code>or</code>?</h3> +<p>The <code>or</code> method evaluates the argument even if it's not used (eager evaluation). +Because calling <code>self.i.next()</code> has side effects, this would create a bug.</p> +<h3 id="step-by">step_by</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Div&lt;</span><span style="color:#8959a8;">usize</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">div</span><span>(</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">rhs</span><span>: </span><span style="color:#8959a8;">usize</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Output { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> shreds </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self +</span><span> .shreds +</span><span> .</span><span style="color:#4271ae;">into_iter</span><span>() +</span><span> .</span><span style="color:#4271ae;">fold</span><span>(vec![], |</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">acc</span><span>, </span><span style="color:#f5871f;">el</span><span>| { +</span><span> acc.</span><span style="color:#4271ae;">push</span><span>((el, acc.</span><span style="color:#4271ae;">len</span><span>())); +</span><span> acc +</span><span> }) +</span><span> .</span><span style="color:#4271ae;">filter</span><span>(|(_, </span><span style="color:#f5871f;">nr</span><span>)| nr </span><span style="color:#3e999f;">%</span><span> rhs </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0</span><span>) +</span><span> .</span><span style="color:#4271ae;">map</span><span>(|(</span><span style="color:#f5871f;">el</span><span>, _)| el) +</span><span> .</span><span style="color:#4271ae;">collect</span><span>(); +</span><span> Shreds { shreds } +</span><span> } +</span><span>} +</span></code></pre> +<p>Instead of <code>fold</code> we can use <code>enumerate</code> to pair each element with its index.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Div&lt;</span><span style="color:#8959a8;">usize</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">div</span><span>(</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">rhs</span><span>: </span><span style="color:#8959a8;">usize</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Output { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> shreds </span><span style="color:#3e999f;">= </span><span style="color:#c82829;">self +</span><span> .shreds +</span><span> .</span><span style="color:#4271ae;">into_iter</span><span>() +</span><span> .</span><span style="color:#4271ae;">enumerate</span><span>() +</span><span> .</span><span style="color:#4271ae;">filter</span><span>(|(_, </span><span style="color:#f5871f;">nr</span><span>)| nr </span><span style="color:#3e999f;">%</span><span> rhs </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0</span><span>) +</span><span> .</span><span style="color:#4271ae;">map</span><span>(|(</span><span style="color:#f5871f;">el</span><span>, _)| el) +</span><span> .</span><span style="color:#4271ae;">collect</span><span>(); +</span><span> Shreds { shreds } +</span><span> } +</span><span>} +</span></code></pre> +<p>However, it can be simplified even more. What we're doing here is basically reimplementing +<code>step_by</code> :)</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; Div&lt;</span><span style="color:#8959a8;">usize</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Output </span><span style="color:#3e999f;">= </span><span>Shreds&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">div</span><span>(</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">rhs</span><span>: </span><span style="color:#8959a8;">usize</span><span>) -&gt; </span><span style="color:#8959a8;">Self::</span><span>Output { +</span><span> Shreds { +</span><span> shreds: </span><span style="color:#c82829;">self</span><span>.shreds.</span><span style="color:#4271ae;">into_iter</span><span>().</span><span style="color:#4271ae;">step_by</span><span>(rhs).</span><span style="color:#4271ae;">collect</span><span>(), +</span><span> } +</span><span> } +</span><span>} +</span></code></pre> +<h3 id="what-s-collect">What's <code>collect()</code>?</h3> +<p>It's not magic. We can collect the elements of an iterator into any type which implements +the appropriate <code>FromIterator</code> <a href="https://doc.rust-lang.org/std/iter/trait.FromIterator.html">trait</a>.</p> +<h2 id="shredding-usize">Shredding usize</h2> +<p>Instead of</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">impl </span><span>Shredder </span><span style="color:#8959a8;">for </span><span>usize { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">shred</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; Shreds { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> elements </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Vec</span><span>::new(); +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">to_string</span><span>().</span><span style="color:#4271ae;">chars</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> dig </span><span style="color:#3e999f;">=</span><span> i </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">usize </span><span style="color:#3e999f;">- </span><span style="color:#718c00;">&#39;0&#39; </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">usize</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> val_dig </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">crate</span><span>::value::Digit::new(dig); +</span><span> elements.</span><span style="color:#4271ae;">push</span><span>(Value::Digit(val_dig)); +</span><span> } +</span><span> Shreds { elements } +</span><span> } +</span><span>} +</span></code></pre> +<p>it's better to use the modulo operator and division to get the digits. Why? Converting a number to +string requires an additional heap allocation.</p> +<h2 id="make-illegal-states-unrepresentable">Make illegal states unrepresentable</h2> +<p>Some people used an i8 or some other integer type to keep track of whose turn it is. But the only +values that were ever used were 0 and 1. It means that there was a lot of cases where the +program would panic. Making it possible to encode an illegal state is +a <a href="https://en.wiktionary.org/wiki/footgun">footgun</a>. Using a <code>bool</code> is a better choice. What if there are more than two states? We can define a custom enum then.</p> + + + + + Smart Pointers + 2022-04-10T00:00:00+00:00 + 2022-04-10T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/ + + <h1 id="working-with-the-heap">Working with the heap</h1> +<p>So far we've only used heap allocated memory indirectly by working with containers such as vectors, maps or the <code>String</code> type, otherwise allocating our variables on the stack. We didn't really have to be aware of the fact that these collections used the heap, as all that memory management details were hidden away from us. In this lesson we'll take a closer look at what is really happening there and how we can do that ourselves.</p> +<p>To work with heap-allocated memory, Rust features <em>smart pointers</em>. You should have already heard this term as it is a very important feature in C++ and the concept is virtually the same here - they are wrappers around raw allocated memory that provide additional, safety-ensuring mechanism. What defines a smart pointer in Rust is generally the implementation of two traits: <code>Drop</code> and <code>Deref</code>.</p> +<p>The <code>Drop</code> trait is pretty straightforward as it consists of one method - <code>fn drop(&amp;mut self)</code> - that is, basically, the destructor, invoked during stack unwinding.</p> +<p>The <code>Deref</code> trait allows us to overload the dereference (<code>*</code>) operator.</p> +<h2 id="deref-coercion">Deref coercion</h2> +<p>Apart from enabling access to the underlying value, implementing the <code>Deref</code> trait enables Rust to perform <em>deref coercion</em> on the pointer - trying to remove as many levels of indirection as it can. What it means in practice is that we will be able to use it with any code working on plain references.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::ops::Deref; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>MyBox&lt;T&gt;(T); +</span><span> +</span><span style="color:#999999;">// We won&#39;t be allocating anything on the heap here as it is not important here. +</span><span style="color:#999999;">// We&#39;re only focusing on the dereference mechanisms. +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T) -&gt; MyBox&lt;T&gt; { +</span><span> MyBox(x) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Deref </span><span style="color:#8959a8;">for </span><span>MyBox&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">type </span><span>Target </span><span style="color:#3e999f;">=</span><span> T; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">deref</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">Self::</span><span>Target { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0 +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#f5871f;">name</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> int_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(x); +</span><span> +</span><span> assert_eq!(</span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#3e999f;">*</span><span>int_box); +</span><span> +</span><span> </span><span style="color:#999999;">// String also implements the `Deref` trait. +</span><span> </span><span style="color:#999999;">// In fact, String actually is a smart pointer. +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;I&#39;m a smart pointer too&quot;</span><span>); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> +</span><span> </span><span style="color:#999999;">// Deref coercion can deal with multiple levels of indirection. +</span><span> </span><span style="color:#8959a8;">let</span><span> str_box </span><span style="color:#3e999f;">= </span><span>MyBox::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Rust&quot;</span><span>)); +</span><span> </span><span style="color:#4271ae;">hello</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>str_box); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/deref_coercion.rs">deref_coercion.rs</a>)</sub></p> +<p>In general, there are three possible coercions that Rust can perform:</p> +<ul> +<li> +<p>From <code>&amp;T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;mut U</code> when <code>T: DerefMut&lt;Target=U&gt;</code></p> +</li> +<li> +<p>From <code>&amp;mut T</code> to <code>&amp;U</code> when <code>T: Deref&lt;Target=U&gt;</code></p> +</li> +</ul> +<p>While the first two coercions are straightforward, the third one is possible because treating a mutable reference as an immutable one does not break the rules of ownership.</p> +<h1 id="box-simple-wrapper"><code>Box</code> - simple wrapper</h1> +<p>The <code>Box&lt;T&gt;</code> type is the most basic out of Rust's smart pointers, equivalent to C++'s <code>std::unique_ptr&lt;T&gt;</code>. It's a simple wrapper that makes sure the underlying memory gets allocated and freed properly.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">box_simple</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> b </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Box</span><span>::new(</span><span style="color:#f5871f;">5</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, b); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10 </span><span style="color:#3e999f;">+ *</span><span>b; +</span><span>} +</span><span> +</span><span style="color:#999999;">// `Box` gives us the indirection required to define +</span><span style="color:#999999;">// recursive types +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">enum </span><span>List { +</span><span> Cons(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#c99e00;">Box</span><span>&lt;List&gt;), +</span><span> Nil, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#4271ae;">box_simple</span><span>(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/box.rs">box.rs</a>)</sub></p> +<h1 id="reference-counting">Reference counting</h1> +<p>The <code>Rc&lt;T&gt;</code> type is the equivalent of <code>std::shared_ptr&lt;T&gt;</code> from C++. There is one caveat to this though - because we're creating multiple references to the same object, those references have to be immutable in accordance with the ownership rules.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> outer_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> inner_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// strong_count represents the number of owning references pointing +</span><span> </span><span style="color:#999999;">// to data +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> outer_ref </span><span style="color:#3e999f;">= </span><span>Rc::clone(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref); +</span><span> +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref)); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>inner_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> still lives!&quot;</span><span>, outer_ref.</span><span style="color:#f5871f;">0</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>outer_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/ref_count.rs">ref_count.rs</a>)</sub></p> +<p>Rust also provides a non-owning pointer in the form of <code>Weak&lt;T&gt;</code> (equivalent to <code>std::weak_ptr&lt;T&gt;</code>) that can be obtained from an instance of <code>Rc&lt;T&gt;</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::rc::Rc; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>LoudInt(</span><span style="color:#8959a8;">i32</span><span>); +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Drop </span><span style="color:#8959a8;">for </span><span>LoudInt { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">drop</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[</span><span style="color:#666969;">{}</span><span style="color:#718c00;">] Farewell!&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> weak_ref; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> shared_ref </span><span style="color:#3e999f;">= </span><span>Rc::new(LoudInt(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// weak_count keeps track of the non-owning reference to the data +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">0</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// `downgrade()` obtains a weak pointer to Rc&#39;s data +</span><span> weak_ref </span><span style="color:#3e999f;">= </span><span>Rc::downgrade(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref); +</span><span> +</span><span> assert_eq!(Rc::weak_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">1</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// In order to use the the data underneath the weak pointer +</span><span> </span><span style="color:#999999;">// we need to obtain a new shared pointer from it. +</span><span> </span><span style="color:#999999;">// The `upgrade()` method returns `Option&lt;Rc&lt;T&gt;&gt;`. +</span><span> </span><span style="color:#8959a8;">let</span><span> temp </span><span style="color:#3e999f;">=</span><span> weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>(); +</span><span> assert_eq!(Rc::strong_count(</span><span style="color:#3e999f;">&amp;</span><span>shared_ref), </span><span style="color:#f5871f;">2</span><span>); +</span><span> println!(</span><span style="color:#718c00;">&quot;The value is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, temp.</span><span style="color:#4271ae;">unwrap</span><span>().</span><span style="color:#f5871f;">0</span><span>); +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;The value should be deallocated by now.&quot;</span><span>); +</span><span> matches!(weak_ref.</span><span style="color:#4271ae;">upgrade</span><span>(), </span><span style="color:#c99e00;">None</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/weak_ref.rs">weak_ref.rs</a>)</sub></p> +<h1 id="mutating-the-immutable">Mutating the immutable</h1> +<p>Good examples and explanation of the inferior mutability pattern and runtime borrow checking can be found in the <a href="https://doc.rust-lang.org/book/ch15-05-interior-mutability.html">book</a>.</p> +<p>Alongisde the <code>RefCell&lt;T&gt;</code> type described above, there is an analogous <a href="https://doc.rust-lang.org/std/cell/struct.Cell.html"><code>Cell&lt;T&gt;</code></a> type that operates on values instead of references.</p> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch15-00-smart-pointers.html">The Book, chapter 15</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html">std::borrow::Cow</a>, a versatile copy-on-write smart pointer</p> +</li> +</ul> +<h1 id="additional-reading">Additional reading</h1> +<ul> +<li><a href="https://www.fpcomplete.com/blog/rust-asref-asderef/">On wrapped references</a></li> +</ul> + + + + + Small Task Feedback #2 + 2022-04-04T00:00:00+00:00 + 2022-04-04T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/09-feedback2/ + + <h2 id="result-combinators">Result combinators</h2> +<p>Rust's <code>Result</code> type implements a lot of methods that simplify working with the two variants of the enum, especially when we're only interested in one of them. These methods are called <em>combinators</em>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> res: </span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// `map` allows us to transform the value inside `Ok()`, +</span><span> </span><span style="color:#999999;">// while leaving the `Err` untouched +</span><span> assert_eq!(res.</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">v</span><span>| v </span><span style="color:#3e999f;">*</span><span> v), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">25</span><span>)); +</span><span> +</span><span> res </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(res.</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">v</span><span>| v </span><span style="color:#3e999f;">*</span><span> v), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// With most combinators there are mirrored ones that work on `Err` +</span><span> </span><span style="color:#999999;">// variants instead of `Ok`s. +</span><span> assert_eq!(res.</span><span style="color:#4271ae;">map_err</span><span>(|</span><span style="color:#f5871f;">v</span><span>| v </span><span style="color:#3e999f;">*</span><span> v), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">25</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// We can swap an `Ok` value for a different one with `and()`. +</span><span> </span><span style="color:#999999;">// Analogously for `Err` and `or()`. +</span><span> res </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(res.</span><span style="color:#4271ae;">and</span><span>(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">100</span><span>)), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">100</span><span>)); +</span><span> +</span><span> res </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(res.</span><span style="color:#4271ae;">and</span><span>(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">100</span><span>)), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// `and_then()` and `or_else()` allow us to invoke functions +</span><span> </span><span style="color:#999999;">// only when the result is either an `Ok` or an `Err` respectively. +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">sq </span><span style="color:#3e999f;">= </span><span>|</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">i32</span><span>| -&gt; </span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>&gt; { </span><span style="color:#c99e00;">Ok</span><span>(x </span><span style="color:#3e999f;">*</span><span> x) }; +</span><span> </span><span style="color:#8959a8;">let </span><span style="color:#4271ae;">err </span><span style="color:#3e999f;">= </span><span>|</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">i32</span><span>| -&gt; </span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>&gt; { </span><span style="color:#c99e00;">Err</span><span>(x) }; +</span><span> +</span><span> assert_eq!(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>).</span><span style="color:#4271ae;">and_then</span><span>(sq).</span><span style="color:#4271ae;">and_then</span><span>(sq), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">16</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>).</span><span style="color:#4271ae;">and_then</span><span>(sq).</span><span style="color:#4271ae;">and_then</span><span>(err), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">4</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>).</span><span style="color:#4271ae;">and_then</span><span>(err).</span><span style="color:#4271ae;">and_then</span><span>(sq), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">3</span><span>).</span><span style="color:#4271ae;">and_then</span><span>(sq).</span><span style="color:#4271ae;">and_then</span><span>(sq), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> +</span><span> assert_eq!(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>).</span><span style="color:#4271ae;">or_else</span><span>(sq).</span><span style="color:#4271ae;">or_else</span><span>(sq), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>).</span><span style="color:#4271ae;">or_else</span><span>(err).</span><span style="color:#4271ae;">or_else</span><span>(sq), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">3</span><span>).</span><span style="color:#4271ae;">or_else</span><span>(sq).</span><span style="color:#4271ae;">or_else</span><span>(err), </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#f5871f;">9</span><span>)); +</span><span> assert_eq!(</span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">3</span><span>).</span><span style="color:#4271ae;">or_else</span><span>(err).</span><span style="color:#4271ae;">or_else</span><span>(err), </span><span style="color:#c99e00;">Err</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span>} +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/09-feedback2/combinators.rs">combinators.rs</a>)</sub></p> +<p>You can find more about the <code>Result</code> type and all its methods <a href="https://doc.rust-lang.org/std/result/enum.Result.html">here</a>.</p> +<h2 id="useful-hashmap-methods">Useful hashmap methods</h2> +<p>We can create a new hashmap in two ways in Rust - either an empty one using the <code>new()</code> method or from a list of key-value pairs using the <code>from()</code> method.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let mut</span><span> empty: HashMap&lt;</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>, </span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span>HashMap::new(); +</span><span style="color:#8959a8;">let</span><span> filled </span><span style="color:#3e999f;">= </span><span>HashMap::from([ +</span><span> (</span><span style="color:#718c00;">&quot;a&quot;</span><span>, </span><span style="color:#f5871f;">1</span><span>), +</span><span> (</span><span style="color:#718c00;">&quot;b&quot;</span><span>, </span><span style="color:#f5871f;">2</span><span>), +</span><span> (</span><span style="color:#718c00;">&quot;c&quot;</span><span>, </span><span style="color:#f5871f;">3</span><span>), +</span><span> (</span><span style="color:#718c00;">&quot;d&quot;</span><span>, </span><span style="color:#f5871f;">4</span><span>), +</span><span>]); +</span></code></pre> +<p><code>std::collections::Hashmap</code> implements the <code>IntoIterator</code> trait, but there are also other very handy methods for iterating over the collection. We can use the <code>values()</code> and <code>values_mut()</code> methods to just iterate over values inside the map and the <code>keys()</code> method to iterate only over the keys.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let mut</span><span> map </span><span style="color:#3e999f;">= </span><span>HashMap::from([(</span><span style="color:#718c00;">&quot;a&quot;</span><span>, </span><span style="color:#f5871f;">1</span><span>), (</span><span style="color:#718c00;">&quot;b&quot;</span><span>, </span><span style="color:#f5871f;">2</span><span>)]); +</span><span> +</span><span>map.</span><span style="color:#4271ae;">values</span><span>().</span><span style="color:#4271ae;">for_each</span><span>(|</span><span style="color:#f5871f;">v</span><span>| println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, v)); +</span><span> +</span><span style="color:#8959a8;">for</span><span> v </span><span style="color:#3e999f;">in</span><span> map.</span><span style="color:#4271ae;">values_mut</span><span>() { +</span><span> </span><span style="color:#3e999f;">*</span><span>v </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span>} +</span><span> +</span><span>map.</span><span style="color:#4271ae;">keys</span><span>().</span><span style="color:#4271ae;">filter</span><span>(|</span><span style="color:#f5871f;">key</span><span>| key.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1</span><span>).</span><span style="color:#4271ae;">count</span><span>(); +</span></code></pre> +<p>We can also consume all the key-value pairs from the map using <code>drain()</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::collections::HashMap; +</span><span> +</span><span style="color:#8959a8;">let mut</span><span> a </span><span style="color:#3e999f;">= </span><span>HashMap::new(); +</span><span>a.</span><span style="color:#4271ae;">insert</span><span>(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#718c00;">&quot;a&quot;</span><span>); +</span><span>a.</span><span style="color:#4271ae;">insert</span><span>(</span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#718c00;">&quot;b&quot;</span><span>); +</span><span> +</span><span style="color:#8959a8;">for </span><span>(k, v) </span><span style="color:#3e999f;">in</span><span> a.</span><span style="color:#4271ae;">drain</span><span>().</span><span style="color:#4271ae;">take</span><span>(</span><span style="color:#f5871f;">1</span><span>) { +</span><span> assert!(k </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">||</span><span> k </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">2</span><span>); +</span><span> assert!(v </span><span style="color:#3e999f;">== </span><span style="color:#718c00;">&quot;a&quot; </span><span style="color:#3e999f;">||</span><span> v </span><span style="color:#3e999f;">== </span><span style="color:#718c00;">&quot;b&quot;</span><span>); +</span><span>} +</span><span> +</span><span>assert!(a.</span><span style="color:#4271ae;">is_empty</span><span>()); +</span></code></pre> +<p>In the previous feedback you can also read about the <code>Entry</code> enum and how to work with it to access and modify values in a hashmap.</p> +<h2 id="matches-macro"><code>matches!()</code> macro</h2> +<p>Rust has a very convenient macro for checking whether something matches a given pattern. You can read more about the <code>matches!()</code> macro <a href="https://doc.rust-lang.org/std/macro.matches.html">here</a>.</p> +<h2 id="assignment-4-graded">Assignment #4 (graded)</h2> +<p><a href="https://classroom.github.com/a/jL6DS9YM">Here</a> you can find the fourth graded assignment. Deadline for submissions is 12.04.2022.</p> + + + + + Small Task Feedback #1 + 2022-03-29T00:00:00+00:00 + 2022-03-29T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/ + + <h2 id="constructors">Constructors</h2> +<p>There are no &quot;constructors&quot; in Rust. +<a href="https://doc.rust-lang.org/nomicon/constructors.html">There is exactly one way to create an instance of a user-defined type: name +it and initialize all its fields at once.</a></p> +<p>However, we often write associated functions that act as constructors.</p> +<p>In the below example you may notice a new keyword - <code>pub</code>. In Rust, almost everything is <em>private</em> by default (exceptions being items in a public <code>trait</code> and enum variants in a public <code>enum</code>). That means that it can be accessed from within the module it has been declared in, but cannot be accessed from the outside. So far we only worked with a singular file and no additional modules (apart from <code>tests</code>) so we didn't have to worry about it.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">mod </span><span>one { +</span><span> </span><span style="color:#8959a8;">pub struct </span><span>Point { +</span><span> </span><span style="color:#c82829;">x</span><span>: </span><span style="color:#8959a8;">i32</span><span>, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">impl </span><span>Point { +</span><span> </span><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; Point { +</span><span> Point { x } +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">pub fn </span><span style="color:#4271ae;">x</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">impl </span><span>Default </span><span style="color:#8959a8;">for </span><span>Point { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">default</span><span>() -&gt; Point { +</span><span> Point { x: </span><span style="color:#f5871f;">10 </span><span>} +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// won&#39;t compile, can&#39;t initialize private fields +</span><span> </span><span style="color:#999999;">// let p = one::Point { +</span><span> </span><span style="color:#999999;">// x: 1, +</span><span> </span><span style="color:#999999;">// }; +</span><span> </span><span style="color:#8959a8;">let</span><span> p </span><span style="color:#3e999f;">= </span><span>one::Point::new(</span><span style="color:#f5871f;">1</span><span>); +</span><span> </span><span style="color:#999999;">// won&#39;t compile, x is private +</span><span> </span><span style="color:#999999;">// println!(&quot;{}&quot;, p.x); +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, p.</span><span style="color:#4271ae;">x</span><span>()); +</span><span> </span><span style="color:#8959a8;">let</span><span> p </span><span style="color:#3e999f;">= </span><span>one::Point::default(); +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, p.</span><span style="color:#4271ae;">x</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/constructor.rs">constructor.rs</a>)</sub></p> +<h2 id="field-initialization-shorthand">Field initialization shorthand</h2> +<p>Note that in the above example, we wrote</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Point { x } +</span></code></pre> +<p>instead of</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>Point { x: x } +</span></code></pre> +<h2 id="destructors">Destructors</h2> +<p>If we need a custom destructor, we can implement the <code>Drop</code> trait. +Read more <a href="https://doc.rust-lang.org/stable/reference/destructors.html">here</a>.</p> +<h2 id="naming">Naming</h2> +<p>Refer to <a href="https://rust-lang.github.io/api-guidelines/naming.html">this guideline</a> for naming conventions.</p> +<h2 id="underscore">Underscore</h2> +<p>Underscore is used to mark variables that are unused.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">example</span><span>(</span><span style="color:#f5871f;">used</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">_unused_param</span><span>: </span><span style="color:#8959a8;">i32</span><span>) { +</span><span> </span><span style="color:#8959a8;">let</span><span> _unused </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">match</span><span> used { +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>unimplemented!(), +</span><span> } +</span><span>} +</span></code></pre> +<p>It should not be used in other contexts.</p> +<h2 id="hashmap-entry">Hashmap entry</h2> +<p><a href="https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.entry">entry</a> is a convenient way to get a mutable reference to a value in a hashmap.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let mut</span><span> map </span><span style="color:#3e999f;">= </span><span>std::collections::HashMap::new(); +</span><span style="color:#3e999f;">*</span><span>map.</span><span style="color:#4271ae;">entry</span><span>(</span><span style="color:#718c00;">&quot;key&quot;</span><span>).</span><span style="color:#4271ae;">or_insert</span><span>(</span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span style="color:#3e999f;">*</span><span>map.</span><span style="color:#4271ae;">entry</span><span>(</span><span style="color:#718c00;">&quot;key&quot;</span><span>).</span><span style="color:#4271ae;">or_insert</span><span>(</span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span>assert_eq!(map[</span><span style="color:#718c00;">&quot;key&quot;</span><span>], </span><span style="color:#f5871f;">2</span><span>); +</span></code></pre> +<h2 id="number-conversions">Number conversions</h2> +<p>Number conversions are... not the most intuitive.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> small_number: </span><span style="color:#8959a8;">u32 </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">u32</span><span>::</span><span style="color:#666969;">MAX</span><span>; +</span><span> </span><span style="color:#999999;">// dbg!(small_number + 1); // this will panic (in debug builds, in release build it will wrap) +</span><span> assert_eq!(small_number </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">u8</span><span>, </span><span style="color:#f5871f;">255</span><span>); +</span><span> assert_eq!(small_number </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">i8</span><span>, </span><span style="color:#3e999f;">-</span><span style="color:#f5871f;">1</span><span>); +</span><span> assert_eq!(small_number </span><span style="color:#3e999f;">as </span><span style="color:#8959a8;">i64</span><span>, </span><span style="color:#f5871f;">4294967295</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> converted: </span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#8959a8;">i8</span><span>, </span><span style="color:#3e999f;">_</span><span>&gt; </span><span style="color:#3e999f;">=</span><span> small_number.</span><span style="color:#4271ae;">try_into</span><span>(); +</span><span> assert!(converted.</span><span style="color:#4271ae;">is_err</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/number_conversions.rs">number_conversions.rs</a>)</sub></p> +<h2 id="use-rustfmt-in-clion">Use rustfmt in Clion</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/rustfmt.png" alt="" /></p> +<h2 id="use-clippy-in-clion">Use clippy in Clion</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/clippy.png" alt="" /></p> +<h2 id="clippy-auto-fixing">Clippy auto-fixing</h2> +<p>Clippy can automagically fix some of the issues it detects if run with <code>cargo clippy --fix</code></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html">The Book, chapter 7</a></li> +</ul> + + + + + Closures and Iterators + 2022-03-27T00:00:00+00:00 + 2022-03-27T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/08-closures-iterators/ + + <h1 id="closures">Closures</h1> +<p>Closures (Polish: &quot;domknięcia&quot;) are anonymous functions that can access variables from the scope in which they were defined.</p> +<p>We'll go through the examples from <a href="https://doc.rust-lang.org/rust-by-example/fn/closures.html">Rust by Example</a>.</p> +<h1 id="iterators">Iterators</h1> +<p>In Rust, there is no hierarchy of types for collections (because there is no inheritance in general). +Instead, what makes a collection is that it can be iterated over.</p> +<p>We'll go through the official <a href="https://doc.rust-lang.org/stable/std/iter/">docs</a>. +Most methods are defined in the <a href="https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html">Iterator trait</a>.</p> +<h1 id="reading">Reading</h1> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch12-00-an-io-project.html">The Book, chapter 12 (that's a project!)</a></li> +<li><a href="https://doc.rust-lang.org/book/ch13-00-functional-features.html">The Book, chapter 13</a></li> +<li><a href="https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html">The Book, chapter 14</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-05-advanced-functions-and-closures.html">The Book, Advanced Functions and Closures</a></li> +<li><a href="https://doc.rust-lang.org/stable/book/ch19-03-advanced-traits.html">The Book, Advanced Traits</a></li> +</ul> +<h1 id="assignment-3-graded">Assignment #3 (graded)</h1> +<p><a href="https://classroom.github.com/a/OLqc7DAh">Here</a> you can find the third graded assignment. Deadline for submissions is 05.04.2022.</p> + + + + + Reasoning About Types + 2022-03-21T00:00:00+00:00 + 2022-03-21T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/ + + <h1 id="type-traits">Type traits</h1> +<p>Traits are a way to defined common behavior between different types. They can be compared to <em>interfaces</em> from many other mainstream languages or to typeclasses from Haskell, however, Rust is not an object-oriented language and there are some notable differences between type traits and typeclasses.</p> +<p>The way we describe behavior in Rust is through methods. Traits consist of a set of these methods which then should be implemented by a type. We've already encountered examples of these, like the <code>Clone</code> trait which specified that the <code>clone()</code> method can be called on some given type. Now, let's take a deeper look and try defining our own trait.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Summary { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>NewsArticle { +</span><span> </span><span style="color:#c82829;">headline</span><span>: String, +</span><span> </span><span style="color:#c82829;">location</span><span>: String, +</span><span> </span><span style="color:#c82829;">author</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>NewsArticle { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">, by </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.headline, </span><span style="color:#c82829;">self</span><span>.author, </span><span style="color:#c82829;">self</span><span>.location) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Tweet { +</span><span> </span><span style="color:#c82829;">username</span><span>: String, +</span><span> </span><span style="color:#c82829;">content</span><span>: String, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Summary </span><span style="color:#8959a8;">for </span><span>Tweet { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">summarize</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.username, </span><span style="color:#c82829;">self</span><span>.content) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> tweet </span><span style="color:#3e999f;">=</span><span> Tweet { +</span><span> username: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;horse_ebooks&quot;</span><span>), +</span><span> content: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;of course, as you probably already know, people&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;1 new tweet: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, tweet.</span><span style="color:#4271ae;">summarize</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/basic_trait.rs">basic_trait.rs</a>)</sub></p> +<h2 id="default-implementations">Default implementations</h2> +<p>Trait definitions can also be provided with default implementations of behaviors.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Upload { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span style="color:#8959a8;">struct </span><span>Photo { +</span><span> </span><span style="color:#c82829;">filename</span><span>: String, +</span><span> </span><span style="color:#c82829;">width</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">height</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span>} +</span><span> +</span><span style="color:#8959a8;">trait </span><span>Description { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;No description available.&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// All default implementations +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Upload {} +</span><span> +</span><span style="color:#999999;">// Default implementations can be overwritten +</span><span style="color:#8959a8;">impl </span><span>Description </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">describe</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; String { +</span><span> format!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> (</span><span style="color:#666969;">{}</span><span style="color:#718c00;"> x </span><span style="color:#666969;">{}</span><span style="color:#718c00;">)&quot;</span><span>, </span><span style="color:#c82829;">self</span><span>.filename, </span><span style="color:#c82829;">self</span><span>.width, </span><span style="color:#c82829;">self</span><span>.height) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Default implementations can rely on methods with no defaults +</span><span style="color:#8959a8;">trait </span><span>Size { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">size</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">width</span><span>() </span><span style="color:#3e999f;">* </span><span style="color:#c82829;">self</span><span>.</span><span style="color:#4271ae;">height</span><span>() +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Size </span><span style="color:#8959a8;">for </span><span>Photo { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">width</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.width +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">height</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.height +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Using default impl of `size()` +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> upload </span><span style="color:#3e999f;">=</span><span> Upload { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;notes.txt&quot;</span><span>), +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Upload: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, upload.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> photo </span><span style="color:#3e999f;">=</span><span> Photo { +</span><span> filename: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;stock_crustacean.png&quot;</span><span>), +</span><span> width: </span><span style="color:#f5871f;">100</span><span>, +</span><span> height: </span><span style="color:#f5871f;">150</span><span>, +</span><span> }; +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Photo: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">describe</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;Size: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, photo.</span><span style="color:#4271ae;">size</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/trait_default.rs">trait_default.rs</a>)</sub></p> +<h2 id="what-about-derive">What about <em>derive</em>?</h2> +<p>There is a trait-related thing we have used quite extensively and not explained yet, namely the <code>#[derive]</code> attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.</p> +<p>Derivable traits:</p> +<ul> +<li> +<p>Equality traits: <code>Eq</code>, <code>PartialEq</code> and comparison traits: <code>Ord</code> and <code>PartialOrd</code>. The <code>Partial-</code> versions exist because there are types which don't fulfill the reflexivity requirement of equality (<code>NaN != NaN</code>) or do not form a total order (<code> NaN &lt; 0.0 == false</code> and <code>NaN &gt;= 0.0 == false</code>).</p> +</li> +<li> +<p>Data duplication traits: <code>Clone</code> and <code>Copy</code></p> +</li> +<li> +<p><code>Hash</code> - allows using values of that type as keys in a hashmap</p> +</li> +<li> +<p><code>Default</code> - provides a zero-arg constructor function</p> +</li> +<li> +<p><code>Debug</code> - provides a formatting of the value which can be used in debugging context. It should <em>NOT</em> be implemented manually. In general, if it's possible to derive the <code>Debug</code>, there are no reasons against doing it.</p> +</li> +</ul> +<h3 id="when-is-it-possible-to-derive-a-trait">When is it possible to derive a trait?</h3> +<p>When all fields of a struct/variants of an enum implement that trait.</p> +<h3 id="should-all-traits-always-be-derived-if-it-is-possible">Should all traits always be derived if it is possible?</h3> +<p>No. Although it may be tempting to just slap <code>#[derive(Clone, Copy)]</code> everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.</p> +<h1 id="generics">Generics</h1> +<p>Suppose we want to find the largest element in a sequence and return it. Very much on purpose, we didn't specify what type these elements would be - ideally, we would love it to work on all types that have a defined notion of a <em>largest</em> element. However, to make things simpler for now, let's focus only on two primitive types: <code>i32</code> and <code>char</code>. Let's try to write the code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>]) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">char</span><span>]) -&gt; </span><span style="color:#8959a8;">char </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_i32</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest_char</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/non_generic.rs">non_generic.rs</a>)</sub></p> +<p>Perfect, it works! Now only twenty more types to go...</p> +<p>Fortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>Cleaner already - we merged possibly very many implementations into one. But, when we try to compile this:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0369]: binary operation `&gt;` cannot be applied to type `T` +</span><span> --&gt; src/main.rs:5:17 +</span><span> | +</span><span>5 | if item &gt; largest { +</span><span> | ---- ^ ------- T +</span><span> | | +</span><span> | T +</span><span> | +</span><span>help: consider restricting type parameter `T` +</span><span> | +</span><span>1 | fn largest&lt;T: std::cmp::PartialOrd&gt;(list: &amp;[T]) -&gt; T { +</span><span> | ++++++++++++++++++++++ +</span></code></pre> +<p>Since <code>T</code> can be of absolutely any type now, the compiler cannot be sure that operator <code>&gt;</code> is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span></code></pre> +<p>We call this a <em>trait bound</em>, a way to provide constraints on what kind of types we are talking about in a given context. This implementation almost works now. Let's look at the new error.</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0508]: cannot move out of type `[T]`, a non-copy slice +</span><span> --&gt; src/main.rs:2:23 +</span><span> | +</span><span>2 | let mut largest = list[0]; +</span><span> | ^^^^^^^ +</span><span> | | +</span><span> | cannot move out of here +</span><span> | move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider borrowing here: `&amp;list[0]` +</span><span> +</span><span>error[E0507]: cannot move out of a shared reference +</span><span> --&gt; src/main.rs:4:18 +</span><span> | +</span><span>4 | for &amp;item in list { +</span><span> | ----- ^^^^ +</span><span> | || +</span><span> | |data moved here +</span><span> | |move occurs because `item` has type `T`, which does not implement the `Copy` trait +</span><span> | help: consider removing the `&amp;`: `item` +</span></code></pre> +<p>Our function attempts to take ownership, but, again, the compiler doesn't know whether <code>T</code> can just be trivially copied. Rust allows us to combine multiple trait bounds together:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt;(</span><span style="color:#f5871f;">list</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[T]) -&gt; T { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> largest </span><span style="color:#3e999f;">=</span><span> list[</span><span style="color:#f5871f;">0</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">for </span><span style="color:#3e999f;">&amp;</span><span>item </span><span style="color:#3e999f;">in</span><span> list { +</span><span> </span><span style="color:#8959a8;">if</span><span> item </span><span style="color:#3e999f;">&gt;</span><span> largest { +</span><span> largest </span><span style="color:#3e999f;">=</span><span> item; +</span><span> } +</span><span> } +</span><span> +</span><span> largest +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> number_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">34</span><span>, </span><span style="color:#f5871f;">50</span><span>, </span><span style="color:#f5871f;">25</span><span>, </span><span style="color:#f5871f;">100</span><span>, </span><span style="color:#f5871f;">65</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>number_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest number is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> char_list </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#718c00;">&#39;y&#39;</span><span>, </span><span style="color:#718c00;">&#39;m&#39;</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>, </span><span style="color:#718c00;">&#39;q&#39;</span><span>]; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>char_list); +</span><span> println!(</span><span style="color:#718c00;">&quot;The largest char is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/generic_largest.rs">generic_largest.rs</a>)</sub></p> +<h2 id="a-powerful-tool">A powerful tool</h2> +<p>There's a lot more that we can do with generics:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fmt::Debug; +</span><span> +</span><span style="color:#999999;">// generic enums +</span><span style="color:#8959a8;">enum </span><span>OurOption&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic structs +</span><span style="color:#8959a8;">struct </span><span>Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: U, +</span><span>} +</span><span> +</span><span style="color:#999999;">// generic implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T, U&gt; Tuple2&lt;T, U&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">x</span><span>: T, </span><span style="color:#f5871f;">y</span><span>: U) -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> </span><span style="color:#8959a8;">Self </span><span>{ x, y } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Pair&lt;T&gt; { +</span><span> </span><span style="color:#c82829;">x</span><span>: T, +</span><span> </span><span style="color:#c82829;">y</span><span>: T, +</span><span>} +</span><span> +</span><span style="color:#999999;">// conditional implementation +</span><span style="color:#8959a8;">impl</span><span>&lt;T: </span><span style="color:#c99e00;">PartialOrd </span><span style="color:#3e999f;">+ </span><span style="color:#c99e00;">Copy</span><span>&gt; Pair&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">largest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&gt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// alternative syntax +</span><span style="color:#8959a8;">impl</span><span>&lt;T&gt; Pair&lt;T&gt; +</span><span style="color:#8959a8;">where +</span><span> T: PartialOrd + Copy, +</span><span>{ +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">smallest</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; T { +</span><span> </span><span style="color:#8959a8;">if </span><span style="color:#c82829;">self</span><span>.x </span><span style="color:#3e999f;">&lt; </span><span style="color:#c82829;">self</span><span>.y { +</span><span> </span><span style="color:#c82829;">self</span><span>.x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c82829;">self</span><span>.y +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// Here information about the concrete underlying type is erased +</span><span style="color:#999999;">// We can only either format or clone the result +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">cloning_machine</span><span>(</span><span style="color:#f5871f;">item</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>(</span><span style="color:#f5871f;">impl Clone</span><span> + </span><span style="color:#f5871f;">Debug</span><span>)) -&gt; impl Clone </span><span style="color:#3e999f;">+</span><span> Debug { +</span><span> item.</span><span style="color:#4271ae;">clone</span><span>() +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> _opt </span><span style="color:#3e999f;">= </span><span>OurOption::Some(</span><span style="color:#f5871f;">10</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> _p1 </span><span style="color:#3e999f;">=</span><span> Tuple2 { x: </span><span style="color:#f5871f;">5</span><span>, y: </span><span style="color:#f5871f;">10 </span><span>}; +</span><span> </span><span style="color:#8959a8;">let</span><span> _p2 </span><span style="color:#3e999f;">= </span><span>Tuple2::new(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2.5</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> arr </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>]; +</span><span> </span><span style="color:#8959a8;">let</span><span> arr2 </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">cloning_machine</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>arr); +</span><span> </span><span style="color:#999999;">// arr2[0]; // won&#39;t compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, arr2) +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/generics.rs">generics.rs</a>)</sub></p> +<p>A bit more involved example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">use </span><span>std::fmt::{Display, Formatter}; +</span><span> +</span><span style="color:#8959a8;">trait </span><span>DefaultishablyPrintable&lt;T&gt; { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">defaultish_print</span><span>() +</span><span> </span><span style="color:#8959a8;">where +</span><span> T: Display + Default, +</span><span> { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, T::default()) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Foo; +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Bar; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Display </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">fmt</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">f</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span>Formatter&lt;&#39;</span><span style="color:#3e999f;">_</span><span>&gt;) -&gt; std::fmt::Result { +</span><span> f.</span><span style="color:#4271ae;">write_str</span><span>(</span><span style="color:#718c00;">&quot;this is a bar&quot;</span><span>) +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Default </span><span style="color:#8959a8;">for </span><span>Bar { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">default</span><span>() -&gt; </span><span style="color:#8959a8;">Self </span><span>{ +</span><span> Bar </span><span style="color:#999999;">// well, we have no other choice +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">impl </span><span>DefaultishablyPrintable&lt;Bar&gt; </span><span style="color:#8959a8;">for </span><span>Foo {} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#3e999f;">&lt;</span><span>Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;</span><span style="color:#3e999f;">&gt;</span><span>::defaultish_print(); +</span><span> &lt;Foo </span><span style="color:#3e999f;">as </span><span>DefaultishablyPrintable&lt;Bar&gt;&gt;::defaultish_print(); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/generics_fun.rs">generics_fun.rs</a>)</sub></p> +<h2 id="static-vs-dynamic-dispatch">Static vs dynamic dispatch</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">trait </span><span>Speak { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Dog; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Dog { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hau hau&quot; </span><span style="color:#999999;">// it&#39;s a Polish dog! +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">struct </span><span>Human; +</span><span> +</span><span style="color:#8959a8;">impl </span><span>Speak </span><span style="color:#8959a8;">for </span><span>Human { +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">speak</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;Hello world&quot; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// It works like templates in C++ +</span><span style="color:#999999;">// A different function will be generated for each T during compilation +</span><span style="color:#999999;">// This process is called &quot;monomorphization&quot; +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">static_dispatch</span><span>&lt;T: Speak&gt;(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>T) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#999999;">// Only one copy of that function will exist in the compiled binary +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#f5871f;">speaking</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>dyn Speak) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, speaking.</span><span style="color:#4271ae;">speak</span><span>()); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> dog </span><span style="color:#3e999f;">=</span><span> Dog; +</span><span> </span><span style="color:#8959a8;">let</span><span> human </span><span style="color:#3e999f;">=</span><span> Human; +</span><span> +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">static_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>dog); +</span><span> </span><span style="color:#4271ae;">dynamic_dispatch</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>human); +</span><span> +</span><span> </span><span style="color:#999999;">// The observable behavior is identical +</span><span> </span><span style="color:#999999;">// Static dispatch in general is a bit faster, +</span><span> </span><span style="color:#999999;">// because there is no need to perform a &quot;vtable lookup&quot;. +</span><span> </span><span style="color:#999999;">// But it can also result in bigger binary sizes. +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/static_dynamic_dispatch.rs">static_dynamic_dispatch.rs</a>)</sub></p> +<h1 id="lifetimes">Lifetimes</h1> +<p>Going back to the lesson about ownership, if we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; +</span><span> } +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); +</span><span>} +</span></code></pre> +<p>we should expect to get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `x` does not live long enough +</span><span> --&gt; src/main.rs:7:17 +</span><span> | +</span><span>7 | r = &amp;x; +</span><span> | ^^ borrowed value does not live long enough +</span><span>8 | } +</span><span> | - `x` dropped here while still borrowed +</span><span>9 | +</span><span>10 | println!(&quot;r: {}&quot;, r); +</span><span> | - borrow later used here +</span></code></pre> +<p>Courtesy of the borrow checker, we didn't end up with a dangling reference. But what exactly is happening behind the scenes? Rust introduces a concept of annotated lifetimes, where the lifetime of each value is being marked and tracked by the checker. Let's look at some examples:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> r; </span><span style="color:#999999;">// ---------+-- &#39;a +</span><span> </span><span style="color:#999999;">// | +</span><span> { </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// -+-- &#39;b | +</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// | | +</span><span> } </span><span style="color:#999999;">// -+ | +</span><span> </span><span style="color:#999999;">// | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | +</span><span>} </span><span style="color:#999999;">// ---------+ +</span></code></pre> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// ----------+-- &#39;b +</span><span> </span><span style="color:#999999;">// | +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>x; </span><span style="color:#999999;">// --+-- &#39;a | +</span><span> </span><span style="color:#999999;">// | | +</span><span> println!(</span><span style="color:#718c00;">&quot;r: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, r); </span><span style="color:#999999;">// | | +</span><span> </span><span style="color:#999999;">// --+ | +</span><span>} </span><span style="color:#999999;">// ----------+ +</span></code></pre> +<h2 id="annotations">Annotations</h2> +<p>Let's consider the following code finding the longer out of two strings:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;abcd&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;xyz&quot;</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span>} +</span></code></pre> +<p>If we try to compile this, we will get an error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:9:33 +</span><span> | +</span><span>9 | fn longest(x: &amp;str, y: &amp;str) -&gt; &amp;str { +</span><span> | ---- ---- ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` +</span><span>help: consider introducing a named lifetime parameter +</span><span> | +</span><span>9 | fn longest&lt;&#39;a&gt;(x: &amp;&#39;a str, y: &amp;&#39;a str) -&gt; &amp;&#39;a str { +</span><span> | ++++ ++ ++ ++ +</span></code></pre> +<p>This is because Rust doesn't know which of the two provided strings (<code>x</code> or <code>y</code>) will be returned from the function. And because they potentially have different lifetimes, the lifetime of what we are returning remains unclear to the compiler - it needs our help.</p> +<p>Rust provides syntax for specifying lifetimes. The lifetime parameter name from the example (<code>a</code>) doesn't have any concrete meaning - it's just an arbitrary name for this one lifetime.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#999999;">// a reference +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a i32 </span><span style="color:#999999;">// a reference with an explicit lifetime +</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a mut i32 </span><span style="color:#999999;">// a mutable reference with an explicit lifetime +</span></code></pre> +<p>So, knowing this, let's address the compiler's demands.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">y</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> x.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> y.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> x +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> y +</span><span> } +</span><span>} +</span></code></pre> +<p>When working with lifetimes, our work will usually revolve around specifying relationships between lifetimes of different values so that the compiler can successfully reason about the program's safety. In the context of the example above, this signature means that both of the function's arguments and its output will live at least as long as lifetime <code>'a</code>. In practice, this means that the output's lifetime will be equal to the smaller of the two inputs' lifetimes.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">longest</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">first</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>, </span><span style="color:#f5871f;">second</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str</span><span>) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a str </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> first.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&gt;</span><span> second.</span><span style="color:#4271ae;">len</span><span>() { +</span><span> first +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> second +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> string1 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;long string is long&quot;</span><span>); +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> string2 </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;xyz&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> result </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">longest</span><span>(string1.</span><span style="color:#4271ae;">as_str</span><span>(), string2.</span><span style="color:#4271ae;">as_str</span><span>()); +</span><span> println!(</span><span style="color:#718c00;">&quot;The longest string is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, result); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// This doesn&#39;t compile - incorrect lifetimes +</span><span> </span><span style="color:#999999;">// +</span><span> </span><span style="color:#999999;">// let string1 = String::from(&quot;long string is long&quot;); +</span><span> </span><span style="color:#999999;">// let result; +</span><span> </span><span style="color:#999999;">// { +</span><span> </span><span style="color:#999999;">// let string2 = String::from(&quot;xyz&quot;); +</span><span> </span><span style="color:#999999;">// result = longest(string1.as_str(), string2.as_str()); +</span><span> </span><span style="color:#999999;">// } +</span><span> </span><span style="color:#999999;">// println!(&quot;The longest string is {}&quot;, result); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/lifetimes_basic.rs">lifetimes_basic.rs</a>)</sub></p> +<p>Trying to compile the second variant displeases the compiler (just like we hoped).</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0597]: `string2` does not live long enough +</span><span> --&gt; src/main.rs:6:44 +</span><span> | +</span><span>6 | result = longest(string1.as_str(), string2.as_str()); +</span><span> | ^^^^^^^^^^^^^^^^ borrowed value does not live long enough +</span><span>7 | } +</span><span> | - `string2` dropped here while still borrowed +</span><span>8 | println!(&quot;The longest string is {}&quot;, result); +</span><span> | ------ borrow later used here +</span></code></pre> +<h2 id="lifetime-elision">Lifetime elision</h2> +<p>We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this <em>lifetime elision</em>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span><span> </span><span style="color:#8959a8;">if</span><span> seq.</span><span style="color:#4271ae;">len</span><span>() </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> seq +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">2</span><span>] +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> seq </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>]; +</span><span> +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;First two elements of the sequence: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, +</span><span> </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>seq[</span><span style="color:#3e999f;">..</span><span>]) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/lifetimes_elision.rs">lifetimes_elision.rs</a>)</sub></p> +<p>The above works, even though we didn't specify any lifetime parameters at all. The reason lies in the rules we mentioned, which are as follows (where input lifetimes are lifetimes on parameters and output lifetimes are lifetimes on return values):</p> +<ul> +<li> +<p>Each parameter that is a reference gets its own lifetime parameter.</p> +</li> +<li> +<p>If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.</p> +</li> +<li> +<p>If there are multiple input lifetime parameters, but one of them is <code>&amp;self</code> or <code>&amp;mut self</code>, the lifetime of <code>self</code> is assigned to all output lifetime parameters.</p> +</li> +</ul> +<p>Let's try to understand how the compiler inferred the lifetimes of our <code>first_two</code> functions. We start with the following signature:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Then, we apply the first rule:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>Next, we check the second rule. It applies here as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_two</span><span>&lt;</span><span style="color:#8959a8;">&#39;a</span><span>&gt;(</span><span style="color:#f5871f;">seq</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a</span><span> [</span><span style="color:#8959a8;">u32</span><span>]) -&gt; </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;a </span><span>[</span><span style="color:#8959a8;">u32</span><span>] { +</span></code></pre> +<p>With that, we arrive at a state where all lifetimes are specified.</p> +<h2 id="static-lifetime">Static lifetime</h2> +<p>There exists one special lifetime called <code>'static</code>, which means that a reference can live for the entire duration of the program. All string literals are annotated with this lifetime as they are stored directly in the program's binary. Full type annotation of a string literal in Rust is therefore as follows:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;I have a static lifetime.&quot;</span><span>; +</span></code></pre> +<h1 id="obligatory-reading">Obligatory reading</h1> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/book/ch10-00-generics.html">The Book, chapter 10</a></p> +</li> +<li> +<p><a href="https://oswalt.dev/2021/06/polymorphism-in-rust/">Polymorphism in Rust</a></p> +</li> +</ul> + + + + + Enums + 2022-03-13T00:00:00+00:00 + 2022-03-13T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/ + + <h2 id="enums">Enums</h2> +<p>It is often the case that we want to define a variable that can only take +a certain set of values and the values are known up front. In C you can an <code>enum</code> for this.</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;stdio.h&gt; +</span><span> +</span><span style="color:#8959a8;">enum </span><span>shirt_size { +</span><span> small, +</span><span> medium, +</span><span> large, +</span><span> xlarge +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_size</span><span>(</span><span style="color:#8959a8;">enum</span><span> shirt_size </span><span style="color:#f5871f;">size</span><span>) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;my size is &quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> </span><span style="color:#8959a8;">if </span><span>(size </span><span style="color:#3e999f;">==</span><span> small) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;small&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> } </span><span style="color:#8959a8;">else if </span><span>(size </span><span style="color:#3e999f;">==</span><span> medium) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;medium&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> } </span><span style="color:#8959a8;">else if </span><span>(size </span><span style="color:#3e999f;">==</span><span> large) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;large&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> } </span><span style="color:#8959a8;">else if </span><span>(size </span><span style="color:#3e999f;">==</span><span> xlarge) { +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;xlarge&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;unknown&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span> } +</span><span> </span><span style="color:#4271ae;">printf(</span><span style="color:#718c00;">&quot;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&quot;</span><span style="color:#4271ae;">)</span><span>; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">=</span><span> medium; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/enums.c">enums.c</a>)</sub></p> +<p>However, in C enums are just integers. Nothing prevents us from writing</p> +<pre data-lang="c" style="background-color:#ffffff;color:#4d4d4c;" class="language-c "><code class="language-c" data-lang="c"><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">enum</span><span> shirt_size my_size </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">666</span><span>; +</span><span> </span><span style="color:#c82829;">print_size</span><span style="color:#4271ae;">(my_size)</span><span>; +</span><span>} +</span></code></pre> +<p>C++ introduces enum classes which are type-safe. Legacy enums are also somewhat safer than in C (same code as above):</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>&lt;source&gt;:27:31: error: invalid conversion from &#39;int&#39; to &#39;shirt_size&#39; [-fpermissive] +</span><span> 27 | enum shirt_size my_size = 666; +</span><span> | ^~~ +</span><span> | | +</span><span> | int +</span></code></pre> +<p>Some programming languages (especially functional ones) allow programmers to define +enums which carry additional information. Such types are usually called <code>tagged unions</code> +or <code>algebraic data types</code>.</p> +<p>In C++ we can use <code>union</code> with an <code>enum</code> tag to define it:</p> +<pre data-lang="cpp" style="background-color:#ffffff;color:#4d4d4c;" class="language-cpp "><code class="language-cpp" data-lang="cpp"><span style="color:#8959a8;">#include </span><span style="color:#718c00;">&lt;iostream&gt; +</span><span> +</span><span style="color:#999999;">// Taken from: https://en.cppreference.com/w/cpp/language/union +</span><span> +</span><span style="color:#999999;">// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +</span><span style="color:#999999;">// and three variant members (c, i, d) +</span><span style="color:#8959a8;">struct </span><span>S +</span><span>{ +</span><span> </span><span style="color:#8959a8;">enum</span><span>{</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#c99e00;">INT</span><span>, DOUBLE} tag; +</span><span> </span><span style="color:#8959a8;">union +</span><span> { +</span><span> </span><span style="color:#8959a8;">char</span><span> c; +</span><span> </span><span style="color:#8959a8;">int</span><span> i; +</span><span> </span><span style="color:#8959a8;">double</span><span> d; +</span><span> }; +</span><span>}; +</span><span> +</span><span style="color:#8959a8;">void </span><span style="color:#4271ae;">print_s</span><span>(</span><span style="color:#8959a8;">const</span><span> S</span><span style="color:#3e999f;">&amp; </span><span style="color:#f5871f;">s</span><span>) +</span><span>{ +</span><span> </span><span style="color:#8959a8;">switch</span><span>(s.</span><span style="color:#c82829;">tag</span><span>) +</span><span> { +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">CHAR</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">c </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::</span><span style="color:#c99e00;">INT</span><span>: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> </span><span style="color:#8959a8;">case</span><span> S::DOUBLE: std::cout </span><span style="color:#3e999f;">&lt;&lt;</span><span> s.</span><span style="color:#c82829;">d </span><span style="color:#3e999f;">&lt;&lt; </span><span style="color:#718c00;">&#39;</span><span style="color:#f5871f;">\n</span><span style="color:#718c00;">&#39;</span><span>; </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">int </span><span style="color:#4271ae;">main</span><span>() +</span><span>{ +</span><span> S s </span><span style="color:#3e999f;">= </span><span>{S::</span><span style="color:#c99e00;">CHAR</span><span>, </span><span style="color:#718c00;">&#39;a&#39;</span><span>}; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span> s.</span><span style="color:#c82829;">tag </span><span style="color:#3e999f;">=</span><span> S::</span><span style="color:#c99e00;">INT</span><span>; +</span><span> s.</span><span style="color:#c82829;">i </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">123</span><span>; +</span><span> </span><span style="color:#c82829;">print_s</span><span style="color:#4271ae;">(s)</span><span>; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/tagged_union.cpp">tagged_union.cpp</a>)</sub></p> +<p>C++17 introduced a new feature called <code>variant</code> which generalizes this concept. +You can read more about it <a href="https://en.cppreference.com/w/cpp/utility/variant">here</a>.</p> +<p>Java has a more or less analogous feature called <code>sealed classes</code> +since <a href="https://docs.oracle.com/en/java/javase/17/language/sealed-classes-and-interfaces.html.">version 17</a>.</p> +<h2 id="enums-in-rust">Enums in Rust</h2> +<p>Let's see how they are defined in Rust.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>NamedSize { +</span><span> Small, +</span><span> Medium, +</span><span> Large, +</span><span> </span><span style="color:#666969;">XL</span><span>, +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span style="color:#8959a8;">enum </span><span>ShirtSize { +</span><span> Named(NamedSize), +</span><span> Numeric(</span><span style="color:#8959a8;">u32</span><span>), +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Isn&#39;t it strange that some clothes&#39; sizes are adjectives like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">,&quot;</span><span>, +</span><span> ShirtSize::Named(NamedSize::Small) +</span><span> ); +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;but sometimes they are numbers like </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">?&quot;</span><span>, +</span><span> ShirtSize::Numeric(</span><span style="color:#f5871f;">42</span><span>) +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/enums.rs">enums.rs</a>)</sub></p> +<p>In Rust, enums are a core feature of the language. +You may have heard that one of Rust's defining characteristics is +the absence of <a href="https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions">&quot;the billion dollar mistake&quot;</a>. +So what can we do to say that a value is missing if there is no <code>null</code>?</p> +<p>In Rust, we can use the <code>Option</code> type to represent the absence of a value.</p> +<p>Option is defined as:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">enum </span><span>Option&lt;T&gt; { +</span><span> </span><span style="color:#c99e00;">Some</span><span>(T), +</span><span> </span><span style="color:#c99e00;">None</span><span>, +</span><span>} +</span></code></pre> +<p>The <code>&lt;T&gt;</code> part is called the &quot;type parameter&quot; and it causes Option to be generic. +We won't go deeper into this for now.</p> +<p>The fact that variables which could be <code>null</code> in other languages have a different type in Rust is +the solution to the billion dollar mistake!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> not_null: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> not_null </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">43</span><span>; +</span><span> </span><span style="color:#999999;">// not_null = None; // this won&#39;t compile because it&#39;s a different type! +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">43</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// such construction is rare, but possible +</span><span> </span><span style="color:#8959a8;">let mut</span><span> double_nullable: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// assert_ne!(double_nullable, Some(42)); // this won&#39;t even compile because it&#39;s a different type! +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> double_nullable </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// None and Some(None) are different! +</span><span> assert_ne!(double_nullable, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now recall that division by 0 *panics* +</span><span> </span><span style="color:#999999;">// A panic is an unrecoverable error +</span><span> </span><span style="color:#999999;">// It is not an exception! +</span><span> </span><span style="color:#999999;">// And in Rust there are no exceptions, so there are no try/catch blocks +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine that we want to divide one number by another +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> dividend </span><span style="color:#3e999f;">/</span><span> divisor +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We get the divisor from the user, so it can be 0 +</span><span> </span><span style="color:#999999;">// We want to handle this situation gracefully - we don&#39;t want to crash the program! +</span><span> </span><span style="color:#999999;">// We can do this by using the Option&lt;T&gt; type +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">safe_divide</span><span>(</span><span style="color:#f5871f;">dividend</span><span>: </span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#f5871f;">divisor</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">if</span><span> divisor </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#c99e00;">None +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#c99e00;">Some</span><span>(dividend </span><span style="color:#3e999f;">/</span><span> divisor) +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Fortunately, such a function is already included in the standard library +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> </span><span style="color:#999999;">// We need to specify the type explicitly +</span><span> </span><span style="color:#999999;">// because checked_div is implemented for all integer types +</span><span> </span><span style="color:#999999;">// and Rust won&#39;t know which type we want to use +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">21</span><span>)); +</span><span> assert_eq!(number.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// Now let&#39;s imagine we search for a value in a vector +</span><span> </span><span style="color:#8959a8;">let</span><span> numbers </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#f5871f;">4</span><span>, </span><span style="color:#f5871f;">5</span><span>]; +</span><span> </span><span style="color:#8959a8;">let</span><span> three </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(three, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> </span><span style="color:#8959a8;">let</span><span> seven </span><span style="color:#3e999f;">=</span><span> numbers.</span><span style="color:#4271ae;">iter</span><span>().</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">find</span><span>(|</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">7</span><span>); +</span><span> assert_eq!(seven, </span><span style="color:#c99e00;">None</span><span>); +</span><span> </span><span style="color:#999999;">// We won&#39;t delve deeper into the details of how iterators work for now, +</span><span> </span><span style="color:#999999;">// but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust +</span><span> +</span><span> </span><span style="color:#999999;">// Usually there are two kinds of methods: +</span><span> </span><span style="color:#999999;">// ones that will panic if the argument is incorrect, +</span><span> </span><span style="color:#999999;">// numbers[8]; // this will panic! +</span><span> </span><span style="color:#999999;">// and `checked` ones that return an Option +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>), </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// We can use `unwrap` to get the value out of an Option +</span><span> </span><span style="color:#999999;">// but we must be absolutely sure that the Option is Some, otherwise we&#39;ll get a panic +</span><span> </span><span style="color:#999999;">// numbers.get(8).unwrap(); // this will panic! +</span><span> assert_eq!(numbers.</span><span style="color:#4271ae;">get</span><span>(</span><span style="color:#f5871f;">8</span><span>).</span><span style="color:#4271ae;">copied</span><span>().</span><span style="color:#4271ae;">unwrap_or</span><span>(</span><span style="color:#f5871f;">0</span><span>), </span><span style="color:#f5871f;">0</span><span>); </span><span style="color:#999999;">// or we can provide a default value +</span><span> +</span><span> </span><span style="color:#999999;">// Usually instead of unwrapping we use pattern matching, we&#39;ll get to this in a minute +</span><span> </span><span style="color:#999999;">// but first let&#39;s see what else we can do with an option +</span><span> </span><span style="color:#8959a8;">let</span><span> number: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>); +</span><span> </span><span style="color:#999999;">// We can use `map` to transform the value inside an Option +</span><span> </span><span style="color:#8959a8;">let</span><span> doubled </span><span style="color:#3e999f;">=</span><span> number.</span><span style="color:#4271ae;">map</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x </span><span style="color:#3e999f;">* </span><span style="color:#f5871f;">2</span><span>); +</span><span> assert_eq!(doubled, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">84</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use flatten to reduce one level of nesting +</span><span> </span><span style="color:#8959a8;">let</span><span> nested </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> assert_eq!(nested.</span><span style="color:#4271ae;">flatten</span><span>(), </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">42</span><span>)); +</span><span> </span><span style="color:#999999;">// We can use `and_then` to chain multiple options +</span><span> </span><span style="color:#999999;">// This operation is called `flatmap` in some languages +</span><span> </span><span style="color:#8959a8;">let</span><span> chained </span><span style="color:#3e999f;">=</span><span> number +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">0</span><span>)) +</span><span> .</span><span style="color:#4271ae;">and_then</span><span>(|</span><span style="color:#f5871f;">x</span><span>| x.</span><span style="color:#4271ae;">checked_div</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> assert_eq!(chained, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#999999;">// The last two things we&#39;ll cover here are `take` and `replace` +</span><span> </span><span style="color:#999999;">// They are important when dealing with non-Copy types +</span><span> </span><span style="color:#999999;">// `take` will return the value inside an Option and leave a None in its place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> option: </span><span style="color:#c99e00;">Option</span><span>&lt;</span><span style="color:#8959a8;">i32</span><span>&gt; </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#999999;">// Again, we need to specify the type +</span><span> </span><span style="color:#999999;">// Even though we want to say that there is no value inside the Option, +</span><span> </span><span style="color:#999999;">// this absent value must have a concrete type! +</span><span> assert_eq!(option.</span><span style="color:#4271ae;">take</span><span>(), </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(option, </span><span style="color:#c99e00;">None</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> y </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">take</span><span>(); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">None</span><span>); +</span><span> assert_eq!(y, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#999999;">// `replace` can be used to swap the value inside an Option +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">5</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">5</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">2</span><span>)); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">None</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> old </span><span style="color:#3e999f;">=</span><span> x.</span><span style="color:#4271ae;">replace</span><span>(</span><span style="color:#f5871f;">3</span><span>); +</span><span> assert_eq!(x, </span><span style="color:#c99e00;">Some</span><span>(</span><span style="color:#f5871f;">3</span><span>)); +</span><span> assert_eq!(old, </span><span style="color:#c99e00;">None</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/option.rs">option.rs</a>)</sub></p> +<h2 id="pattern-matching">Pattern matching</h2> +<p>Pattern matching is a powerful feature of Rust and many functional languages, but it's slowly making +its way into imperative languages like Java and Python as well.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// Pattern matching is basically a switch on steroids. +</span><span> </span><span style="color:#8959a8;">let</span><span> number </span><span style="color:#3e999f;">= </span><span>rand::random::&lt;</span><span style="color:#8959a8;">i32</span><span>&gt;(); +</span><span> </span><span style="color:#8959a8;">match</span><span> number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7 </span><span>{ +</span><span> </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is *almost* divisible by 7&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{number}</span><span style="color:#718c00;"> is not divisible by 7&quot;</span><span>), +</span><span> } +</span><span> +</span><span> #[</span><span style="color:#c82829;">derive</span><span>(Debug)] +</span><span> </span><span style="color:#8959a8;">enum </span><span>Color { +</span><span> Pink, +</span><span> Brown, +</span><span> Lime, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> color </span><span style="color:#3e999f;">= </span><span>Color::Lime; +</span><span> </span><span style="color:#8959a8;">match</span><span> color { +</span><span> Color::Pink </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color!&quot;</span><span>), </span><span style="color:#999999;">// _ is a wildcard +</span><span> </span><span style="color:#999999;">// Rust will statically check that we covered all cases or included a default case. +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can also use pattern matching to match on multiple values. +</span><span> </span><span style="color:#8959a8;">match </span><span>(color, number </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">7</span><span>) { +</span><span> (Color::Pink, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color and number!&quot;</span><span>), +</span><span> (Color::Pink, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite color!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#f5871f;">0</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;My favorite number!&quot;</span><span>), +</span><span> (</span><span style="color:#3e999f;">_</span><span>, </span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not my favorite color or number!&quot;</span><span>), +</span><span> } +</span><span> </span><span style="color:#999999;">// (This is not special syntax, we&#39;re just pattern matching tuples.) +</span><span> +</span><span> </span><span style="color:#999999;">// But we can also *destructure* the value +</span><span> </span><span style="color:#8959a8;">struct </span><span>Human { +</span><span> </span><span style="color:#c82829;">age</span><span>: </span><span style="color:#8959a8;">u8</span><span>, +</span><span> </span><span style="color:#c82829;">favorite_color</span><span>: Color, +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> john </span><span style="color:#3e999f;">=</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> }; +</span><span> +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#3e999f;">&amp;</span><span>john { +</span><span> Human { +</span><span> age: </span><span style="color:#f5871f;">42</span><span>, +</span><span> favorite_color: Color::Pink, +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Okay, that&#39;s John!&quot;</span><span>), +</span><span> Human { +</span><span> favorite_color: Color::Pink, +</span><span> </span><span style="color:#3e999f;">.. +</span><span> } </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Not John, but still his favorite color!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Somebody else?&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Note two things: +</span><span> </span><span style="color:#999999;">// 1. Color is *not* Eq, so we can&#39;t use == to compare it, but pattern matching is fine. +</span><span> </span><span style="color:#999999;">// 2. We *borrowed* the value, so we can use it after the match. +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;John is </span><span style="color:#666969;">{}</span><span style="color:#718c00;"> years old and still kicking!&quot;</span><span>, john.age); +</span><span> +</span><span> </span><span style="color:#999999;">// To save some time, we can use `if let` to match against only one thing +</span><span> </span><span style="color:#999999;">// We could also use `while let ... {}` in the same way +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;He&#39;s also a man of great taste&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can match ranges... +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult!&quot;</span><span>), +</span><span> </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature!&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old!&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use match and capture the value at the same time. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">12 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a kid, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">13</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">20</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">29 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is a young adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">30</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">49 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an adult, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">50</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">69 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is mature, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is old, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use guards to check for multiple conditions. +</span><span> </span><span style="color:#8959a8;">match</span><span> john.age { +</span><span> age </span><span style="color:#3e999f;">@ </span><span style="color:#f5871f;">12</span><span style="color:#3e999f;">..=</span><span style="color:#f5871f;">19 </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *odd* teenager, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> age </span><span style="color:#8959a8;">if</span><span> age </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is an *even* man, age </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, age), +</span><span> </span><span style="color:#3e999f;">_ =&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;John is normal&quot;</span><span>), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// Finally, let&#39;s look at some references now +</span><span> </span><span style="color:#8959a8;">let</span><span> reference: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= &amp;</span><span style="color:#f5871f;">4</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">match</span><span> reference { +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>val </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Value under reference is: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, val), +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// `ref` can be used to create a reference when destructuring +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// `john` is still valid, because we borrowed using `ref` +</span><span> </span><span style="color:#8959a8;">if let </span><span>Color::Pink </span><span style="color:#3e999f;">= &amp;</span><span>john.favorite_color { +</span><span> println!(</span><span style="color:#718c00;">&quot;John still has his color - </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">!&quot;</span><span>, favorite_color); +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> john </span><span style="color:#3e999f;">=</span><span> john; +</span><span> +</span><span> </span><span style="color:#999999;">// `ref mut` borrows mutably +</span><span> </span><span style="color:#8959a8;">let</span><span> Human { +</span><span> age, +</span><span> </span><span style="color:#8959a8;">ref mut</span><span> favorite_color, +</span><span> } </span><span style="color:#3e999f;">=</span><span> john; +</span><span> </span><span style="color:#999999;">// We use `*` to dereference +</span><span> </span><span style="color:#3e999f;">*</span><span>favorite_color </span><span style="color:#3e999f;">= </span><span>Color::Brown; +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Tastes do change with time and John likes </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;"> now.&quot;</span><span>, +</span><span> john.favorite_color +</span><span> ); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/pattern_matching.rs">pattern_matching.rs</a>)</sub></p> +<h2 id="result">Result</h2> +<p>We said there are no exceptions in Rust and panics mean errors which cannot be caught. +So how do we handle situations which can fail? That's where the <code>Result</code> type comes in.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(dead_code)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">use </span><span>std::fs::File; +</span><span style="color:#8959a8;">use </span><span>std::io; +</span><span style="color:#8959a8;">use </span><span>std::io::Read; +</span><span> +</span><span style="color:#999999;">// Let&#39;s try reading from a file. +</span><span style="color:#999999;">// Obviously this can fail. +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">first_try</span><span>() -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(</span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>); +</span><span> </span><span style="color:#8959a8;">match</span><span> file { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#8959a8;">mut</span><span> file) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#999999;">// We got a file! +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> </span><span style="color:#999999;">// Matching each result quickly become tedious... +</span><span> </span><span style="color:#8959a8;">match</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(</span><span style="color:#3e999f;">_</span><span>) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span style="color:#c99e00;">Err</span><span>(error), +</span><span> } +</span><span> } +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>{ +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#999999;">// This is needed in order to change the type from `io::Result&lt;File&gt;` to `io::Result&lt;()&gt;` +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// The &#39;?&#39; operator allows us to return early in case of an error +</span><span style="color:#999999;">// (it automatically converts the error type) +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">second_try</span><span>(</span><span style="color:#f5871f;">filename</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">&#39;static str</span><span>) -&gt; io::</span><span style="color:#c99e00;">Result</span><span>&lt;</span><span style="color:#c99e00;">String</span><span>&gt; { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> file </span><span style="color:#3e999f;">= </span><span>File::open(filename)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> buffer </span><span style="color:#3e999f;">= </span><span>vec![</span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#f5871f;">128</span><span>]; +</span><span> file.</span><span style="color:#4271ae;">read_exact</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> buffer)</span><span style="color:#3e999f;">?</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> gibberish </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from_utf8_lossy(</span><span style="color:#3e999f;">&amp;</span><span>buffer); +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish.</span><span style="color:#4271ae;">to_string</span><span>()) +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> filenames </span><span style="color:#3e999f;">= </span><span>[ +</span><span> </span><span style="color:#718c00;">&quot;/dev/random&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/null&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/cpu&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;/dev/fuse&quot;</span><span>, +</span><span> </span><span style="color:#718c00;">&quot;there_certainly_is_no_such_file&quot;</span><span>, +</span><span> ]; +</span><span> </span><span style="color:#8959a8;">for</span><span> filename </span><span style="color:#3e999f;">in</span><span> filenames { +</span><span> println!(</span><span style="color:#718c00;">&quot;Trying to read from &#39;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&#39;&quot;</span><span>, filename); +</span><span> </span><span style="color:#8959a8;">match </span><span style="color:#4271ae;">second_try</span><span>(filename) { +</span><span> </span><span style="color:#c99e00;">Ok</span><span>(gibberish) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, gibberish), +</span><span> </span><span style="color:#c99e00;">Err</span><span>(error) </span><span style="color:#3e999f;">=&gt; </span><span>println!(</span><span style="color:#718c00;">&quot;Error: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, error), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/result.rs">result.rs</a>)</sub></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li>The Book, chapters <a href="https://doc.rust-lang.org/stable/book/ch06-00-enums.html">6</a>, +<a href="https://doc.rust-lang.org/stable/book/ch08-00-common-collections.html">8</a> +and <a href="https://doc.rust-lang.org/stable/book/ch09-00-error-handling.html">9</a></li> +<li><a href="https://doc.rust-lang.org/std/option/">Option docs</a></li> +<li><a href="https://doc.rust-lang.org/std/result/">Result docs</a></li> +</ul> +<h2 id="assignment-2-graded">Assignment #2 (graded)</h2> +<p><a href="https://classroom.github.com/a/KGDd4ofC">Here</a> you can find the second graded assignment. Deadline for submissions is 24.03.2022.</p> + + + + + Tests + 2022-03-13T00:00:00+00:00 + 2022-03-13T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/05-tests/ + + <h1 id="unit-testing">Unit testing!</h1> +<p>Rust has a built-in testing framework.</p> +<p>You can write <code>cargo test</code> to run all tests.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#999999;">// This function is going to be used only in the tests, so we add the `#[cfg(test)]` attribute. +</span><span style="color:#999999;">// It means that it won&#39;t be compiled in the final executable. +</span><span>#[</span><span style="color:#c82829;">cfg</span><span>(test)] +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">return_42</span><span>() -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> </span><span style="color:#f5871f;">42 +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">frobnicate</span><span>(</span><span style="color:#f5871f;">x</span><span>: </span><span style="color:#8959a8;">i32</span><span>) -&gt; </span><span style="color:#8959a8;">i32 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;frobicating...!&quot;</span><span>); +</span><span> x </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">40 +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#4271ae;">frobnicate</span><span>(</span><span style="color:#f5871f;">2</span><span>); +</span><span>} +</span><span> +</span><span>#[</span><span style="color:#c82829;">cfg</span><span>(test)] +</span><span style="color:#8959a8;">mod </span><span>tests { +</span><span> </span><span style="color:#8959a8;">use super</span><span>::</span><span style="color:#3e999f;">*</span><span>; +</span><span> +</span><span> #[</span><span style="color:#c82829;">test</span><span>] +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">it_works</span><span>() { +</span><span> assert_eq!(</span><span style="color:#4271ae;">return_42</span><span>(), </span><span style="color:#f5871f;">42</span><span>); +</span><span> } +</span><span> +</span><span> #[</span><span style="color:#c82829;">test</span><span>] +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">test_frobnicate</span><span>() { +</span><span> assert_eq!(</span><span style="color:#4271ae;">frobnicate</span><span>(</span><span style="color:#f5871f;">2</span><span>), </span><span style="color:#f5871f;">42</span><span>); +</span><span> } +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/05-tests/test.rs">test.rs</a>)</sub></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/book/ch11-00-testing.html">The Book, chapter 11</a></li> +</ul> + + + + + Introduction to Rust + 2022-03-09T00:00:00+00:00 + 2022-03-09T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/ + + <p><img src="https://www.rust-lang.org/logos/rust-logo-blk.svg" alt="Logo" /></p> +<h2 id="why-use-rust">Why use Rust?</h2> +<ul> +<li>It is <strong>safe</strong> (compared to C++ for example, as we will see in a minute)</li> +<li>It is <strong>fast</strong> (because it is compiled to machine code)</li> +<li>It is ergonomic and pleasant to use (static typing, expressive type system, helpful compiler +warnings)</li> +<li>It +is <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">loved by programmers</a></li> +<li>It provides excellent tooling</li> +</ul> +<h2 id="why-learn-rust">Why learn Rust?</h2> +<p>Even if you don't end up using Rust, learning it expands your horizons</p> +<ul> +<li>it helps especially with the awareness of what you can and can't do in concurrent applications</li> +<li>it helps you understand memory management and learn its good practices</li> +</ul> +<h2 id="why-not-to-learn-rust">Why not to learn Rust?</h2> +<ul> +<li>Some people say Rust is too hard to learn because of the borrow checker</li> +<li>Once you get to know Cargo you won't ever want to use a language without a built-in package +manager ;)</li> +<li>You will start hating C++ (Piotrek, don't punch me!)</li> +</ul> +<h2 id="demo">Demo</h2> +<p>Let's compare the same code written in <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/errors_demo.c">C</a>, <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/errors_demo.cpp">C++</a> +and <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/errors_demo.rs">Rust</a>.</p> +<h2 id="installing-rust">Installing Rust</h2> +<ul> +<li><a href="https://rustup.rs/">Rustup</a></li> +<li>Setup an IDE +<ul> +<li><a href="https://www.jetbrains.com/clion/">CLion</a> (you can get +it <a href="https://www.jetbrains.com/community/education/">for free</a>) +and <a href="https://intellij-rust.github.io/">Intellij-Rust</a></li> +<li><a href="https://code.visualstudio.com/">VSCode</a> +and <a href="https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer">rust-analyzer</a></li> +<li>rust-analyzer also works +with <a href="https://rust-analyzer.github.io/manual.html#installation">other IDEs</a></li> +</ul> +</li> +</ul> +<h2 id="useful-tools">Useful tools</h2> +<p><img src="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/clippy.jpg" alt="Clippy" /></p> +<ul> +<li><code>cargo clippy</code> (for static analysis)</li> +<li>there's also <code>cargo check</code>, but it's less powerful than clippy</li> +<li><code>cargo fmt</code> (for code formatting)</li> +</ul> +<h3 id="rust-playground">Rust Playground</h3> +<ul> +<li><a href="https://play.rust-lang.org/">online Rust compiler</a></li> +</ul> +<h2 id="hello-world">Hello world</h2> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> name </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;World&quot;</span><span>; +</span><span> println!(</span><span style="color:#718c00;">&quot;Hello, </span><span style="color:#666969;">{}</span><span style="color:#718c00;">!&quot;</span><span>, name); </span><span style="color:#999999;">// using the println! macro +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/hello_world.rs">hello_world.rs</a>)</sub></p> +<h3 id="variables">Variables</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_assignments)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// inferred type +</span><span> </span><span style="color:#8959a8;">let</span><span> y: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; </span><span style="color:#999999;">// specified type +</span><span> +</span><span> { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40 </span><span style="color:#3e999f;">+ </span><span style="color:#f5871f;">2</span><span>; </span><span style="color:#999999;">// shadowing +</span><span> println!(</span><span style="color:#718c00;">&quot;x is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, x); </span><span style="color:#999999;">// prints 42 +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// x = 0; // compilation error, variables are by default immutable +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">40</span><span>; </span><span style="color:#999999;">// declare as mutable +</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; </span><span style="color:#999999;">// now we can reassign +</span><span> +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; </span><span style="color:#999999;">// x = x + 1 +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/variables.rs">variables.rs</a>)</sub></p> +<h3 id="conditionals">Conditionals</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">42</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">42 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 42&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">43 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is 43&quot;</span><span>); +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;x is not 42 or 43&quot;</span><span>); +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can also use ifs as expressions +</span><span> </span><span style="color:#8959a8;">let</span><span> a_or_b </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">if</span><span> x </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;a&quot; </span><span style="color:#999999;">// notice no semicolon at the end +</span><span> } </span><span style="color:#8959a8;">else </span><span>{ +</span><span> </span><span style="color:#718c00;">&quot;b&quot; +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/conditionals.rs">conditionals.rs</a>)</sub></p> +<h3 id="loops">Loops</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#c82829;">allow</span><span>(unused_variables)] +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">for</span><span> i </span><span style="color:#3e999f;">in </span><span style="color:#f5871f;">0</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">10 </span><span>{ +</span><span> println!(</span><span style="color:#718c00;">&quot;i is </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, i); </span><span style="color:#999999;">// i in [0, 10) +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> x </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">while</span><span> x </span><span style="color:#3e999f;">&lt; </span><span style="color:#f5871f;">50 </span><span>{ +</span><span> x </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> y </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let mut</span><span> iterations </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> iterations </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> iterations </span><span style="color:#3e999f;">% </span><span style="color:#f5871f;">2 </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">0 </span><span>{ +</span><span> </span><span style="color:#8959a8;">continue</span><span>; +</span><span> } +</span><span> y </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> y </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// we can use labels to refer to a specific loop +</span><span> </span><span style="color:#8959a8;">let mut</span><span> count </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> &#39;counting_up: </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">let mut</span><span> remaining </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">10</span><span>; +</span><span> +</span><span> </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> </span><span style="color:#8959a8;">if</span><span> remaining </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">9 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break</span><span>; +</span><span> } +</span><span> </span><span style="color:#8959a8;">if</span><span> count </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">2 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break &#39;counting_up</span><span>; </span><span style="color:#999999;">// ends the outer loop +</span><span> } +</span><span> remaining </span><span style="color:#3e999f;">-= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> count </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// We can use break with a value. +</span><span> </span><span style="color:#999999;">// Because loops are expressions too, +</span><span> </span><span style="color:#999999;">// the value we break with will be returned from the functions +</span><span> </span><span style="color:#8959a8;">let mut</span><span> counter </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#8959a8;">let</span><span> value </span><span style="color:#3e999f;">= </span><span style="color:#8959a8;">loop </span><span>{ +</span><span> counter </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> </span><span style="color:#8959a8;">if</span><span> counter </span><span style="color:#3e999f;">== </span><span style="color:#f5871f;">10 </span><span>{ +</span><span> </span><span style="color:#8959a8;">break </span><span style="color:#f5871f;">32</span><span>; +</span><span> } +</span><span> }; +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/loops.rs">loops.rs</a>)</sub></p> +<h3 id="functions">Functions</h3> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">get_5</span><span>() -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#f5871f;">5 </span><span style="color:#999999;">// we could also write &quot;return 5;&quot; +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">print_sum</span><span>(</span><span style="color:#f5871f;">a</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">b</span><span>: </span><span style="color:#8959a8;">u32</span><span>) { +</span><span> println!(</span><span style="color:#718c00;">&quot;a + b = </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, a </span><span style="color:#3e999f;">+</span><span> b); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> a </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">100</span><span>; +</span><span> </span><span style="color:#4271ae;">print_sum</span><span>(a, </span><span style="color:#4271ae;">get_5</span><span>()); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/functions.rs">functions.rs</a>)</sub></p> +<h2 id="test-assignment-not-graded">Test assignment (not graded)</h2> +<p>Click <a href="https://classroom.github.com/a/sFJOi1pT">here</a></p> +<h2 id="obligatory-reading">Obligatory reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/book/">The Book, chapters 1-3</a></li> +</ul> +<h2 id="additional-reading">Additional reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/stable/rust-by-example/">Rust By Example</a></li> +</ul> + + + + + Data Types + 2022-03-09T00:00:00+00:00 + 2022-03-09T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/03-data-types/ + + <h2 id="aggregating-data">Aggregating data</h2> +<p>Below is a compact overview of Rust's structs</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Copy, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Position(</span><span style="color:#8959a8;">i32</span><span>, </span><span style="color:#8959a8;">i32</span><span>); </span><span style="color:#999999;">// tuple struct +</span><span> +</span><span style="color:#999999;">// Could Hero derive the Copy trait? +</span><span>#[</span><span style="color:#c82829;">derive</span><span>(Clone, Debug, Eq, PartialEq)] +</span><span style="color:#8959a8;">struct </span><span>Hero { +</span><span> </span><span style="color:#c82829;">name</span><span>: String, +</span><span> </span><span style="color:#c82829;">level</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">experience</span><span>: </span><span style="color:#8959a8;">u32</span><span>, +</span><span> </span><span style="color:#c82829;">position</span><span>: Position, +</span><span>} +</span><span> +</span><span style="color:#999999;">// we can add methods to structs using the &#39;impl&#39; keyword +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// static method +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">new</span><span>(</span><span style="color:#f5871f;">name</span><span>: String) -&gt; Hero { +</span><span> Hero { +</span><span> name, +</span><span> level: </span><span style="color:#f5871f;">1</span><span>, +</span><span> experience: </span><span style="color:#f5871f;">0</span><span>, +</span><span> position: Position(</span><span style="color:#f5871f;">0</span><span>, </span><span style="color:#f5871f;">0</span><span>), +</span><span> } +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#999999;">// multiple impl blocks are possible for one struct +</span><span style="color:#8959a8;">impl </span><span>Hero { +</span><span> </span><span style="color:#999999;">// instance method, first argument (self) is the calling instance +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">distance</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#f5871f;">self</span><span>, </span><span style="color:#f5871f;">pos</span><span>: Position) -&gt; </span><span style="color:#8959a8;">u32 </span><span>{ +</span><span> </span><span style="color:#999999;">// fields of tuples and tuple structs can be accessed through &#39;tuple.[i]&#39; +</span><span> (pos.</span><span style="color:#f5871f;">0 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">0</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() </span><span style="color:#3e999f;">+ </span><span>(pos.</span><span style="color:#f5871f;">1 </span><span style="color:#3e999f;">- </span><span style="color:#c82829;">self</span><span>.position.</span><span style="color:#f5871f;">1</span><span>).</span><span style="color:#4271ae;">unsigned_abs</span><span>() +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// mutable borrow of self allows to change instance fields +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">level_up</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut </span><span style="color:#f5871f;">self</span><span>) { +</span><span> </span><span style="color:#c82829;">self</span><span>.experience </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">0</span><span>; +</span><span> </span><span style="color:#c82829;">self</span><span>.level </span><span style="color:#3e999f;">+= </span><span style="color:#f5871f;">1</span><span>; +</span><span> } +</span><span> +</span><span> </span><span style="color:#999999;">// &#39;self&#39; is not borrowed here and will be moved into the method +</span><span> </span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">die</span><span>(</span><span style="color:#f5871f;">self</span><span>) { +</span><span> println!( +</span><span> </span><span style="color:#718c00;">&quot;Here lies </span><span style="color:#666969;">{}</span><span style="color:#718c00;">, a hero who reached level </span><span style="color:#666969;">{}</span><span style="color:#718c00;">. RIP.&quot;</span><span>, +</span><span> </span><span style="color:#c82829;">self</span><span>.name, </span><span style="color:#c82829;">self</span><span>.level +</span><span> ); +</span><span> } +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> hero: Hero </span><span style="color:#3e999f;">= </span><span>Hero::new(</span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Marty The Brave&quot;</span><span>)); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); </span><span style="color:#999999;">// &#39;self&#39; is always passed implicitly +</span><span> +</span><span> </span><span style="color:#999999;">// fields other than &#39;name&#39; will be the same as in &#39;hero&#39; +</span><span> </span><span style="color:#8959a8;">let</span><span> steve </span><span style="color:#3e999f;">=</span><span> Hero { +</span><span> name: </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Steve The Normal Guy&quot;</span><span>), +</span><span> </span><span style="color:#3e999f;">..</span><span>hero +</span><span> }; +</span><span> +</span><span> assert_eq!(hero.level, steve.level); +</span><span> +</span><span> </span><span style="color:#8959a8;">let mut</span><span> twin </span><span style="color:#3e999f;">=</span><span> hero.</span><span style="color:#4271ae;">clone</span><span>(); +</span><span> +</span><span> </span><span style="color:#999999;">// we can compare Hero objects because it derives the PartialEq trait +</span><span> assert_eq!(hero, twin); +</span><span> twin.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_ne!(hero, twin); +</span><span> hero.</span><span style="color:#4271ae;">level_up</span><span>(); +</span><span> assert_eq!(hero, twin); +</span><span> +</span><span> </span><span style="color:#999999;">// we can print out a the struct&#39;s debug string with &#39;{:?}&#39; +</span><span> println!(</span><span style="color:#718c00;">&quot;print to stdout: </span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, hero); +</span><span> +</span><span> hero.</span><span style="color:#4271ae;">die</span><span>(); </span><span style="color:#999999;">// &#39;hero&#39; is not usable after this invocation, see the method&#39;s definiton +</span><span> +</span><span> </span><span style="color:#999999;">// the dbg! macro prints debug strings to stderr along with file and line number +</span><span> dbg!(</span><span style="color:#718c00;">&quot;print to stderr: {}&quot;</span><span>, twin); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> pos </span><span style="color:#3e999f;">=</span><span> Position(</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">0</span><span>); +</span><span> </span><span style="color:#8959a8;">let</span><span> dist </span><span style="color:#3e999f;">=</span><span> steve.</span><span style="color:#4271ae;">distance</span><span>(pos); </span><span style="color:#999999;">// no clone here as Position derives the Copy trait +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{:?}</span><span style="color:#718c00;">&quot;</span><span>, pos); +</span><span> assert_eq!(dist, </span><span style="color:#f5871f;">42</span><span>); +</span><span>} +</span><span> +</span></code></pre> +<p><sub>(Download the source code for this example: <a href="https://mimuw-jnp2-rust.github.io/lessons/old/2021L/03-data-types/data_types.rs">data_types.rs</a>)</sub></p> +<h2 id="further-reading">Further reading</h2> +<ul> +<li><a href="https://doc.rust-lang.org/book/ch05-00-structs.html">The Book, chapter 5</a></li> +</ul> +<h2 id="assignment-1-graded">Assignment #1 (graded)</h2> +<p><a href="https://classroom.github.com/a/nc92p2Ow">Here</a> you can find our first biweekly assignment. The deadline for submissions is 14.03.2022.</p> + + + + + Ownership Model + 2022-03-06T00:00:00+00:00 + 2022-03-06T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/02-ownership/ + + <h2 id="why-all-the-fuss">Why all the fuss?</h2> +<p>Even if you've never seen Rust code before, chances are you still heard the term <em>borrow checker</em> or something about Rust's ownership. Indeed, Rust's ownership model lies at the very core of its uniqueness. But to fully understand it and appreciate it, let's first take a look at how memory management is handled in most popular languages.</p> +<ul> +<li> +<p><strong>Garbage Collection</strong> - in many high-level programming languages, like Java, Haskell or Python, memory management is done fully by the language, relieving the programmer from this burden. This prevents memory leaks and memory related errors (like <em>use after free</em>), but does come at a cost - there is a runtime overhead, both memory and performance wise, caused by the constantly running garbage collection algorithms and the programmer usually has very little control over when the garbage collection takes place.</p> +</li> +<li> +<p><strong>Mind your own memory</strong> - in low-level languages and specific ones like C++, performance comes first so we cannot really afford to run expansive bookkeeping and cleaning algorithms. Most of these languages compile directly to machine code and have no language-specific runtime environment. That means that the only place where memory management can happen is in the produced code. While compilers insert these allocation and deallocation calls for stack allocated memory, it generally requires a lot of discipline from the programmer to adhere to good practices and patterns to avoid as many memory related issues as possible and one such bug can be quite deadly to the program and a nightmare to find and fix. These languages basically live by the <em>&quot;your memory, your problem&quot;</em> mantra.management of stack allocated memory</p> +</li> +</ul> +<p>And then we have Rust. Rust is a systems programming language and in many ways it's akin to C++ - it's basically low-level with many high-level additions. But unlike C++, it doesn't exactly fall into either of the categories described above, though it's way closer to the second one. It performs no additional management at runtime, but instead imposes a set of rules on the code, making it easier to reason about and thus check for its safety and correctness at compile time - these rules make up Rust's <strong>ownership model</strong>.</p> +<p>In a way, programming in Rust is like pair-programming with a patient and very experienced partner. Rust's compiler will make sure you follow all the good patterns and practices (by having them ingrained in the language itself) and very often even tell you how to fix the issues it finds.</p> +<p><em><strong>Disclaimer:</strong> when delving deeper into Rust below we will make heavy use of concepts like scopes, moving data, stack and heap, which should have been introduced as part of the JNP1 C++ course. If you need a refresher of any of these, it's best to do so now, before reading further.</em></p> +<h2 id="start-with-the-basics-ownership">Start with the basics - ownership</h2> +<p>In the paragraph above we mentioned a set of rules that comprise Rust's ownership model. <a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules">The book</a> starts off with the following three as its very foundation:</p> +<ol> +<li> +<p>Each value in Rust is tied to a specific variable - we call that variable its <strong>owner</strong>.</p> +</li> +<li> +<p>There can only be one owner at a time.</p> +</li> +<li> +<p>When the owner goes out of scope, the value will be destroyed (or in Rust terms - <em>dropped</em>).</p> +</li> +</ol> +<p>The third point might make you think about C++ and its automatic storage duration. We will later see that, while very similar at first, Rust expands on these mechanics quite a bit. The following code illustrates the basic version of this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> a: </span><span style="color:#8959a8;">i32 </span><span style="color:#3e999f;">= </span><span style="color:#f5871f;">5</span><span>; </span><span style="color:#999999;">// allocation on the stack, &#39;a&#39; becomes an owner +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;a&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;a&#39;, the owner, goes out of scope and the value is dropped +</span></code></pre> +<p>So far, so good. Variables are pushed onto the stack when they enter the scope and destroyed during stack unwinding that happens upon leaving their scope. However, allocating and deallocating simple integers doesn't impress anybody. Let's try something more complex:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span>{ +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is allocated on the stack, while its contents (&quot;a string&quot;) +</span><span> </span><span style="color:#999999;">// are allocated on the heap. &#39;s&#39; is the owner of this String object. +</span><span> +</span><span> </span><span style="color:#999999;">// do some stuff with &#39;a&#39; +</span><span> +</span><span>} </span><span style="color:#999999;">// &#39;s&#39;, the owner, goes out of scope and the String is dropped, its heap allocated memory freed +</span></code></pre> +<p>If you recall the RAII (Resource Acquisition Is Initialization) pattern from C++, the above is basically the same thing. We go two for two now in the similarity department, so... is Rust really any different then? There is a part of these examples that we skipped over - actually doing something with the values.</p> +<h2 id="moving-around-is-fun">Moving around is fun</h2> +<p>Let's expand on the last example. The scoping is not really important for that one, so we don't include it here.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// same thing, &#39;s&#39; is now an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s; </span><span style="color:#999999;">// easy, &#39;s2&#39; becomes another owner... right? +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// this doesn&#39;t work, can you guess why? +</span></code></pre> +<p>At first glance everything looks great. If we write this code (well, an equivalent of it) in basically any other popular language, it will compile no issue - but it does not here and there's a good reason why.</p> +<p>To understand what's happening, we have to consult the rules again, rule 2 in particular. It says that there can only be one owner of any value at a given time. So, <code>s</code> and <code>s2</code> cannot own the same object. Okay, makes sense, but what is happening in this line then - <code>let s2 = s;</code>? Experience probably tells you that <code>s</code> just gets copied into <code>s2</code>, creating a new String object. That would result in each variable owning its very own instance of the string and each instance having exactly one owner. Sounds like everyone should be happy now, but wait - in that case the last line should work no issue, right? But it doesn't, so can't be a copy. Let's see now what the compiler actually has to say:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0382]: borrow of moved value: `s` +</span><span> --&gt; src/main.rs:6:42 +</span><span> | +</span><span>2 | let s = String::from(&quot;a string&quot;); +</span><span> | - move occurs because `s` has type `String`, which does not implement the `Copy` trait +</span><span>3 | +</span><span>4 | let s2 = s; +</span><span> | - value moved here +</span><span>5 | +</span><span>6 | println!(&quot;And the contents are: {}&quot;, s); +</span><span> | ^ value borrowed here after move +</span></code></pre> +<p><em>&quot;value moved here&quot;</em> - gotcha! So <code>s</code> is being moved to <code>s2</code>, which also means that <code>s2</code> now becomes the new owner of the string being moved and <code>s</code> is left in an invalid state. In Rust, the default method of passing values around is by move, not by copy. While it may sound a bit odd at first, it actually has some very interesting implications. But before we get to them, let's fix our code so it compiles now. To do so, we have to explicitly tell Rust to make a copy by invoking the <code>clone</code> method:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;a string&quot;</span><span>); </span><span style="color:#999999;">// &#39;s&#39; is an owner +</span><span> +</span><span style="color:#8959a8;">let</span><span> s2 </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">clone</span><span>(); </span><span style="color:#999999;">// &#39;s2&#39; now contains its own copy +</span><span> +</span><span>println!(</span><span style="color:#718c00;">&quot;And the contents are: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, s); </span><span style="color:#999999;">// success! +</span></code></pre> +<p>The compiler is happy now and so are we. The implicit move takes some getting used to, but the compiler is here to help us. Now, let's put the good, old C++ on the table again and compare the two lines:</p> +<div style="text-align: center"> +<p><code>let s2 = s;</code> is equivalent to <code>auto s2 = std::move(s);</code></p> +<p><code>let s2 = s.clone()</code> is equivalent to <code>auto s2 = s</code></p> +</div> +<p>There are a few important things to note here:</p> +<ul> +<li> +<p>Making a copy is oftentimes not cheap. Memory needs to be allocated and copied, and a call to the system has to be made. We should prefer to move things as much as possible to avoid this cost - in C++ we have a myriad of language features like <code>std::move</code> and <em>r-references</em> to achieve this. Every programmer worth their salt needs to be well versed in all of them to write efficient C++ code and simply forgetting one move can lead to significant performance loss (and this happens to even the most senior devs ever existing, let's not pretend). On the contrary, in Rust you need to make an effort to make a copy and that makes you very aware of the cost you're paying - something that we'll see quite a lot of in the language. Also, if you forget a clone there's no harm done - it just won't compile!</p> +</li> +<li> +<p>Hidden in all of this is another nice thing Rust gives us. In C++, nothing prevents you from using variables after they've been moved from, leading to unexpected errors in a more complex code. In Rust, that variable (in our case <code>s</code>) simply becomes invalid and the compiler gives us a nice error about it.</p> +</li> +</ul> +<h3 id="but-what-about-ints">But what about ints?</h3> +<p>A good question to ask. Copying primitives is cheap. And it's not convenient for the programmer to have to always write <code>.clone()</code> after every primitive. If we take a look at the error from the previous example:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>move occurs because `s` has type `String`, which does not implement the `Copy` trait` +</span></code></pre> +<p>It says that <code>s</code> was moved because the <code>String</code> type doesn't have the <code>Copy</code> trait. We will talk about traits more in depth in the future lessons, but what this basically means is that <code>String</code> is not specified to be copied by default. All primitive types (<code>i32</code>, <code>bool</code>, <code>f64</code>, <code>char</code>, etc.) and tuples consisting only of primitive types implement the <code>Copy</code> trait.</p> +<h3 id="exercise">Exercise</h3> +<p>How to fix that code?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); +</span><span>} +</span></code></pre> +<h2 id="let-s-borrow-some-books">Let's borrow some books</h2> +<p>We now know how to move things around and how to clone them if moving is not possible. But what if making a copy is unnecessary - maybe we just want to let someone look at our resource and keep on holding onto it once their done. Consider the following example:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(book.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Cloning is pretty excessive here. Imagine recommending a book to your friend and instead of lending it to them for the weekend, you scan it and print an exact copy. Not the best way to go about it, is it? Thankfully, Rust allows us to access a resource without becoming an owner through the use of references and the <code>&amp;</code> operator. This is called a borrow.</p> +<p>The adjusted code should look like this:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span>book); +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;Book is still there: </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>As with everything, references are too, by default, immutable, which means that the <code>read_book</code> function is not able to modify that book passed into it. We can also borrow something mutably by specifing it both in the receiving function signature and the place it gets called. Maybe you want to have your book signed by its author?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">push_str</span><span>(</span><span style="color:#718c00;">&quot; ~ Arthur Author&quot;</span><span>); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#999999;">// note that the book has to be marked as mutable in the first place +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">sign_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// it&#39;s always clear when a parameter might get modified +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); </span><span style="color:#999999;">// book is now signed +</span><span>} +</span></code></pre> +<p>Pretty neat, but doesn't seem that safe right now. Let's try to surprise our friend:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> String) { +</span><span> book.</span><span style="color:#4271ae;">clear</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">read_book</span><span>(</span><span style="color:#f5871f;">book</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;[Reading] </span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let mut</span><span> book </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;Merry lived in a big old house. The end.&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#8959a8;">let</span><span> r </span><span style="color:#3e999f;">= &amp;</span><span>book; </span><span style="color:#999999;">// an immutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">erase_book</span><span>(</span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">mut</span><span> book); </span><span style="color:#999999;">// a mutable borrow +</span><span> +</span><span> </span><span style="color:#4271ae;">read_book</span><span>(r); </span><span style="color:#999999;">// would be pretty sad to open a blank book when it was not +</span><span> </span><span style="color:#999999;">// what we borrowed initially +</span><span> +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{}</span><span style="color:#718c00;">&quot;</span><span>, book); +</span><span>} +</span></code></pre> +<p>Fortunately for us (and our poor friend just wanting to read), the compiler steps in and doesn't let us do that, printing the following message:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0502]: cannot borrow `book` as mutable because it is also borrowed as immutable +</span><span> --&gt; src/main.rs:14:14 +</span><span> | +</span><span>12 | let r = &amp;book; // an immutable borrow +</span><span> | ----- immutable borrow occurs here +</span><span>13 | +</span><span>14 | erase_book(&amp;mut book); // a mutable borrow +</span><span> | ^^^^^^^^^ mutable borrow occurs here +</span><span>15 | +</span><span>16 | read_book(r); // would be pretty sad to open a blank book when it was not +</span><span> | - immutable borrow later used here +</span></code></pre> +<p>This is where the famous borrow checker comes in. To keep things super safe, Rust clearly states what can and cannot be done with references and tracks their lifetimes. Exactly one of the following is always true for references to a given resource:</p> +<ul> +<li> +<p>There exists only one mutable reference and no immutable references, <strong>or</strong></p> +</li> +<li> +<p>There is any number of immutable references and no mutable ones.</p> +</li> +</ul> +<p>You may notice a parallel to the <em>readers - writers</em> problem from concurrent programming. In fact, the way Rust's borrow checker is designed lends itself incredibly well to preventing data race related issues.</p> +<h3 id="dangling-references">Dangling references</h3> +<p>Rust also checks for dangling references. If we try to compile the following code:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> reference_to_nothing </span><span style="color:#3e999f;">= </span><span style="color:#4271ae;">dangle</span><span>(); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">dangle</span><span>() -&gt; </span><span style="color:#3e999f;">&amp;</span><span>String { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;hello&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#3e999f;">&amp;</span><span>s +</span><span>} +</span></code></pre> +<p>we will get an adequate error:</p> +<pre style="background-color:#ffffff;color:#4d4d4c;"><code><span>error[E0106]: missing lifetime specifier +</span><span> --&gt; src/main.rs:5:16 +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;String { +</span><span> | ^ expected named lifetime parameter +</span><span> | +</span><span> = help: this function&#39;s return type contains a borrowed value, but there is no value for it to be borrowed from +</span><span>help: consider using the `&#39;static` lifetime +</span><span> | +</span><span>5 | fn dangle() -&gt; &amp;&#39;static String { +</span><span> | ^^^^^^^^ +</span></code></pre> +<p>The message above suggests specifing a lifetime for the returned string. In Rust, the lifetime of each variable is also a part of its type, but we will talk more about it later.</p> +<h3 id="exercise-1">Exercise</h3> +<p>Our previous solution using <code>clone()</code> was pretty inefficient. How should this code look now?</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, s.</span><span style="color:#4271ae;">clone</span><span>()); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, s); </span><span style="color:#999999;">// we could&#39;ve ommitted the clone() here. Why? +</span><span>} +</span></code></pre> +<h2 id="everyone-gets-a-slice">Everyone gets a slice</h2> +<p>The last part of working with references that we will cover in this lesson are slices. A <em>slice</em> in Rust is a view over continuous data. Let us start with a string slice - the <code>&amp;str</code> type.</p> +<p><em><strong>Note:</strong> for the purposes of these examples we assume we are working with ASCII strings. More comprehensive articles on handling strings are linked at the end of this lesson.</em></p> +<p>To create a string slice from the <code>String</code> object <code>s</code>, we can simply write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// creates a slice of length 2, starting with the character at index 1 +</span></code></pre> +<p>This makes use of the <code>&amp;</code> operator and Rust's range notation to specify the beginning and end of the slice. Thus, we can also write:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#f5871f;">2</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// everything from index 2 till the end +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">1</span><span>]; </span><span style="color:#999999;">// only the first byte +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">= &amp;</span><span>s[</span><span style="color:#3e999f;">..</span><span>]; </span><span style="color:#999999;">// the whole string as a slice +</span><span style="color:#8959a8;">let</span><span> slice </span><span style="color:#3e999f;">=</span><span> s.</span><span style="color:#4271ae;">as_str</span><span>(); </span><span style="color:#999999;">// also the whole string +</span></code></pre> +<p>You might have noticed that we always built <code>String</code> values using the <code>from()</code> method and never actually used the string literals directly. What type is a string literal then? Turns out it's the new string slice we just learned about!</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span style="color:#8959a8;">str </span><span style="color:#3e999f;">= </span><span style="color:#718c00;">&quot;string literal&quot;</span><span>; +</span></code></pre> +<p>In fact, it makes a lot sense - string literals, after all, are not allocated on the heap, but rather placed in a special section of the resulting binary. It's only natural we just reference that place with a slice.</p> +<p>Slices can also be taken from arrays:</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">let</span><span> array: [</span><span style="color:#8959a8;">i32</span><span>; </span><span style="color:#f5871f;">4</span><span>] </span><span style="color:#3e999f;">= </span><span>[</span><span style="color:#f5871f;">42</span><span>, </span><span style="color:#f5871f;">10</span><span>, </span><span style="color:#f5871f;">5</span><span>, </span><span style="color:#f5871f;">2</span><span>]; </span><span style="color:#999999;">// creates an array of four 32 bit integers +</span><span style="color:#8959a8;">let</span><span> slice: </span><span style="color:#3e999f;">&amp;</span><span>[</span><span style="color:#8959a8;">i32</span><span>] </span><span style="color:#3e999f;">= &amp;</span><span>array[</span><span style="color:#f5871f;">1</span><span style="color:#3e999f;">..</span><span style="color:#f5871f;">3</span><span>]; </span><span style="color:#999999;">// results in a slice [10, 5] +</span></code></pre> +<h3 id="exercise-2">Exercise</h3> +<p>Can this code still be improved from the previous version utilizing references? Think about the signature of <code>count_animals</code>.</p> +<pre data-lang="rust" style="background-color:#ffffff;color:#4d4d4c;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">num</span><span>: </span><span style="color:#8959a8;">u32</span><span>, </span><span style="color:#f5871f;">animal</span><span>: </span><span style="color:#3e999f;">&amp;</span><span>String) { +</span><span> println!(</span><span style="color:#718c00;">&quot;</span><span style="color:#666969;">{} {}</span><span style="color:#718c00;"> ...&quot;</span><span>, num, animal); +</span><span>} +</span><span> +</span><span style="color:#8959a8;">fn </span><span style="color:#4271ae;">main</span><span>() { +</span><span> </span><span style="color:#8959a8;">let</span><span> s </span><span style="color:#3e999f;">= </span><span style="color:#c99e00;">String</span><span>::from(</span><span style="color:#718c00;">&quot;sheep&quot;</span><span>); +</span><span> +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">1</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">2</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span> </span><span style="color:#4271ae;">count_animals</span><span>(</span><span style="color:#f5871f;">3</span><span>, </span><span style="color:#3e999f;">&amp;</span><span>s); +</span><span>} +</span></code></pre> +<h3 id="further-reading">Further reading</h3> +<ul> +<li> +<p><a href="https://doc.rust-lang.org/std/primitive.char.html">Char documentation</a></p> +</li> +<li> +<p><a href="https://fasterthanli.me/articles/working-with-strings-in-rust">Working with strings in Rust</a></p> +</li> +<li> +<p><a href="https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html">The Book, chapter 4</a></p> +</li> +</ul> + + + + + Oragnizational lesson + 2022-02-16T00:00:00+00:00 + 2022-02-16T00:00:00+00:00 + + Unknown + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/00-organizational/ + + <h1 id="jnp-2-rust">JNP 2: Rust</h1> +<p>We will be using <a href="https://classroom.github.com/">Github Classroom</a> for task submission. +Once you are enrolled in the Moodle course we will import the data into Classroom.</p> +<p>Our main learning/teaching resource will be <a href="https://doc.rust-lang.org/stable/book/">&quot;The Book&quot;</a>.</p> +<h2 id="schedule">Schedule</h2> +<ol> +<li>2022-03-01: Introduction. Setting up the environment. Why Rust? &quot;Hello world&quot;.</li> +<li>2022-03-08: Ownership and borrow checking. Data types and structures.</li> +<li>2022-03-15: Enums. Pattern matching. Option and Result. Cargo test.</li> +<li>2022-03-22: Reasoning about types - generics, traits and lifetimes.</li> +<li>2022-03-29: Closures and iterators.</li> +</ol> +<h2 id="grading">Grading</h2> +<ul> +<li>1/3 of the grade is based on small tasks. There will be approximately 1 task every two weeks and each task will be graded on a scale of 0 to 3.</li> +<li>2/3 of the grade is based on a big project. You can choose a topic yourself, but it must be accepted by us. The project has to be split into two parts. It can be done in groups of two.</li> +<li>The grade may be increased by a bonus. You can get a bonus for: +<ul> +<li>Making a presentation about some advanced topic (const generics, futures, macros, etc.) or about architecture of a selected Rust open-source library</li> +<li>Contributing to a selected Rust open-source library</li> +<li>Contributing to this course's materials</li> +</ul> +</li> +</ul> +<h2 id="project-deadlines">Project Deadlines</h2> +<ol> +<li>2022-04-12: Project ideas should be presented to us for further refining. If you wish to pair up with someone, now is the time to tell us.</li> +<li>2022-04-26: Final project ideas should be accepted by now.</li> +<li>2022-05-24: Deadline for submitting the first part of the project.</li> +<li>2022-06-21: Deadline for submitting the second and final part of the project.</li> +</ol> + + + + diff --git a/book.css b/book.css new file mode 100644 index 0000000..d6a657c --- /dev/null +++ b/book.css @@ -0,0 +1 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;text-size-adjust:100%;}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible;}pre{font-family:monospace;font-size:1em;}a{background-color:rgba(0,0,0,0)}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted;}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em;}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0;}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal;}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0;}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px;}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-file-upload-button{appearance:button;font:inherit;}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}*{box-sizing:border-box}html{font-size:62.5%}body,html{height:100%}body{text-rendering:optimizelegibility;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:.2px}.menu{height:100%;position:absolute;left:0;overflow-y:auto;width:300px;color:#364149;background:#fafafa;border-right:1px solid rgba(0,0,0,.07);transition:.5s}.menu ul{list-style:none;margin:0;padding:0}.menu ul a{display:block;color:#364149;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;text-decoration:none;padding:10px 15px}.menu ul a:hover{text-decoration:underline}.menu ul li.active>a{color:#008cff;text-decoration:none}.menu ul ul{padding-left:20px}.menu-hidden{width:0}@media screen and (max-width: 600px){.menu{width:calc(100% - 100px);z-index:1}.menu-hidden{width:0;z-index:0}}.page-without-menu{width:100%;left:0}.page-without-menu .previous{left:15px}.page{position:absolute;width:calc(100% - 300px);height:100%;overflow-y:auto;color:#000;background:#fff;padding-bottom:20px;transition:.5s}.page .zola-anchor{color:#4183c4;padding-left:10px;text-decoration:none;font-weight:initial}.page .zola-anchor:hover{text-decoration:underline}.page img{max-width:100%}.page__content{font-size:1.6rem;word-wrap:break-word;line-height:1.7;position:relative;left:0;max-width:800px;margin:0 auto;padding:0 15px 40px}.page__content a{color:#4183c4;text-decoration:none}.page__content a:hover{text-decoration:underline}.page__content hr{height:4px;padding:0;margin:1.7em 0;overflow:hidden;background-color:#e7e7e7;border:none}.page__content pre{padding:1rem}.page__content pre span{white-space:pre-wrap}.page__content blockquote{margin:0;margin-bottom:.85em;padding:0 15px;color:#858585;border-left:4px solid #e5e5e5}.page__content code{display:inline-block;vertical-align:middle;padding:.1em .3em;border-radius:3px;background:#f1f1f1;font-size:.875em;font-family:"Source Code Pro",Consolas,"Ubuntu Mono",Menlo,"DejaVu Sans Mono",monospace}.page__content pre code{background:none}.page__content iframe{border:0}.page__content table{margin:0 auto;border-collapse:collapse;border-color:#ccc}.page__content table td{padding:3px 20px;border:1px solid #ccc}.page__content table thead th{padding:6px 13px;font-weight:bold;border:1px solid #ccc}.page__content p{margin-top:0;margin-bottom:.85em}.page .previous{left:300px;float:left;transition:left .5s}.page .next{right:15px;float:right}.page .previous,.page .next{position:fixed;display:flex;top:50px;bottom:0;font-size:2.5em;color:#ccc;text-decoration:none;text-align:center;margin:0;max-width:150px;min-width:90px;justify-content:center;align-content:center;flex-direction:column}.page .previous:hover,.page .next:hover{color:#333}@media screen and (max-width: 1250px){.page .previous,.page .next{position:static;top:auto;display:inline-block;max-width:49%;width:49%}.page .previous:hover,.page .next:hover{text-decoration:none}}@media screen and (min-width: 600px){.page{left:300px}}@media screen and (max-width: 600px){.page{width:100%;left:calc(100% - 100px)}.page-without-menu{left:0}}.search-container{display:none}.search-container--is-visible{display:block}.search-container #search{width:100%;padding:1rem;border:1px solid #aaa;border-radius:3px;background-color:#fafafa;color:#000}.search-container .search-results__header{font-weight:bold;padding:1rem 0}.search-container .search-results__items{margin:0;padding:0;list-style:none}.search-container .search-results__item{margin-bottom:1rem}.search-container .search-results__teaser{font-size:90%}.search-mode .prev-link,.search-mode .next-link{display:none}.page-header{height:50px}.page-header .menu-icon{height:50px;width:50px;font-size:24px;text-align:center;float:left;position:relative;transition:background .5s;cursor:pointer}@keyframes clickfirst{0%{transform:translateY(6px) rotate(0deg)}100%{transform:translateY(0) rotate(45deg)}}@keyframes clickmid{0%{opacity:1}100%{opacity:0}}@keyframes clicklast{0%{transform:translateY(-6px) rotate(0deg)}100%{transform:translateY(0) rotate(-45deg)}}@keyframes outfirst{0%{transform:translateY(0) rotate(-45deg)}100%{transform:translateY(-6px) rotate(0deg)}}@keyframes outmid{0%{opacity:0}100%{opacity:1}}@keyframes outlast{0%{transform:translateY(0) rotate(45deg)}100%{transform:translateY(6px) rotate(0deg)}}.page-header .menu-icon span{position:absolute;left:calc((100% - 20px)/2);top:calc((100% - 1px)/2);width:20px;height:2px;background-color:rgba(0,0,0,.5)}.page-header .menu-icon span:nth-child(1){transform:translateY(6px) rotate(0deg)}.page-header .menu-icon span:nth-child(3){transform:translateY(-6px) rotate(0deg)}.page-header .menu-icon.icon-click span:nth-child(1){animation-duration:.5s;animation-fill-mode:both;animation-name:clickfirst}.page-header .menu-icon.icon-click span:nth-child(2){animation-duration:.2s;animation-fill-mode:both;animation-name:clickmid}.page-header .menu-icon.icon-click span:nth-child(3){animation-duration:.5s;animation-fill-mode:both;animation-name:clicklast}.page-header .menu-icon.icon-out span:nth-child(1){animation-duration:.5s;animation-fill-mode:both;animation-name:outfirst}.page-header .menu-icon.icon-out span:nth-child(2){animation-duration:.2s;animation-fill-mode:both;animation-name:outmid}.page-header .menu-icon.icon-out span:nth-child(3){animation-duration:.5s;animation-fill-mode:both;animation-name:outlast}.page-header .menu-icon:hover span{background-color:#000}.page-header .search-icon{height:50px;width:50px;display:inline-block;text-align:center;line-height:50px;color:rgba(0,0,0,.5);cursor:pointer;font-size:2rem}.page-header .search-icon:hover{color:#000}pre{padding:1rem;overflow:auto}pre[data-linenos]{padding:1rem 0}pre table td{padding:0}pre table td:nth-of-type(1){text-align:center;user-select:none}pre mark{display:block;background-color:rgba(254,252,232,.9)}pre table{width:100%;border-collapse:collapse} \ No newline at end of file diff --git a/book.js b/book.js new file mode 100644 index 0000000..83fbcde --- /dev/null +++ b/book.js @@ -0,0 +1,232 @@ +let elasticlunr; + +function initToggleMenu() { + const $menu = document.querySelector(".menu"); + const $menuIcon = document.querySelector(".menu-icon"); + const $page = document.querySelector(".page"); + $menuIcon.addEventListener("click", () => { + $menu.classList.toggle("menu-hidden"); + $page.classList.toggle("page-without-menu"); + }); +} + +function debounce(func, wait) { + let timeout; + + return () => { + const context = this; + const args = arguments; + clearTimeout(timeout); + + timeout = setTimeout(() => { + timeout = null; + func.apply(context, args); + }, wait); + }; +} + +// Taken from mdbook +// The strategy is as follows: +// First, assign a value to each word in the document: +// Words that correspond to search terms (stemmer aware): 40 +// Normal words: 2 +// First word in a sentence: 8 +// Then use a sliding window with a constant number of words and count the +// sum of the values of the words within the window. Then use the window that got the +// maximum sum. If there are multiple maximas, then get the last one. +// Enclose the terms in . +function makeTeaser(body, terms) { + const TERM_WEIGHT = 40; + const NORMAL_WORD_WEIGHT = 2; + const FIRST_WORD_WEIGHT = 8; + const TEASER_MAX_WORDS = 30; + + const stemmedTerms = terms.map((w) => elasticlunr.stemmer(w.toLowerCase())); + let termFound = false; + let index = 0; + const weighted = []; // contains elements of ["word", weight, index_in_document] + + // split in sentences, then words + const sentences = body.toLowerCase().split(". "); + + for (const i in sentences) { + const words = sentences[i].split(" "); + let value = FIRST_WORD_WEIGHT; + + for (const j in words) { + const word = words[j]; + + if (word.length > 0) { + for (const k in stemmedTerms) { + if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { + value = TERM_WEIGHT; + termFound = true; + } + } + weighted.push([word, value, index]); + value = NORMAL_WORD_WEIGHT; + } + + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + return body; + } + + const windowWeights = []; + const windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); + // We add a window with all the weights first + let curSum = 0; + for (let i = 0; i < windowSize; i++) { + curSum += weighted[i][1]; + } + windowWeights.push(curSum); + + for (let i = 0; i < weighted.length - windowSize; i++) { + curSum -= weighted[i][1]; + curSum += weighted[i + windowSize][1]; + windowWeights.push(curSum); + } + + // If we didn't find the term, just pick the first window + let maxSumIndex = 0; + if (termFound) { + let maxFound = 0; + // backwards + for (let i = windowWeights.length - 1; i >= 0; i--) { + if (windowWeights[i] > maxFound) { + maxFound = windowWeights[i]; + maxSumIndex = i; + } + } + } + + const teaser = []; + let startIndex = weighted[maxSumIndex][2]; + for (let i = maxSumIndex; i < maxSumIndex + windowSize; i++) { + const word = weighted[i]; + if (startIndex < word[2]) { + // missing text from index to start of `word` + teaser.push(body.substring(startIndex, word[2])); + startIndex = word[2]; + } + + // add around search terms + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + startIndex = word[2] + word[0].length; + teaser.push(body.substring(word[2], startIndex)); + + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + } + teaser.push("…"); + return teaser.join(""); +} + +function formatSearchResultItem(item, terms) { + const li = document.createElement("li"); + li.classList.add("search-results__item"); + li.innerHTML = `${item.doc.title}`; + li.innerHTML += `
${makeTeaser( + item.doc.body, + terms + )}
`; + return li; +} + +// Go from the book view to the search view +function toggleSearchMode() { + const $bookContent = document.querySelector(".book-content"); + const $searchContainer = document.querySelector(".search-container"); + if ($searchContainer.classList.contains("search-container--is-visible")) { + $searchContainer.classList.remove("search-container--is-visible"); + document.body.classList.remove("search-mode"); + $bookContent.style.display = "block"; + } else { + $searchContainer.classList.add("search-container--is-visible"); + document.body.classList.add("search-mode"); + $bookContent.style.display = "none"; + document.getElementById("search").focus(); + } +} + +function initSearch() { + const $searchInput = document.getElementById("search"); + if (!$searchInput) { + return; + } + const $searchIcon = document.querySelector(".search-icon"); + $searchIcon.addEventListener("click", toggleSearchMode); + + const $searchResults = document.querySelector(".search-results"); + const $searchResultsHeader = document.querySelector( + ".search-results__header" + ); + const $searchResultsItems = document.querySelector(".search-results__items"); + const MAX_ITEMS = 10; + + const options = { + bool: "AND", + fields: { + title: { boost: 2 }, + body: { boost: 1 }, + }, + }; + let currentTerm = ""; + const index = elasticlunr.Index.load(window.searchIndex); + + $searchInput.addEventListener( + "keyup", + debounce(() => { + const term = $searchInput.value.trim(); + if (term === currentTerm || !index) { + return; + } + $searchResults.style.display = term === "" ? "none" : "block"; + $searchResultsItems.innerHTML = ""; + if (term === "") { + return; + } + + const results = index + .search(term, options) + .filter((r) => r.doc.body !== ""); + if (results.length === 0) { + $searchResultsHeader.innerText = `No search results for '${term}'.`; + return; + } + + currentTerm = term; + $searchResultsHeader.innerText = `${results.length} search results for '${term}':`; + for (let i = 0; i < Math.min(results.length, MAX_ITEMS); i++) { + if (!results[i].doc.body) { + continue; + } + + $searchResultsItems.appendChild( + formatSearchResultItem(results[i], term.split(" ")) + ); + } + }, 150) + ); +} + +if ( + document.readyState === "complete" || + (document.readyState !== "loading" && !document.documentElement.doScroll) +) { + initToggleMenu(); +} else { + document.addEventListener("DOMContentLoaded", () => { + initToggleMenu(); + initSearch(); + }); +} diff --git a/elasticlunr.min.js b/elasticlunr.min.js new file mode 100644 index 0000000..79dad65 --- /dev/null +++ b/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.6 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o + + + + + + + Rust course + + + + + + + + + + + + + + + +
+ + +
+ +
+ +
+
+
    +
    +
    + +
    + +

    JNP 2: Rust

    +

    This is a website for the JNP2: Rust course at MIM UW.

    +

    The course's instructor is Wojciech Przytuła (GitHub: wprzytula, e-mail: wp418383[at]students[dot]mimuw[dot]edu[dot]pl),

    +

    Instructori emeriti:

    +
      +
    • Piotr Wojtczak (GitHub: StarostaGit, e-mail: pw394980[at]students[dot]mimuw[dot]edu[dot]pl),
    • +
    • Andrzej Głuszak (GitHub: agluszak, e-mail: agluszak[at]mimuw[dot]edu[dot]pl).
    • +
    +

    You can find lesson scenarios here.

    +

    Contributions are welcome! ;)

    + + +
    +
    + + + + +
    + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/00-organizational/index.html b/lessons/00-organizational/index.html new file mode 100644 index 0000000..1f599d4 --- /dev/null +++ b/lessons/00-organizational/index.html @@ -0,0 +1,257 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
    + + +
    + +
    + +
    +
    +
      +
      +
      + +
      + +

      Organizational lesson

      +

      + 2024-10-03 (last edit: 2024-10-06) +

      +

      Rust course

      +

      We will be using Github Classroom for task submission and Discord for discussions.

      +

      Our main learning/teaching resource will be "The Book".

      +

      Also worth mentioning: "Rust by Example".

      +

      Grading

      +
        +
      • 1/3 of the grade is based on small tasks. There will be approximately 1 task every two weeks and each task will be graded on a scale of 0 to 3.
      • +
      • 2/3 of the grade is based on a big project. You can choose a topic yourself, but it must be accepted by me. The project has to be split into two parts. It can be done in groups of two (or bigger, if ambitious enough).
      • +
      • The grade may be increased by a bonus. You can get a bonus for: +
          +
        • Making a presentation about some advanced topic (const generics, futures, macros, etc.) or about architecture of a selected Rust open-source library
        • +
        • Contributing to a selected Rust open-source library
        • +
        • Contributing to this course's materials
        • +
        • Quizzes, homeworks, general activity etc.
        • +
        +
      • +
      +

      Project Deadlines

      +
        +
      1. 2024-11-7: Project ideas should be presented to me for further refining. If you wish to pair up with someone, now is the time to tell me.
      2. +
      3. 2024-11-14: Final project ideas should be accepted by now.
      4. +
      5. 2024-12-12: Deadline for submitting the first part of the project.
      6. +
      7. 2025-01-09: Deadline for submitting the second and final part of the project.
      8. +
      + + +
      +
      + + + + +
      + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.html b/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.html new file mode 100644 index 0000000..10f5407 --- /dev/null +++ b/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.html @@ -0,0 +1,4780 @@ + + + + + + + + + + + + + + + + +
      + +
      +
      + +
      +

      A taste of Rust

      +

      + ...so that you quickly grasp its core concepts and + characteristics. +

      +
      +
      +
      +

      + Syntax: mostly similar to C++ +

      +
      +              fn
      +                bar(num_cats:
      +                u32)
      +                ->
      +                usize {
      +                let
      +                a: &str
      +                = "Ann has ";
      +                // <-- think of char*
      +
      +                let
      +                mut b:
      +                String =
      +                String::from(a);
      +                // <-- think of std::string
      +                b += num_cats.to_string(); b += " cats";
      +                b.push('.');
      +
      +                println!("{}", &b); return b.len();
      +
      +                // <-- b is dropped (deallocated) here - RAII mechanism
      +                  employed.
      +                }
      +              
      +            
      +
      +
      +
      +

      Syntax: C/C++/ML/...

      +

      + C/C++: Code blocks with braces, lines end with + semicolons, main function begins every program, + modules/associated functions :: notation (e.g. + std::thread::spawn)... +

      +

      + ML: Match statement, code blocks evaluated as + expressions. +

      +
      +
      +
      +

      + Inspired by functional languages (ML, Haskell) +

      +
      +
      +
      +

      + Variables are not mutable by default +

      +
      +              fn
      +                variables_non_mut_by_default() { let
      +                cant_mutate_me =
      +                5;
      +                // <-- variable `const` by default
      +                // cant_mutate_me = 3; <-- compile error
      +                let
      +                mut can_mutate_me =
      +                5; can_mutate_me +=
      +                1; }
      +              
      +            
      +
      +
      +
      +

      + Blocks' last statement can be evaluated as an expression +

      +
      +              fn
      +                blocks_are_expressions(flag: bool)
      +                ->
      +                i32 {
      +                let
      +                x =
      +                if flag {
      +                42
      +                // <-- mind lack of semicolon
      +                } else {
      +                let
      +                init = -37; (init + 17) * (init -
      +                3)
      +                // <-- the last statement can be returned
      +                }; x
      +                // <-- same about functions - their body is a block,
      +                  too.
      +                }
      +              
      +            
      +
      +
      +
      +

      + Enum types, pattern matching +

      +
      +              enum
      +                Animal { Cat { tail_len:
      +                usize }, Fish, }
      +
      +                fn
      +                pattern_matching(animal: Animal) { let
      +                tail_len =
      +                match animal { Cat { tail_len
      +                } => tail_len, Fish => 0,
      +                }; }
      +              
      +            
      +
      +
      +
      +

      Functional iterators

      +
      +              let
      +                peers:
      +                Vec<Peer> = initial_peers
      +                .iter()
      +                .enumerate()
      +                .map(|(id,
      +                endpoint)| { let
      +                token =
      +                get_token(endpoint); Peer { address: endpoint.address(), tokens:
      +                vec![Token::new(token as
      +                i64)], datacenter:
      +                None, rack:
      +                None, host_id: Uuid::new_v4(), } }) .collect();
      +              
      +            
      +
      +
      +
      +

      + What Rust lacks (on purpose) +

      +
      +
      +
      +

      Classes, inheritance?

      +
      +              class
      +                Vehicle {
      +                float
      +                  estimate_cost(float route_len) = 0; };
      +                class
      +                Car: Vehicle {
      +                int engine_size_cm3; };
      +                class
      +                Bike: Vehicle {
      +                int rider_stamina; };
      +
      +                float
      +                  Car::estimate_cost(float route_len) { engine_size_cm3 * CAR_COST_COEFFICIENT * route_len }
      +
      +                float
      +                  Bike::estimate_cost(float route_len) { /* */ }
      +              
      +            
      +
      +
      +
      +

      + Classes, inheritance. Structs, traits, composition. +

      +
      +              struct
      +                Car { engine_size_cm3:
      +                i32 };
      +                struct
      +                Bike { rider_stamina:
      +                i32 };
      +
      +                trait
      +                Travel {
      +                fn
      +                estimate_cost(&self, route_len:
      +                f32)
      +                ->
      +                f32; }
      +
      +                impl
      +                Travel
      +                for
      +                Car {
      +                fn
      +                estimate_cost(&self, route_len:
      +                f32)
      +                ->
      +                f32 {
      +                self.engine_size_cm3 *
      +                CAR_COST_COEFFICIENT * route_len } }
      +
      +                impl
      +                Travel
      +                for
      +                Bike {
      +                /* */ }
      +              
      +            
      +
      +
      +
      +

      Null value?

      +
      +              public
      +                void
      +                doSomething(SomeObject obj) {
      +                obj.some_method();
      +                // BOOOM! NullPointerException!
      +
      +                if obj !=
      +                null { obj.some_method();
      +                // Whew, we are safe now. We checked this.
      +                }
      +
      +                /* A lot of code later */
      +
      +                // Have I checked `obj` for not being null? Yeah, for
      +                  sure.
      +                obj.some_method();
      +                // BOOOM! NullPointerException!
      +                }
      +              
      +            
      +
      +
      +
      +

      + Null value. Lack of value represented as an Option enum. +

      +
      +              fn
      +                this_returns_something_or_nothing(password: &str)
      +                ->
      +                Result<i32, String> {
      +                if password ==
      +                "Rust rulez!" {
      +                Ok(42) } else {
      +                Err("You still have to learn a lot...") } }
      +
      +                fn
      +                main() {
      +                match
      +                this_can_fail_or_succeed("Rust is hard...")
      +                { Ok(code)
      +                => println!("Success! Code: {}", code),
      +                Err(msg)
      +                => println!("Oops... {}", msg), }; }
      +              
      +            
      +
      +
      +
      +

      Exceptions?

      +
      +              void
      +                  some_critical_operation(data: VeryFragile) {
      +                // We have to be extremely careful not to interrupt
      +                  this.
      +                // Else we will end up with an invalid state!
      +
      +                int
      +                const res =
      +                some_innocent_procedure(data);
      +                // BOOOM! Exception thrown.
      +
      +                finalise_critical_operation(res); }
      +
      +                fn main() {
      +                match this_can_fail_or_succeed("Rust is hard...") { Ok(code) =>
      +                println!("Success! Code: {}", code), Err(msg) =>
      +                println!("Oops... {}", msg), }; }
      +              
      +            
      +
      +
      +
      +

      + Exceptions. Errors propagated explicitly as enums. +

      +
      +              fn
      +                this_can_fail_or_succeed(password: &str)
      +                ->
      +                Result<i32, String> {
      +                if password ==
      +                "Rust rulez!" {
      +                Ok(42) } else {
      +                Err("You still have to learn a lot...") } }
      +
      +                fn
      +                main() {
      +                match
      +                this_can_fail_or_succeed("Rust is hard...")
      +                { Ok(code)
      +                => println!("Success! Code: {}", code),
      +                Err(msg)
      +                => println!("Oops... {}", msg), }; }
      +              
      +            
      +
      +
      +
      +

      + Exceptions. Errors propagated explicitly as enums. +

      +
      +              fn
      +                deserialize( typ:
      +                &'frame ColumnType, v:
      +                Option<FrameSlice<'frame>>, ) ->
      +                Result<CqlType,
      +                DeserializationError> {
      +                let
      +                mut val =
      +                ensure_not_null_slice::<CqlType>(typ, v)?;
      +                let
      +                cql =
      +                deser_cql_value(typ, &mut val) .map_err(deser_error_replace_rust_name::<CqlType>)?;
      +                Ok(cql) }
      +              
      +            
      +
      +
      +
      +

      + Unique feature of Rust - the borrow checker +

      +
      +
      +
      +

      Dangling references?

      +
      +              int
      +                  const&
      +                  bar() { int n =
      +                10;
      +                return n; }
      +
      +                int
      +                  main() { int
      +                const& i =
      +                bar();
      +                // i is a dangling reference to an invalidated stack
      +                  frame...
      +                std::cout << i << std::endl;
      +                // May result in segmentation fault.
      +                return
      +                0; }
      +              
      +            
      +
      +
      +
      +

      + Dangling references Lifetimes! +

      +
      +              // This function does not compile! Reference can't
      +                  borrow from nowhere.
      +                fn
      +                bar()
      +                -> &i32
      +                { let
      +                n =
      +                10; &n }
      +              
      +            
      +
      +
      +
      +

      + Dangling references Lifetimes! +

      +
      +              fn
      +                main() {
      +                let
      +                v =
      +                vec![1, 2,
      +                3];
      +                let
      +                v_ref = &v;
      +                std::mem::drop(v);
      +
      +                // This does not compile! `v` has been dropped,
      +                // so references to it are no longer valid.
      +                let
      +                n = v_ref[1]; }
      +              
      +            
      +
      +
      +
      +

      + Move semantics by default (& more lifetimes!). +

      +
      +              fn
      +                main() {
      +                let
      +                mut x =
      +                vec![1, 2,
      +                3];
      +                let
      +                y = x;
      +                // x's contents are moved into y.
      +                // No heap allocation involved, O(1).
      +
      +                // This does not compile! `x`'s contents has been moved
      +                  out,
      +                // so it is no longer valid (alive).
      +                // let n = x[0];
      +
      +                x = vec![0, 0];
      +                // Now x is valid (alive) again.
      +                let
      +                n = x[0]; }
      +              
      +            
      +
      +
      +
      +

      Data races?

      +
      +              void
      +                  thread1(shared_data: &Data) { while
      +                true { shared_data.write(next_int()); } }
      +
      +                void
      +                  thread2(shared_data: const&
      +                    Data) { while
      +                true { std::cout <<
      +                shared_data.read() <<
      +                std::endl; } }
      +
      +                int
      +                  main() { Data shared_data;
      +
      +                // The threads race with each other!
      +                // A write is concurrent to another memory access (read or
      +                  write)!
      +                std::thread t1{shared_data}; std::thread t2{shared_data}; }
      +              
      +            
      +
      +
      +
      +

      + Data races Aliasing XOR mutability +

      +
      +              fn
      +                thread1(shared_data:
      +                &mut Data) {
      +                loop { shared_data.write(next_int()); } } fn
      +                thread2(shared_data:
      +                &Data) { loop {
      +                println!("{}", shared_data.read()); } } fn
      +                main() {
      +                let
      +                mut shared_data = Data::new(); std::thread::scope(|s| { let
      +                t1 = s.spawn(|| {
      +                thread1(&mut shared_data); });
      +                let
      +                t2 = s.spawn(|| {
      +                thread2(&shared_data);
      +                // Compiler yells:
      +                // "cannot borrow `shared_data` as immutable because it
      +                  is also borrowed as mutable"
      +                }); }); }
      +              
      +            
      +
      +
      +
      +

      Rust

      +

      + A language empowering everyone to build reliable and efficient + software. +

      +
      +
      +
      +
      + + + diff --git a/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.pdf b/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.pdf new file mode 100644 index 0000000..c3b3167 Binary files /dev/null and b/lessons/01-introduction/a_taste_of_rust-introductory_slides/A_taste_of_Rust.pdf differ diff --git a/lessons/01-introduction/clippy.jpg b/lessons/01-introduction/clippy.jpg new file mode 100644 index 0000000..cf490ba Binary files /dev/null and b/lessons/01-introduction/clippy.jpg differ diff --git a/lessons/01-introduction/conditionals.rs b/lessons/01-introduction/conditionals.rs new file mode 100644 index 0000000..441aba6 --- /dev/null +++ b/lessons/01-introduction/conditionals.rs @@ -0,0 +1,20 @@ +#![allow(unused_variables)] + +fn main() { + let x = 42; + + if x == 42 { + println!("x is 42"); + } else if x == 43 { + println!("x is 43"); + } else { + println!("x is not 42 or 43"); + } + + // we can also use ifs as expressions + let a_or_b = if x == 0 { + "a" // notice no semicolon at the end + } else { + "b" + }; +} diff --git a/lessons/01-introduction/cpp_meme.jpg b/lessons/01-introduction/cpp_meme.jpg new file mode 100644 index 0000000..4ec2e17 Binary files /dev/null and b/lessons/01-introduction/cpp_meme.jpg differ diff --git a/lessons/01-introduction/errors_demo.c b/lessons/01-introduction/errors_demo.c new file mode 100644 index 0000000..936ac9c --- /dev/null +++ b/lessons/01-introduction/errors_demo.c @@ -0,0 +1,35 @@ +#include + +#define LENGTH 3 + +int main() { + //# Syntax error + // printf("hello world"; + + //char* array[LENGTH] = {"hello", "ancient", "world!"}; + + //# Array out-of-bounds with a statically known index + // printf("%s\n", array[LENGTH]); + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= LENGTH; i++) { + // printf("%s\n", array[i]); + // } + + //# Doing God knows what + // char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = 0 ? 1 : 0; + // int joy_division = 1/joy; + + // printf("joy division equals %d", joy_division); + + return 0; +} diff --git a/lessons/01-introduction/errors_demo.cpp b/lessons/01-introduction/errors_demo.cpp new file mode 100644 index 0000000..1c7d88b --- /dev/null +++ b/lessons/01-introduction/errors_demo.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +using std::cout; +using std::endl; + +int main() { + //# Syntax error + // cout < "hello world"; + + // constexpr int length = 3; + // std::array array = {"hello", "old", "world!"}; + + //# Array access with an out of bounds index and bounds checking during compile time + // cout << std::get(array) << endl; + + //# Array access with an out of bounds index and bounds checking during runtime + // cout << array.at(length) << endl; + + //# Most common access without any checks + // cout << array[length] << endl; + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= length; i++) { + // cout << array.at(i) << endl; + // } + + //# This will be there in Clang 14 ... + // std::string format = std::format("a very innocent hello {}"); + // cout << format << endl; + + //# ... but for now, this is doing God knows what + // const char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = false ? 1 : 0; + // int joy_division = 1/joy; + + // cout << "joy division equals " << joy_division << endl; + + return 0; +} diff --git a/lessons/01-introduction/errors_demo.rs b/lessons/01-introduction/errors_demo.rs new file mode 100644 index 0000000..bf5a394 --- /dev/null +++ b/lessons/01-introduction/errors_demo.rs @@ -0,0 +1,29 @@ +fn main() { + //# Syntax error + // println("hello world"); + + // let array = ["hello", "new", "world!"]; + + //# Array out-of-bounds with a statically known index + // println!("{}", array[3]); + + //# Array out-of-bounds with a dynamically computed index + // for i in 0..=array.len() { + // println!("{}", array[i]); + // } + + //# An unsuccessful attempt at emulating C++'s ability to read the memory we're not supposed to access + // let format = "a very innocent hello {}"; + // println!(format); + + //# Division by zero + // let joy_division = 0/0; + + // let joy = 0; + // let joy_division = 0/joy; + + // let joy = if false {1} else {0}; + // let joy_division = 0/joy; + + // println!("{}", joy_division); +} diff --git a/lessons/01-introduction/functions.rs b/lessons/01-introduction/functions.rs new file mode 100644 index 0000000..2987a36 --- /dev/null +++ b/lessons/01-introduction/functions.rs @@ -0,0 +1,12 @@ +fn get_5() -> u32 { + 5 // we could also write "return 5;" +} + +fn print_sum(a: u32, b: u32) { + println!("a + b = {}", a + b); +} + +fn main() { + let a = 100; + print_sum(a, get_5()); +} diff --git a/lessons/01-introduction/hello_world.rs b/lessons/01-introduction/hello_world.rs new file mode 100644 index 0000000..fa9fb84 --- /dev/null +++ b/lessons/01-introduction/hello_world.rs @@ -0,0 +1,4 @@ +fn main() { + let name = "World"; + println!("Hello, {}!", name); // using the println! macro +} diff --git a/lessons/01-introduction/index.html b/lessons/01-introduction/index.html new file mode 100644 index 0000000..3888984 --- /dev/null +++ b/lessons/01-introduction/index.html @@ -0,0 +1,672 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
      + + +
      + +
      + +
      +
      +
        +
        +
        + +
        + +

        Introduction to Rust

        +

        + 2024-10-03 (last edit: 2024-09-20) +

        +

        Logo

        +

        A language empowering everyone to build reliable and efficient software.

        +

        (unofficial logo)

        +

        Why use Rust?

        +
          +
        • It is safe (compared to C++ for example, as we will see in a minute)
        • +
        • It is fast (because it is compiled to machine code)
        • +
        • It is ergonomic and pleasant to use (static typing, expressive type system, helpful compiler +warnings)
        • +
        • It +is loved by programmers
        • +
        • It provides excellent tooling
        • +
        +

        Why learn Rust?

        +

        Even if you don't end up using Rust, learning it expands your horizons

        +
          +
        • it helps especially with the awareness of what you can and can't do in concurrent applications
        • +
        • it helps you understand memory management and learn its good practices
        • +
        +

        Why not to learn Rust?

        +
          +
        • Some people say Rust is too hard to learn because of the borrow checker
        • +
        • Once you get to know Cargo you won't ever want to use a language without a built-in package +manager ;)
        • +
        • You will start hating C++
        • +
        +

        Demos

        +

        Meme

        +

        Let's compare the same code written in C, C++ +and Rust.

        +

        Code you sent in previous editions!

        +

        Aleksander Tudruj

        +
        #include <iostream>
        +#include <unordered_map>
        +
        +using name = std::string;
        +using age = int;
        +using person = std::pair<name, age>;
        +using address = std::string;
        +using address_book = std::unordered_map<person, address>;
        +
        +void print_address_book(const address_book &book)
        +{
        +    for (const auto &[person, address] : book)
        +    {
        +        std::cout << person.first << " is " << person.second << " years old and lives at " << address << std::endl;
        +    }
        +}
        +
        +int main()
        +{
        +
        +    address_book people{};
        +    people.insert({{"John", 20}, "221B Baker Street, London"});
        +    people.insert({{"Mary", 30}, "Avenue des Champs-Élysées, Paris"});
        +    people.insert({{"Jack", 73}, "Wall Street, New York"});
        +    print_address_book(people);
        +
        +    return 0;
        +}
        +
        +
        +

        (Download the source code for this example: tudruj.cpp)

        +

        Krystyna Gasińska

        +
        # sample 1 - different ways of removing elements from the list while iterating
        +list1 = [1, 2, 3, 4]
        +for idx, item in enumerate(list1):
        +    del item
        +list1
        +
        +# [1, 2, 3, 4]
        +
        +list2 = [1, 2, 3, 4]
        +for idx, item in enumerate(list2):
        +    list2.remove(item)
        +list2
        +
        +# [2, 4]
        +
        +list3 = [1, 2, 3, 4]
        +for idx, item in enumerate(list3[:]):
        +    list3.remove(item)
        +list3
        +
        +# []
        +
        +list4 = [1, 2, 3, 4]
        +for idx, item in enumerate(list4):
        +    list4.pop(idx)
        +list4
        +
        +# [2, 4]
        +
        +# sample 2 - string interning
        +a = "abc"
        +b = "abc"
        +a is b
        +
        +# True
        +
        +a = ''.join(['a', 'b', 'c'])
        +b = ''.join(['a', 'b', 'c'])
        +a is b
        +
        +# False
        +
        +a = "abc!"
        +b = "abc!"
        +a is b
        +
        +# False
        +
        +# sample 3 - chained operations
        +(False == False) in [False]
        +
        +# False
        +
        +False == (False in [False])
        +
        +# False
        +
        +False == False in [False] # unexpected...
        +
        +# True
        +
        +# sample 4 - is operator
        +a = 256
        +b = 256
        +a is b
        +
        +# True
        +
        +a = 257
        +b = 257
        +a is b
        +
        +# False
        +
        +a, b = 257, 257
        +a is b
        +
        +# True
        +
        +257 is 257
        +
        +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
        +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
        +# C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
        +#  257 is 257
        +
        +# sample 5 - local variables
        +def f(trufel):
        +    if trufel:
        +        y = 1
        +    y += 1
        +
        +f(True) # everything is fine
        +
        +f(False) # gives error: local variable 'y' referenced before assignment
        +
        +# ---------------------------------------------------------------------------
        +# UnboundLocalError                         Traceback (most recent call last)
        +# Input In [17], in <cell line: 1>()
        +# ----> 1 f(False)
        +
        +# Input In [15], in f(trufel)
        +#       3 if trufel:
        +#       4     y = 1
        +# ----> 5 y += 1
        +
        +# UnboundLocalError: local variable 'y' referenced before assignment
        +
        +

        (Download the source code for this example: gasinska.py)

        +

        Antoni Koszowski

        +
        // mutowalność jest wbudowana w język
        +
        +type S struct {
        +    A string
        +    B []string
        +}
        + 
        +func main() {
        +    x := S{"x-A", []string{"x-B"}}
        +    y := x // copy the struct
        +    y.A = "y-A"
        +    y.B[0] = "y-B"
        + 
        +    fmt.Println(x, y)
        +    // Outputs "{x-A [y-B]} {y-A [y-B]}" -- x was modified!
        +}
        +
        +// slices i kwestia append
        +
        +func doStuff(value []string) {
        +    fmt.Printf("value=%v\n", value)
        + 
        +    value2 := value[:]
        +    value2 = append(value2, "b")
        +    fmt.Printf("value=%v, value2=%v\n", value, value2)
        + 
        +    value2[0] = "z"
        +    fmt.Printf("value=%v, value2=%v\n", value, value2)
        +}
        + 
        +func main() {
        +    slice1 := []string{"a"} // length 1, capacity 1
        + 
        +    doStuff(slice1)
        +    // Output:
        +    // value=[a] -- ok
        +    // value=[a], value2=[a b] -- ok: value unchanged, value2 updated
        +    // value=[a], value2=[z b] -- ok: value unchanged, value2 updated
        + 
        +    slice10 := make([]string, 1, 10) // length 1, capacity 10
        +    slice10[0] = "a"
        + 
        +    doStuff(slice10)
        +    // Output:
        +    // value=[a] -- ok
        +    // value=[a], value2=[a b] -- ok: value unchanged, value2 updated
        +    // value=[z], value2=[z b] -- WTF?!? value changed???
        +}
        +
        +// error handling
        +
        +len, err := reader.Read(bytes)
        +if err != nil {
        +    if err == io.EOF {
        +        // All good, end of file
        +    } else {
        +        return err
        +    }
        +}
        +
        +
        +// interfejs nil
        +
        +type Explodes interface {
        +    Bang()
        +    Boom()
        +}
        + 
        +// Type Bomb implements Explodes
        +type Bomb struct {}
        +func (*Bomb) Bang() {}
        +func (Bomb) Boom() {}
        + 
        +func main() {
        +    var bomb *Bomb = nil
        +    var explodes Explodes = bomb
        +    println(bomb, explodes) // '0x0 (0x10a7060,0x0)'
        +    if explodes != nil {
        +        println("Not nil!") // 'Not nil!' What are we doing here?!?!
        +        explodes.Bang()     // works fine
        +        explodes.Boom()     // panic: value method main.Bomb.Boom called using nil *Bomb pointer
        +    } else {
        +        println("nil!")     // why don't we end up here?
        +    }
        +}
        +
        +// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{}
        +// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany.
        +
        +

        (Download the source code for this example: koszowski.go)

        +

        Mieszko Grodzicki

        +
        def add_contents(input_list, contents=[]):
        +     for val in input_list:
        +         contents.append(val)
        +     return contents
        +
        +print(add_contents([1])) # [1]
        +print(add_contents([2])) # [1, 2]
        +
        +

        (Download the source code for this example: grodzicki.py)

        +

        Installing Rust

        + +

        Useful tools

        +

        Clippy

        +
          +
        • cargo clippy (for static analysis)
        • +
        • there's also cargo check, but it's less powerful than clippy
        • +
        • cargo fmt (for code formatting)
        • +
        +

        Rust Playground

        + +

        Hello world

        +
        fn main() {
        +    let name = "World";
        +    println!("Hello, {}!", name); // using the println! macro
        +}
        +
        +
        +

        (Download the source code for this example: hello_world.rs)

        +

        Variables

        +
        #![allow(unused_variables)]
        +#![allow(unused_assignments)]
        +
        +fn main() {
        +    let x = 40; // inferred type
        +    let y: i32 = 100; // specified type
        +
        +    {
        +        let x = 40 + 2; // shadowing
        +        println!("x is {}", x); // prints 42
        +    }
        +
        +    // x = 0; // compilation error, variables are by default immutable
        +    let mut x = 40; // declare as mutable
        +    x = 0; // now we can reassign
        +
        +    x += 1; // x = x + 1
        +}
        +
        +
        +

        (Download the source code for this example: variables.rs)

        +

        Conditionals

        +
        #![allow(unused_variables)]
        +
        +fn main() {
        +    let x = 42;
        +
        +    if x == 42 {
        +        println!("x is 42");
        +    } else if x == 43 {
        +        println!("x is 43");
        +    } else {
        +        println!("x is not 42 or 43");
        +    }
        +
        +    // we can also use ifs as expressions
        +    let a_or_b = if x == 0 {
        +        "a" // notice no semicolon at the end
        +    } else {
        +        "b"
        +    };
        +}
        +
        +
        +

        (Download the source code for this example: conditionals.rs)

        +

        Loops

        +
        #![allow(unused_variables)]
        +
        +fn main() {
        +    for i in 0..10 {
        +        println!("i is {}", i); // i in [0, 10)
        +    }
        +
        +    let mut x = 0;
        +
        +    while x < 50 {
        +        x += 1;
        +    }
        +
        +    let mut y = 0;
        +    let mut iterations = 0;
        +    loop {
        +        iterations += 1;
        +        if iterations % 2 == 0 {
        +            continue;
        +        }
        +        y += 1;
        +        if y == 10 {
        +            break;
        +        }
        +    }
        +
        +    // we can use labels to refer to a specific loop
        +    let mut count = 0;
        +    'counting_up: loop {
        +        let mut remaining = 10;
        +
        +        loop {
        +            if remaining == 9 {
        +                break;
        +            }
        +            if count == 2 {
        +                break 'counting_up; // ends the outer loop
        +            }
        +            remaining -= 1;
        +        }
        +
        +        count += 1;
        +    }
        +
        +    // We can use break with a value.
        +    // Because loops are expressions too,
        +    // the value we break with will be returned from the functions
        +    let mut counter = 0;
        +    let value = loop {
        +        counter += 1;
        +        if counter == 10 {
        +            break 32;
        +        }
        +    };
        +}
        +
        +
        +

        (Download the source code for this example: loops.rs)

        +

        Functions

        +
        fn get_5() -> u32 {
        +    5 // we could also write "return 5;"
        +}
        +
        +fn print_sum(a: u32, b: u32) {
        +    println!("a + b = {}", a + b);
        +}
        +
        +fn main() {
        +    let a = 100;
        +    print_sum(a, get_5());
        +}
        +
        +
        +

        (Download the source code for this example: functions.rs)

        +

        Test assignment (not graded)

        +

        Click here

        +

        Obligatory reading

        + +

        Additional reading

        + + + +
        +
        + + + + +
        + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/01-introduction/loops.rs b/lessons/01-introduction/loops.rs new file mode 100644 index 0000000..8a02d72 --- /dev/null +++ b/lessons/01-introduction/loops.rs @@ -0,0 +1,55 @@ +#![allow(unused_variables)] + +fn main() { + for i in 0..10 { + println!("i is {}", i); // i in [0, 10) + } + + let mut x = 0; + + while x < 50 { + x += 1; + } + + let mut y = 0; + let mut iterations = 0; + loop { + iterations += 1; + if iterations % 2 == 0 { + continue; + } + y += 1; + if y == 10 { + break; + } + } + + // we can use labels to refer to a specific loop + let mut count = 0; + 'counting_up: loop { + let mut remaining = 10; + + loop { + if remaining == 9 { + break; + } + if count == 2 { + break 'counting_up; // ends the outer loop + } + remaining -= 1; + } + + count += 1; + } + + // We can use break with a value. + // Because loops are expressions too, + // the value we break with will be returned from the functions + let mut counter = 0; + let value = loop { + counter += 1; + if counter == 10 { + break 32; + } + }; +} diff --git a/lessons/01-introduction/students/gasinska.py b/lessons/01-introduction/students/gasinska.py new file mode 100644 index 0000000..e4989f1 --- /dev/null +++ b/lessons/01-introduction/students/gasinska.py @@ -0,0 +1,107 @@ +# sample 1 - different ways of removing elements from the list while iterating +list1 = [1, 2, 3, 4] +for idx, item in enumerate(list1): + del item +list1 + +# [1, 2, 3, 4] + +list2 = [1, 2, 3, 4] +for idx, item in enumerate(list2): + list2.remove(item) +list2 + +# [2, 4] + +list3 = [1, 2, 3, 4] +for idx, item in enumerate(list3[:]): + list3.remove(item) +list3 + +# [] + +list4 = [1, 2, 3, 4] +for idx, item in enumerate(list4): + list4.pop(idx) +list4 + +# [2, 4] + +# sample 2 - string interning +a = "abc" +b = "abc" +a is b + +# True + +a = ''.join(['a', 'b', 'c']) +b = ''.join(['a', 'b', 'c']) +a is b + +# False + +a = "abc!" +b = "abc!" +a is b + +# False + +# sample 3 - chained operations +(False == False) in [False] + +# False + +False == (False in [False]) + +# False + +False == False in [False] # unexpected... + +# True + +# sample 4 - is operator +a = 256 +b = 256 +a is b + +# True + +a = 257 +b = 257 +a is b + +# False + +a, b = 257, 257 +a is b + +# True + +257 is 257 + +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# 257 is 257 + +# sample 5 - local variables +def f(trufel): + if trufel: + y = 1 + y += 1 + +f(True) # everything is fine + +f(False) # gives error: local variable 'y' referenced before assignment + +# --------------------------------------------------------------------------- +# UnboundLocalError Traceback (most recent call last) +# Input In [17], in () +# ----> 1 f(False) + +# Input In [15], in f(trufel) +# 3 if trufel: +# 4 y = 1 +# ----> 5 y += 1 + +# UnboundLocalError: local variable 'y' referenced before assignment \ No newline at end of file diff --git a/lessons/01-introduction/students/grodzicki.py b/lessons/01-introduction/students/grodzicki.py new file mode 100644 index 0000000..1454659 --- /dev/null +++ b/lessons/01-introduction/students/grodzicki.py @@ -0,0 +1,7 @@ +def add_contents(input_list, contents=[]): + for val in input_list: + contents.append(val) + return contents + +print(add_contents([1])) # [1] +print(add_contents([2])) # [1, 2] \ No newline at end of file diff --git a/lessons/01-introduction/students/koszowski.go b/lessons/01-introduction/students/koszowski.go new file mode 100644 index 0000000..2916f91 --- /dev/null +++ b/lessons/01-introduction/students/koszowski.go @@ -0,0 +1,88 @@ +// mutowalność jest wbudowana w język + +type S struct { + A string + B []string +} + +func main() { + x := S{"x-A", []string{"x-B"}} + y := x // copy the struct + y.A = "y-A" + y.B[0] = "y-B" + + fmt.Println(x, y) + // Outputs "{x-A [y-B]} {y-A [y-B]}" -- x was modified! +} + +// slices i kwestia append + +func doStuff(value []string) { + fmt.Printf("value=%v\n", value) + + value2 := value[:] + value2 = append(value2, "b") + fmt.Printf("value=%v, value2=%v\n", value, value2) + + value2[0] = "z" + fmt.Printf("value=%v, value2=%v\n", value, value2) +} + +func main() { + slice1 := []string{"a"} // length 1, capacity 1 + + doStuff(slice1) + // Output: + // value=[a] -- ok + // value=[a], value2=[a b] -- ok: value unchanged, value2 updated + // value=[a], value2=[z b] -- ok: value unchanged, value2 updated + + slice10 := make([]string, 1, 10) // length 1, capacity 10 + slice10[0] = "a" + + doStuff(slice10) + // Output: + // value=[a] -- ok + // value=[a], value2=[a b] -- ok: value unchanged, value2 updated + // value=[z], value2=[z b] -- WTF?!? value changed??? +} + +// error handling + +len, err := reader.Read(bytes) +if err != nil { + if err == io.EOF { + // All good, end of file + } else { + return err + } +} + + +// interfejs nil + +type Explodes interface { + Bang() + Boom() +} + +// Type Bomb implements Explodes +type Bomb struct {} +func (*Bomb) Bang() {} +func (Bomb) Boom() {} + +func main() { + var bomb *Bomb = nil + var explodes Explodes = bomb + println(bomb, explodes) // '0x0 (0x10a7060,0x0)' + if explodes != nil { + println("Not nil!") // 'Not nil!' What are we doing here?!?! + explodes.Bang() // works fine + explodes.Boom() // panic: value method main.Bomb.Boom called using nil *Bomb pointer + } else { + println("nil!") // why don't we end up here? + } +} + +// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{} +// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany. \ No newline at end of file diff --git a/lessons/01-introduction/students/tudruj.cpp b/lessons/01-introduction/students/tudruj.cpp new file mode 100644 index 0000000..a5e48fd --- /dev/null +++ b/lessons/01-introduction/students/tudruj.cpp @@ -0,0 +1,28 @@ +#include +#include + +using name = std::string; +using age = int; +using person = std::pair; +using address = std::string; +using address_book = std::unordered_map; + +void print_address_book(const address_book &book) +{ + for (const auto &[person, address] : book) + { + std::cout << person.first << " is " << person.second << " years old and lives at " << address << std::endl; + } +} + +int main() +{ + + address_book people{}; + people.insert({{"John", 20}, "221B Baker Street, London"}); + people.insert({{"Mary", 30}, "Avenue des Champs-Élysées, Paris"}); + people.insert({{"Jack", 73}, "Wall Street, New York"}); + print_address_book(people); + + return 0; +} diff --git a/lessons/01-introduction/variables.rs b/lessons/01-introduction/variables.rs new file mode 100644 index 0000000..3350568 --- /dev/null +++ b/lessons/01-introduction/variables.rs @@ -0,0 +1,18 @@ +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn main() { + let x = 40; // inferred type + let y: i32 = 100; // specified type + + { + let x = 40 + 2; // shadowing + println!("x is {}", x); // prints 42 + } + + // x = 0; // compilation error, variables are by default immutable + let mut x = 40; // declare as mutable + x = 0; // now we can reassign + + x += 1; // x = x + 1 +} diff --git a/lessons/01_introduction/a_taste_of_rust-introductory_slides/a-taste-of-rust/index.html b/lessons/01_introduction/a_taste_of_rust-introductory_slides/a-taste-of-rust/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/01_introduction/a_taste_of_rust-introductory_slides/a-taste-of-rust/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
        +

        Welcome to Zola!

        +

        + You're seeing this page because we couldn't find a template to render. +

        +

        + To modify this page, create a page.html file in the templates directory or + install a theme. +
        + You can find what variables are available in this template in the documentation. +

        +
        + + + + diff --git a/lessons/02-ownership/aliasing-xor-mutability/Cargo.toml b/lessons/02-ownership/aliasing-xor-mutability/Cargo.toml new file mode 100644 index 0000000..c4044ef --- /dev/null +++ b/lessons/02-ownership/aliasing-xor-mutability/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "aliasing_xor_mutability" +version = "0.1.0" +edition = "2021" diff --git a/lessons/02-ownership/aliasing-xor-mutability/src/main.rs b/lessons/02-ownership/aliasing-xor-mutability/src/main.rs new file mode 100644 index 0000000..d6fed8b --- /dev/null +++ b/lessons/02-ownership/aliasing-xor-mutability/src/main.rs @@ -0,0 +1,39 @@ +fn next_int() -> i32 { + 42 +} + +struct Data(i32); +impl Data { + fn new() -> Self { + Self(0) + } + + fn read(&self) -> i32 { self.0 } + + fn write(&mut self, n: i32) { self.0 = n } +} + +fn thread1(shared_data: &mut Data) { + loop { + shared_data.write(next_int()); + } +} + +fn thread2(shared_data: &Data) { + loop { + println!("{}", shared_data.read()); + } +} + +fn main() { + let mut shared_data = Data::new(); + + std::thread::scope(|s| { + let t1 = s.spawn(|| { + thread1(&mut shared_data); + }); + let t2 = s.spawn(|| { + thread2(&shared_data); + }); + }); +} \ No newline at end of file diff --git a/lessons/02-ownership/dont_panic/dont_panic.html b/lessons/02-ownership/dont_panic/dont_panic.html new file mode 100644 index 0000000..3963ba2 --- /dev/null +++ b/lessons/02-ownership/dont_panic/dont_panic.html @@ -0,0 +1,3903 @@ + + + + + + + + + + + + + + + + +
        + +
        +
        + +
        +

        Don't panic

        +

        + ...or how should your Rust program behave when faced a critical + error. +

        +
        +
        +
        +

        + A situation which unfortunately happens too often... +

        +
        +              // This function returns Order, so we are forced to
        +                  return
        +                // an Order even for incorrect inputs.
        +                fn
        +                create_order(num_dishes: usize)
        +                -> Order {
        +                if num_dishes <
        +                Order::MAXIMUM_NUM_DISHES {
        +                Ok(Order::new(num_dishes)) } else {
        +                // ??? Order::INVALID ???
        +                } }
        +              
        +            
        +
        +
        +
        +

        LET'S PANIC!

        +
        +              // This function returns Order, so we are forced to
        +                  return
        +                // an Order even for incorrect inputs.
        +                fn
        +                create_order(num_dishes: usize)
        +                -> Order {
        +                if num_dishes <
        +                Order::MAXIMUM_NUM_DISHES {
        +                Ok(Order::new(num_dishes)) } else {
        +                panic!("Too many dishes for a single Order")
        +                // This either unwinds the stack or aborts the program
        +                  immediately.
        +                // (configurable in Cargo.toml of your project)
        +                } }
        +              
        +            
        +
        +
        +
        +

        + Hmm, maybe let's reconsider this... +

        +
        +              /// Error signifying that there are too many dishes to fit in
        +                  an Order.
        +                struct
        +                TooManyDishes;
        +
        +                // This function returns Result, so that we are not forced to
        +                  return
        +                // an Order for incorrect inputs - we just return Err.
        +                fn
        +                create_order(num_dishes: usize)
        +                ->
        +                Result<Order,
        +                TooManyDishes> {
        +                if num_dishes <
        +                Order::MAXIMUM_NUM_DISHES {
        +                Ok(Order::new(num_dishes)) } else {
        +                Err(TooManyDishes) } }
        +              
        +            
        +
        +
        +
        +

        + Another common case - Option/Result +

        +
        +              struct
        +                DivisionByZero;
        +
        +                fn
        +                div(dividend:
        +                i32, divisor:
        +                i32)
        +                ->
        +                Result<i32, DivisionByZero> {
        +                if divisor ==
        +                0 {
        +                // It is idiomatic to return errors after failed checks early
        +                  in an imperative way, using explicit `return`.
        +                return
        +                Err(DivisionByZero) }
        +
        +                // It is idiomatic to have the "happy path" (the
        +                  no-errors scenario) linear and using functional syntax.
        +                Ok(dividend /
        +                divisor) } fn
        +                main() {
        +                let
        +                res:
        +                Result<i32, DivisionByZero> =
        +                div(2137, 42);
        +
        +                // We are 100% sure division by 42 can't fail, so
        +                  let's use this shorthand.
        +                let
        +                quotient = res.unwrap();
        +
        +                // This is equivalent to:
        +                let
        +                quotient =
        +                match res {
        +                Ok(x) =>
        +                x, Err(err)
        +                => panic!("called `Result::unwrap()` on an `Err` value:
        +                  {:?}", err), } }
        +              
        +            
        +
        +
        +
        +

        + Let's encode more guarantees in the type system! +

        +
        +              use std::num::NonZeroI32;
        +
        +                fn
        +                div(dividend:
        +                i32, divisor: NonZeroI32)
        +                ->
        +                i32 { dividend / divisor
        +                // Nothing can get broken here!
        +                }
        +                fn
        +                main() {
        +                // let quotient = div(2137, 42); // This would not type
        +                  check, because 42 is not NonZeroI32.
        +
        +                // We have to create a NonZeroI32 instance:
        +                let
        +                non_zero_divisor_opt:
        +                Option<NonZeroI32> =
        +                NonZeroI32::new(42);
        +
        +                // We are 100% sure 42 is not 0, so let's use this
        +                  shorthand.
        +                let
        +                non_zero_divisor =
        +                non_zero_divisor_opt.unwrap();
        +
        +                // This is equivalent to:
        +                let
        +                non_zero_divisor =
        +                match non_zero_divisor_opt {
        +                Some(x) =>
        +                x, None =>
        +                panic!("called `Option::unwrap()` on a `None` value"), }
        +
        +                let
        +                quotient =
        +                div(2137, non_zero_divisor); }
        +              
        +            
        +
        +
        +
        +

        + But wait, we ended up with an unwrap() anyway. Did we + then improve at all? +

        +

        + Actually, yes. Now, the function (here: div()) with + some (possibly complex) logic only accepts (so + that the compiler verifies that in compile-time) valid arguments. + This way, the function's code can be simpler (no + invalid-input-related error handling). Also, it's easier to + convince yourself that your number is nonzero that to make sure + that it upholds all guarantees required in the docs of the + function containing logic. +

        +
        +
        +
        +

        + To panic or not to panic +

        +

        Don't panic:

        +
          +
        • + if the failure is caused by bad user input - you don't want to + open up a highway for DoS attackers, do you? +
        • +
        • + if the failure only affects some task and not the program in + general, e.g. when the server returned bad data for a request of + one of the users; others are unaffected; +
        • +
        • + if the failure is recoverable (there is a reasonable action to + be done in such situation, e.g. give user the default value when + the server can't be queried for the actual value); +
        • +
        +
        +
        +
        +

        + To panic or not to panic +

        +

        Do panic:

        +
          +
        • + if the failure is for sure caused by a bug in your code. It + makes sense to inform the whole world that you wrote deficient + software, by yelling at them. More seriously, this shortens the + feedback loop and bugs are fixed earlier, instead of silently + damaging production; +
        • +
        • + if the failure is not recoverable (there is no hope, the program + is broken, R.I.P., only the famous + restart could help here); +
        • +
        +
        +
        +
        +
        +
        +
        + + + diff --git a/lessons/02-ownership/dont_panic/dont_panic.pdf b/lessons/02-ownership/dont_panic/dont_panic.pdf new file mode 100644 index 0000000..0928151 Binary files /dev/null and b/lessons/02-ownership/dont_panic/dont_panic.pdf differ diff --git a/lessons/02-ownership/index.html b/lessons/02-ownership/index.html new file mode 100644 index 0000000..70b5b34 --- /dev/null +++ b/lessons/02-ownership/index.html @@ -0,0 +1,515 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
        + + +
        + +
        + +
        +
        +
          +
          +
          + +
          + +

          Ownership Model

          +

          + 2024-10-10 (last edit: 2024-10-08) +

          +

          Why all the fuss?

          +

          Even if you've never seen Rust code before, chances are you still heard the term borrow checker or something about Rust's ownership. Indeed, Rust's ownership model lies at the very core of its uniqueness. But to fully understand it and appreciate it, let's first take a look at how memory management is handled in most popular languages.

          +
            +
          • +

            Garbage Collection - in many high-level programming languages, like Java, Haskell or Python, memory management is done fully by the language, relieving the programmer from this burden. This prevents memory leaks and memory related errors (like use after free), but does come at a cost - there is a runtime overhead, both memory and performance wise, caused by the constantly running garbage collection algorithms and the programmer usually has very little control over when the garbage collection takes place. Also, garbage collection does not prevent concurrency-related errors, such as data races, in any way.

            +
          • +
          • +

            Mind your own memory - in low-level languages and specific ones like C++, performance comes first so we cannot really afford to run expansive bookkeeping and cleaning algorithms. Most of these languages compile directly to machine code and have no language-specific runtime environment. That means that the only place where memory management can happen is in the produced code. While compilers insert these construction and destruction calls for stack allocated memory, it generally requires a lot of discipline from the programmer to adhere to good practices and patterns to avoid as many memory related issues as possible and one such bug can be quite deadly to the program and a nightmare to find and fix. These languages basically live by the "your memory, your problem" mantra.

            +
          • +
          +

          And then we have Rust. Rust is a systems programming language and in many ways it's akin to C++ - it's basically low-level with many high-level additions. But unlike C++, it doesn't exactly fall into either of the categories described above, though it's way closer to the second one. It performs no additional management at runtime, but instead imposes a set of rules on the code, making it easier to reason about and thus check for its safety and correctness at compile time - these rules make up Rust's ownership model.

          +

          In a way, programming in Rust is like pair-programming with a patient and very experienced partner. Rust's compiler will make sure you follow all the good patterns and practices (by having them ingrained in the language itself) and very often even tell you how to fix the issues it finds.

          +

          Disclaimer: when delving deeper into Rust below we will make heavy use of concepts like scopes, moving data, stack and heap, which should have been introduced as part of the C++ course. If you need a refresher of any of these, it's best to do so now, before reading further.

          +

          Start with the basics - ownership

          +

          In the paragraph above we mentioned a set of rules that comprise Rust's ownership model. The book starts off with the following three as its very foundation:

          +
            +
          1. +

            Each value in Rust is tied to a specific variable - we call that variable its owner.

            +
          2. +
          3. +

            There can only be one owner at a time.

            +
          4. +
          5. +

            When the owner goes out of scope, the value will be destroyed (or in Rust terms - dropped).

            +
          6. +
          +

          The third point might make you think about C++ and its automatic storage duration. We will later see that, while very similar at first, Rust expands on these mechanics quite a bit. The following code illustrates the basic version of this:

          +
          {
          +    let a: i32 = 5; // allocation on the stack, 'a' becomes an owner
          +
          +    // do some stuff with 'a'
          +
          +} // 'a', the owner, goes out of scope and the value is dropped
          +
          +

          So far, so good. Variables are pushed onto the stack when they enter the scope and destroyed during stack unwinding that happens upon leaving their scope. However, allocating and deallocating simple integers doesn't impress anybody. Let's try something more complex:

          +
          {
          +    let s = String::from("a string"); // 's' is allocated on the stack, while its contents ("a string")
          +                                      // are allocated on the heap. 's' is the owner of this String object.
          +
          +    // do some stuff with 's'
          +
          +} // 's', the owner, goes out of scope and the String is dropped, its heap allocated memory freed
          +
          +

          If you recall the RAII (Resource Acquisition Is Initialization) pattern from C++, the above is basically the same thing. We go two for two now in the similarity department, so... is Rust really any different then? There is a part of these examples that we skipped over - actually doing something with the values.

          +

          Moving around is fun

          +

          Let's expand on the last example. The scoping is not really important for that one, so we don't include it here.

          +
          let s = String::from("a string"); // same thing, 's' is now an owner
          +
          +let s2 = s; // easy, 's2' becomes another owner... right?
          +
          +println!("And the contents are: {}", s); // this doesn't work, can you guess why?
          +
          +

          At first glance everything looks great. If we write this code (well, an equivalent of it) in basically any other popular language, it will compile no issue - but it does not here and there's a good reason why.

          +

          To understand what's happening, we have to consult the rules again, rule 2 in particular. It says that there can only be one owner of any value at a given time. So, s and s2 cannot own the same object. Okay, makes sense, but what is happening in this line then - let s2 = s;? Experience probably tells you that s just gets copied into s2, creating a new String object. That would result in each variable owning its very own instance of the string and each instance having exactly one owner. Sounds like everyone should be happy now, but wait - in that case the last line should work no issue, right? But it doesn't, so can't be a copy. Let's see now what the compiler actually has to say:

          +
          error[E0382]: borrow of moved value: `s`
          + --> src/main.rs:6:42
          +  |
          +2 |     let s = String::from("a string");
          +  |         - move occurs because `s` has type `String`, which does not implement the `Copy` trait
          +3 |
          +4 |     let s2 = s;
          +  |              - value moved here
          +5 |
          +6 |     println!("And the contents are: {}", s);
          +  |                                          ^ value borrowed here after move
          +
          +

          "value moved here" - gotcha! So s is being moved to s2, which also means that s2 now becomes the new owner of the string being moved and s cannot be used anymore. In Rust, the default method of passing values around is by move, not by copy. While it may sound a bit odd at first, it actually has some very interesting implications. But before we get to them, let's fix our code, so it compiles now. To do so, we have to explicitly tell Rust to make a copy by invoking the clone method:

          +
          let s = String::from("a string"); // 's' is an owner
          +
          +let s2 = s.clone(); // 's2' now contains its own copy
          +
          +println!("And the contents are: {}", s); // success!
          +
          +

          The compiler is happy now and so are we. The implicit move takes some getting used to, but the compiler is here to help us. Now, let's put the good, old C++ on the table again and compare the two lines:

          +
          +

          let s2 = s; is equivalent to auto s2 = std::move(s);

          +

          let s2 = s.clone() is equivalent to auto s2 = s

          +
          +

          There are a few important things to note here:

          +
            +
          • +

            Making a copy is oftentimes not cheap. Memory needs to be allocated and copied, and a call to the system has to be made. We should prefer to move things as much as possible to avoid this cost - in C++ we have a myriad of language features like std::move and r-references to achieve this. Every programmer worth their salt needs to be well versed in all of them to write efficient C++ code and simply forgetting one move can lead to significant performance loss (and this happens to even the most senior devs ever existing, let's not pretend). On the contrary, in Rust you need to make an effort to make a copy and that makes you very aware of the cost you're paying - something that we'll see quite a lot of in the language. Also, if you forget a clone there's no harm done - it just won't compile!

            +
          • +
          • +

            Hidden in all of this is another nice thing Rust gives us. In C++, nothing prevents you from using variables after they've been moved from, leading to unexpected errors in a more complex code. In Rust, that variable (in our case s) simply becomes invalid and the compiler gives us a nice error about it.

            +
          • +
          +

          But what about ints?

          +

          A good question to ask. Copying primitives is cheap. And it's not convenient for the programmer to have to always write .clone() after every primitive. If we take a look at the error from the previous example:

          +
          move occurs because `s` has type `String`, which does not implement the `Copy` trait`
          +
          +

          It says that s was moved because the String type doesn't have the Copy trait. We will talk about traits more in depth in the future lessons, but what this basically means is that String is not specified to be copied by default. All primitive types (i32, bool, f64, char, etc.) and tuples consisting only of primitive types implement the Copy trait.

          +

          Exercise

          +

          How to fix that code?

          +
          fn count_animals(num: u32, animal: String) {
          +    println!("{} {} ...", num, animal);
          +}
          +
          +fn main() {
          +  let s = String::from("sheep");
          +
          +  count_animals(1, s);
          +  count_animals(2, s);
          +  count_animals(3, s);
          +}
          +
          +

          Let's borrow some books

          +

          We now know how to move things around and how to clone them if moving is not possible. But what if making a copy is unnecessary - maybe we just want to let someone look at our resource and keep on holding onto it once they're done. Consider the following example:

          +
          fn read_book(book: String) {
          +    println!("[Reading] {}", book);
          +}
          +
          +fn main() {
          +  let book = String::from("Merry lived in a big old house. The end.");
          +
          +  read_book(book.clone());
          +
          +  println!("Book is still there: {}", book);
          +}
          +
          +

          Cloning is pretty excessive here. Imagine recommending a book to your friend and instead of lending it to them for the weekend, you scan it and print an exact copy. Not the best way to go about it, is it? Thankfully, Rust allows us to access a resource without becoming an owner through the use of references and the & operator. This is called a borrow.

          +

          The adjusted code should look like this:

          +
          fn read_book(book: &String) {
          +    println!("[Reading] {}", book);
          +}
          +
          +fn main() {
          +  let book = String::from("Merry lived in a big old house. The end.");
          +
          +  read_book(&book);
          +
          +  println!("Book is still there: {}", book);
          +}
          +
          +

          As with everything, references are too, by default, immutable, which means that the read_book function is not able to modify that book passed into it. We can also borrow something mutably by specifying it both in the receiving function signature and the place it gets called. Maybe you want to have your book signed by its author?

          +
          fn sign_book(book: &mut String) {
          +    book.push_str(" ~ Arthur Author");
          +}
          +
          +fn main() {
          +  // note that the book has to be marked as mutable in the first place
          +  let mut book = String::from("Merry lived in a big old house. The end.");
          +
          +  sign_book(&mut book); // it's always clear when a parameter might get modified
          +
          +  println!("{}", book); // book is now signed
          +}
          +
          +

          Pretty neat, but doesn't seem that safe right now. Let's try to surprise our friend:

          +
          fn erase_book(book: &mut String) {
          +    book.clear();
          +}
          +
          +fn read_book(book: &String) {
          +    println!("[Reading] {}", book);
          +}
          +
          +fn main() {
          +  let mut book = String::from("Merry lived in a big old house. The end.");
          +
          +  let r = &book; // an immutable borrow
          +
          +  erase_book(&mut book); // a mutable borrow
          +
          +  read_book(r); // would be pretty sad to open a blank book when it was not
          +                // what we borrowed initially
          +
          +  println!("{}", book);
          +}
          +
          +

          Fortunately for us (and our poor friend just wanting to read), the compiler steps in and doesn't let us do that, printing the following message:

          +
          error[E0502]: cannot borrow `book` as mutable because it is also borrowed as immutable
          +  --> src/main.rs:14:14
          +   |
          +12 |   let r = &book; // an immutable borrow
          +   |           ----- immutable borrow occurs here
          +13 |
          +14 |   erase_book(&mut book); // a mutable borrow
          +   |              ^^^^^^^^^ mutable borrow occurs here
          +15 |
          +16 |   read_book(r); // would be pretty sad to open a blank book when it was not
          +   |             - immutable borrow later used here
          +
          +

          This is where the famous borrow checker comes in. To keep things super safe, Rust clearly states what can and cannot be done with references and tracks their lifetimes. Exactly one of the following is always true for references to a given resource:

          +
            +
          • +

            There exists only one mutable reference and no immutable references, or

            +
          • +
          • +

            There is any number of immutable references and no mutable ones.

            +
          • +
          +

          You may notice a parallel to the readers - writers problem from concurrent programming. In fact, the way Rust's borrow checker is designed lends itself incredibly well to preventing data race related issues.

          +

          Dangling references

          +

          Rust also checks for dangling references. If we try to compile the following code:

          +
          fn main() {
          +    let reference_to_nothing = dangle();
          +}
          +
          +fn dangle() -> &String {
          +    let s = String::from("hello");
          +
          +    &s
          +}
          +
          +

          we will get an adequate error:

          +
          error[E0106]: missing lifetime specifier
          + --> src/main.rs:5:16
          +  |
          +5 | fn dangle() -> &String {
          +  |                ^ expected named lifetime parameter
          +  |
          +  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
          +help: consider using the `'static` lifetime
          +  |
          +5 | fn dangle() -> &'static String {
          +  |                ^^^^^^^^
          +
          +

          The message above suggests specifing a lifetime for the returned string. In Rust, the lifetime of each variable is also a part of its type, but we will talk more about it later.

          +

          Exercise

          +

          Our previous solution using clone() was pretty inefficient. How should this code look now?

          +
          fn count_animals(num: u32, animal: String) {
          +    println!("{} {} ...", num, animal);
          +}
          +
          +fn main() {
          +  let s = String::from("sheep");
          +
          +  count_animals(1, s.clone());
          +  count_animals(2, s.clone());
          +  count_animals(3, s); // we could've ommitted the clone() here. Why?
          +}
          +
          +

          Everyone gets a slice

          +

          The last part of working with references that we will cover in this lesson are slices. A slice in Rust is a view over continuous data. Let us start with a string slice - the &str type.

          +

          Note: for the purposes of these examples we assume we are working with ASCII strings. More comprehensive articles on handling strings are linked at the end of this lesson.

          +

          To create a string slice from the String object s, we can simply write:

          +
          let slice = &s[1..3]; // creates a slice of length 2, starting with the character at index 1
          +
          +

          This makes use of the & operator and Rust's range notation to specify the beginning and end of the slice. Thus, we can also write:

          +
          let slice = &s[2..];    // everything from index 2 till the end
          +let slice = &s[..1];    // only the first byte
          +let slice = &s[..];     // the whole string as a slice
          +let slice = s.as_str(); // also the whole string
          +
          +

          You might have noticed that we always built String values using the from() method and never actually used the string literals directly. What type is a string literal then? Turns out it's the new string slice we just learned about!

          +
          let slice: &str = "string literal";
          +
          +

          In fact, it makes a lot sense - string literals, after all, are not allocated on the heap, but rather placed in a special section of the resulting binary. It's only natural we just reference that place with a slice.

          +

          Slices can also be taken from arrays:

          +
          let array: [i32; 4] = [42, 10, 5, 2]; // creates an array of four 32 bit integers
          +let slice: &[i32] = &array[1..3];     // results in a slice [10, 5]
          +
          +

          Exercise

          +

          Can this code still be improved from the previous version utilizing references? Think about the signature of count_animals.

          +
          fn count_animals(num: u32, animal: &String) {
          +    println!("{} {} ...", num, animal);
          +}
          +
          +fn main() {
          +  let s = String::from("sheep");
          +
          +  count_animals(1, &s);
          +  count_animals(2, &s);
          +  count_animals(3, &s);
          +}
          +
          +

          Further reading

          + +

          Assignment 1 (graded)

          +

          ordering in Van Binh

          +

          Deadline: 16.10.2024 23:59

          + + +
          +
          + + + + +
          + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/02-ownership/string_formatting/string_formatting.html b/lessons/02-ownership/string_formatting/string_formatting.html new file mode 100644 index 0000000..d891819 --- /dev/null +++ b/lessons/02-ownership/string_formatting/string_formatting.html @@ -0,0 +1,3842 @@ + + + + + + + + + + + + + + + + +
          + +
          +
          + +
          +

          String formatting

          +

          ...printf(), sprintf() equivalents & related topics.

          +
          +
          +
          +

          + Simplest possible output from Rust program +

          +
          +              fn
          +                main() {
          +                println!("Hello stdout!"); eprintln!("Hello stderr!"); }
          +              
          +            
          +
          +
          +
          +

          + Formatted output (using Display trait) +

          +
          +              fn
          +                agriculture() {
          +                let
          +                num_animals =
          +                42_usize;
          +                let
          +                animal_name =
          +                "ducks";
          +
          +                println!("We have {} {} in our farm.", num_animals, animal_name);
          +
          +                let
          +                s:
          +                String =
          +                format!(
          +                "Nothing is better than {0} {2}, except for {1}
          +                  {2},", num_animals, num_animals +
          +                1, animal_name );
          +
          +                // Minimal assert.
          +                assert!(num_animals >=
          +                42);
          +                // assert with a custom panic message.
          +                assert!( num_animals >=
          +                42,
          +                "Too few animals in our farm :( - only {} {}", num_animals, animal_name ); }
          +              
          +            
          +
          +
          +
          +

          + Formatted output (using Debug trait) +

          +
          +              fn
          +                agriculture() {
          +                let
          +                animals: &[&str] = &["Azor",
          +                "Mućka"];
          +
          +                // Does not compile: &[&str] does not implement
          +                  Display.
          +                // println!("We have the following animals in our farm:
          +                  {}", animals);
          +
          +                // Concise printing for debug purposes:
          +                println!("We have the following animals in our farm:
          +                  {:?}", animals);
          +                // Outputs:
          +                // We have the following animals in our farm:
          +                  ["Azor", "Mućka"]
          +
          +                // Pretty-printing for debug purposes:
          +                println!("We have the following animals in our farm:
          +                  {:#?}", animals);
          +                // Outputs:
          +                // We have the following animals in our farm: [
          +                // "Azor",
          +                // "Mućka"
          +                // ]
          +                }
          +              
          +            
          +

          Various ways to panic

          +
          +              fn
          +                panicking_is_fun() {
          +                panic!("A general panic."); assert!(false,
          +                "An assertion failed.");
          +
          +                unimplemented!("This code is not implemented."); todo!("This code is not YET implemented - it's going to
          +                  change.");
          +
          +                unreachable!("We should never reach this line of code!"); }
          +              
          +            
          +
          +
          +
          +

          + Memory backing considerations +

          +
          +              fn
          +                agriculture() {
          +                let
          +                animals: &[&str] = &["Azor",
          +                "Mućka"];
          +
          +                let
          +                animals: [&str; 2] = ["Azor", "Mućka"];
          +                let
          +                animals: &[&str] = &animals;
          +
          +                let
          +                animals:
          +                Vec<&str> = vec!["Azor", "Mućka"];
          +
          +                static ANIMALS: [&str; 2] = ["Azor", "Mućka"];
          +                static ANIMALS_SLICE:
          +                &[&str] = &ANIMALS;
          +
          +                let
          +                animals:
          +                Vec<&str> = vec!["Azor", "Mućka"];
          +                let
          +                animals_slice:
          +                &[&str] = &animals;
          +
          +                let
          +                animals:
          +                Vec<String> = vec!["Azor".into(),
          +                "Mućka".into()]; }
          +              
          +            
          +
          +
          +
          +

          + Memory backing considerations - with hints +

          +
          +              fn
          +                agriculture() {
          +                let
          +                animals: &[&str] = &["Azor",
          +                "Mućka"];
          +                // stack-allocated stack-backed slice.
          +
          +                // stack-allocated array (of statically-allocated
          +                  strings).
          +                let
          +                animals: [&str; 2] = ["Azor", "Mućka"];
          +                let
          +                animals: &[&str] = &animals;
          +                // stack-allocated stack-backed slice.
          +
          +                let
          +                animals:
          +                Vec<&str> = vec!["Azor", "Mućka"];
          +                // stack-allocated heap-backed slice.
          +
          +                static ANIMALS: [&str; 2] = ["Azor", "Mućka"];
          +                // statically-allocated array.
          +                static ANIMALS_SLICE:
          +                &[&str] = &ANIMALS;
          +                // statically-allocated statically-backed slice.
          +
          +                let
          +                animals:
          +                Vec<&str> = vec!["Azor", "Mućka"];
          +                // stack-allocated heap-backed Vec.
          +                let
          +                animals_slice:
          +                &[&str] = &animals;
          +                // stack-allocated heap-backed slice.
          +
          +                // stack-allocated heap-backed Vec of heap-allocated
          +                  strings.
          +                let
          +                animals:
          +                Vec<String> = vec!["Azor".into(),
          +                "Mućka".into()]; }
          +              
          +            
          +
          +
          +
          +
          +
          +
          + + + diff --git a/lessons/02-ownership/string_formatting/string_formatting.pdf b/lessons/02-ownership/string_formatting/string_formatting.pdf new file mode 100644 index 0000000..23ab234 Binary files /dev/null and b/lessons/02-ownership/string_formatting/string_formatting.pdf differ diff --git a/lessons/02_ownership/dont_panic/dont-panic/index.html b/lessons/02_ownership/dont_panic/dont-panic/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/02_ownership/dont_panic/dont-panic/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
          +

          Welcome to Zola!

          +

          + You're seeing this page because we couldn't find a template to render. +

          +

          + To modify this page, create a page.html file in the templates directory or + install a theme. +
          + You can find what variables are available in this template in the documentation. +

          +
          + + + + diff --git a/lessons/02_ownership/string_formatting/string-formatting/index.html b/lessons/02_ownership/string_formatting/string-formatting/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/02_ownership/string_formatting/string-formatting/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
          +

          Welcome to Zola!

          +

          + You're seeing this page because we couldn't find a template to render. +

          +

          + To modify this page, create a page.html file in the templates directory or + install a theme. +
          + You can find what variables are available in this template in the documentation. +

          +
          + + + + diff --git a/lessons/03-data-types/data_types.rs b/lessons/03-data-types/data_types.rs new file mode 100644 index 0000000..181acea --- /dev/null +++ b/lessons/03-data-types/data_types.rs @@ -0,0 +1,88 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct Position(i32, i32); // tuple struct + +// Could Hero derive the Copy trait? +#[derive(Clone, Debug, Eq, PartialEq)] +struct Hero { + name: String, + level: u32, + experience: u32, + position: Position, +} + +// we can add methods to structs using the 'impl' keyword +impl Hero { + // static method (in Rust nomenclature: "associated function") + fn new(name: String) -> Hero { + Hero { + name, + level: 1, + experience: 0, + position: Position(0, 0), + } + } +} + +// multiple impl blocks are possible for one struct +impl Hero { + // instance method, first argument (self) is the calling instance + fn distance(&self, pos: Position) -> u32 { + // shorthand to: `self: &Self` + // field `i` of a tuple or a tuple struct can be accessed through 'tuple.i' + (pos.0 - self.position.0).unsigned_abs() + (pos.1 - self.position.1).unsigned_abs() + } + + // mutable borrow of self allows to change instance fields + fn level_up(&mut self) { + // shorthand to: `self: &mut Self` + self.experience = 0; + self.level += 1; + } + + // 'self' is not borrowed here and will be moved into the method + fn die(self) { + // shorthand to: `self: Self` + println!( + "Here lies {}, a hero who reached level {}. RIP.", + self.name, self.level + ); + } +} + +fn main() { + // Calling associated functions requires scope (`::`) operator. + let mut hero: Hero = Hero::new(String::from("Ferris")); + hero.level_up(); // 'self' is always passed implicitly + + // fields other than 'name' will be the same as in 'hero' + let steve = Hero { + name: String::from("Steve The Normal Guy"), + ..hero + }; + + assert_eq!(hero.level, steve.level); + + let mut twin = hero.clone(); + + // we can compare Hero objects because it derives the PartialEq trait + assert_eq!(hero, twin); + twin.level_up(); + assert_ne!(hero, twin); + hero.level_up(); + assert_eq!(hero, twin); + + // we can print out a the struct's debug string with '{:?}' + println!("print to stdout: {:?}", hero); + + hero.die(); // 'hero' is not usable after this invocation, see the method's definiton + + // the dbg! macro prints debug strings to stderr along with file and line number + // dbg! takes its arguments by value, so better borrow them not to have them + // moved into dbg! and consumed. + dbg!("print to stderr: {}", &twin); + + let pos = Position(42, 0); + let dist = steve.distance(pos); // no clone here as Position derives the Copy trait + println!("{:?}", pos); + assert_eq!(dist, 42); +} diff --git a/lessons/03-data-types/enums.c b/lessons/03-data-types/enums.c new file mode 100644 index 0000000..0b20f64 --- /dev/null +++ b/lessons/03-data-types/enums.c @@ -0,0 +1,35 @@ +#include + +enum shirt_size { + small, + medium, + large, + xlarge +}; + +void print_size(enum shirt_size size) { + printf("my size is "); + switch (size) { + case small: + printf("small"); + break; + case medium: + printf("medium"); + break; + case large: + printf("large"); + break; + case xlarge: + printf("xlarge"); + break; + default: + printf("unknown"); + break; + } + printf("\n"); +} + +int main() { + enum shirt_size my_size = medium; + print_size(my_size); +} diff --git a/lessons/03-data-types/enums.rs b/lessons/03-data-types/enums.rs new file mode 100644 index 0000000..ceab783 --- /dev/null +++ b/lessons/03-data-types/enums.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +#[derive(Debug)] +enum NamedSize { + Small, + Medium, + Large, + XL, +} + +#[derive(Debug)] +enum ShirtSize { + Named(NamedSize), + Numeric(u32), +} + +fn main() { + println!( + "Isn't it strange that some clothes' sizes are adjectives like {:?},", + ShirtSize::Named(NamedSize::Small) + ); + println!( + "but sometimes they are numbers like {:?}?", + ShirtSize::Numeric(42) + ); +} diff --git a/lessons/03-data-types/index.html b/lessons/03-data-types/index.html new file mode 100644 index 0000000..caf3c23 --- /dev/null +++ b/lessons/03-data-types/index.html @@ -0,0 +1,797 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
          + + +
          + +
          + +
          +
          +
            +
            +
            + +
            + +

            Data Types

            +

            + 2024-10-17 (last edit: 2024-10-17) +

            +

            Aggregating data

            +

            Below is a compact overview of Rust's structs

            +
            #[derive(Clone, Copy, Debug, Eq, PartialEq)]
            +struct Position(i32, i32); // tuple struct
            +
            +// Could Hero derive the Copy trait?
            +#[derive(Clone, Debug, Eq, PartialEq)]
            +struct Hero {
            +    name: String,
            +    level: u32,
            +    experience: u32,
            +    position: Position,
            +}
            +
            +// we can add methods to structs using the 'impl' keyword
            +impl Hero {
            +    // static method (in Rust nomenclature: "associated function")
            +    fn new(name: String) -> Hero {
            +        Hero {
            +            name,
            +            level: 1,
            +            experience: 0,
            +            position: Position(0, 0),
            +        }
            +    }
            +}
            +
            +// multiple impl blocks are possible for one struct
            +impl Hero {
            +    // instance method, first argument (self) is the calling instance
            +    fn distance(&self, pos: Position) -> u32 {
            +        // shorthand to: `self: &Self`
            +        // field `i` of a tuple or a tuple struct can be accessed through 'tuple.i'
            +        (pos.0 - self.position.0).unsigned_abs() + (pos.1 - self.position.1).unsigned_abs()
            +    }
            +
            +    // mutable borrow of self allows to change instance fields
            +    fn level_up(&mut self) {
            +        // shorthand to: `self: &mut Self`
            +        self.experience = 0;
            +        self.level += 1;
            +    }
            +
            +    // 'self' is not borrowed here and will be moved into the method
            +    fn die(self) {
            +        // shorthand to: `self: Self`
            +        println!(
            +            "Here lies {}, a hero who reached level {}. RIP.",
            +            self.name, self.level
            +        );
            +    }
            +}
            +
            +fn main() {
            +    // Calling associated functions requires scope (`::`) operator.
            +    let mut hero: Hero = Hero::new(String::from("Ferris"));
            +    hero.level_up(); // 'self' is always passed implicitly
            +
            +    // fields other than 'name' will be the same as in 'hero'
            +    let steve = Hero {
            +        name: String::from("Steve The Normal Guy"),
            +        ..hero
            +    };
            +
            +    assert_eq!(hero.level, steve.level);
            +
            +    let mut twin = hero.clone();
            +
            +    // we can compare Hero objects because it derives the PartialEq trait
            +    assert_eq!(hero, twin);
            +    twin.level_up();
            +    assert_ne!(hero, twin);
            +    hero.level_up();
            +    assert_eq!(hero, twin);
            +
            +    // we can print out a the struct's debug string with '{:?}'
            +    println!("print to stdout: {:?}", hero);
            +
            +    hero.die(); // 'hero' is not usable after this invocation, see the method's definiton
            +
            +    // the dbg! macro prints debug strings to stderr along with file and line number
            +    // dbg! takes its arguments by value, so better borrow them not to have them
            +    // moved into dbg! and consumed.
            +    dbg!("print to stderr: {}", &twin);
            +
            +    let pos = Position(42, 0);
            +    let dist = steve.distance(pos); // no clone here as Position derives the Copy trait
            +    println!("{:?}", pos);
            +    assert_eq!(dist, 42);
            +}
            +
            +
            +

            (Download the source code for this example: data_types.rs)

            +

            Enums

            +

            It is often the case that we want to define a variable that can only take +a certain set of values and the values are known up front. In C you can use an enum for this.

            +
            #include <stdio.h>
            +
            +enum shirt_size {
            +    small,
            +    medium,
            +    large,
            +    xlarge
            +};
            +
            +void print_size(enum shirt_size size) {
            +    printf("my size is ");
            +    switch (size) {
            +        case small:
            +            printf("small");
            +            break;
            +        case medium:
            +            printf("medium");
            +            break;
            +        case large:
            +            printf("large");
            +            break;
            +        case xlarge:
            +            printf("xlarge");
            +            break;
            +        default:
            +            printf("unknown");
            +            break;
            +    }
            +    printf("\n");
            +}
            +
            +int main() {
            +    enum shirt_size my_size = medium;
            +    print_size(my_size);
            +}
            +
            +
            +

            (Download the source code for this example: enums.c)

            +

            However, in C enums are just integers. Nothing prevents us from writing

            +
            int main() {
            +    enum shirt_size my_size = 666;
            +    print_size(my_size);
            +}
            +
            +

            C++ introduces enum classes which are type-safe. Legacy enums are also somewhat safer than in C (same code as above):

            +
            <source>:27:31: error: invalid conversion from 'int' to 'shirt_size' [-fpermissive]
            +   27 |     enum shirt_size my_size = 666;
            +      |                               ^~~
            +      |                               |
            +      |                               int
            +
            +

            Some programming languages (especially functional ones) allow programmers to define +enums which carry additional information. Such types are usually called tagged unions +or algebraic data types.

            +

            In C++ we can use union with an enum tag to define it:

            +
            #include <iostream>
            +
            +// Taken from: https://en.cppreference.com/w/cpp/language/union
            +
            +// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE),
            +// and three variant members (c, i, d)
            +struct S
            +{
            +    enum{CHAR, INT, DOUBLE} tag;
            +    union
            +    {
            +        char c;
            +        int i;
            +        double d;
            +    };
            +};
            +
            +void print_s(const S& s)
            +{
            +    switch(s.tag)
            +    {
            +        case S::CHAR: std::cout << s.c << '\n'; break;
            +        case S::INT: std::cout << s.i << '\n'; break;
            +        case S::DOUBLE: std::cout << s.d << '\n'; break;
            +    }
            +}
            +
            +int main()
            +{
            +    S s = {S::CHAR, 'a'};
            +    print_s(s);
            +    s.tag = S::INT;
            +    s.i = 123;
            +    print_s(s);
            +}
            +
            +
            +

            (Download the source code for this example: tagged_union.cpp)

            +

            C++17 introduced a new feature called variant which generalizes this concept. +You can read more about it here.

            +

            Java has a more or less analogous feature called sealed classes +since version 17.

            +

            Enums in Rust

            +

            Let's see how they are defined in Rust.

            +
            #![allow(unused_assignments)]
            +#![allow(unused_variables)]
            +#![allow(dead_code)]
            +
            +#[derive(Debug)]
            +enum NamedSize {
            +    Small,
            +    Medium,
            +    Large,
            +    XL,
            +}
            +
            +#[derive(Debug)]
            +enum ShirtSize {
            +    Named(NamedSize),
            +    Numeric(u32),
            +}
            +
            +fn main() {
            +    println!(
            +        "Isn't it strange that some clothes' sizes are adjectives like {:?},",
            +        ShirtSize::Named(NamedSize::Small)
            +    );
            +    println!(
            +        "but sometimes they are numbers like {:?}?",
            +        ShirtSize::Numeric(42)
            +    );
            +}
            +
            +
            +

            (Download the source code for this example: enums.rs)

            +

            In Rust, enums are a core feature of the language. +You may have heard that one of Rust's defining characteristics is +the absence of "the billion dollar mistake". +So what can we do to say that a value is missing if there is no null?

            +

            In Rust, we can use the Option type to represent the absence of a value.

            +

            Option is defined as:

            +
            enum Option<T> {
            +    Some(T),
            +    None,
            +}
            +
            +

            The <T> part is called the "type parameter" and it causes Option to be generic. +We won't go deeper into this for now.

            +

            The fact that variables which could be null in other languages have a different type in Rust is +the solution to the billion dollar mistake!

            +
            #![allow(unused_assignments)]
            +#![allow(unused_variables)]
            +#![allow(dead_code)]
            +
            +fn main() {
            +    let mut not_null: i32 = 42;
            +    not_null = 43;
            +    // not_null = None; // this won't compile because it's a different type!
            +
            +    let mut nullable: Option<i32> = Some(42);
            +    nullable = None;
            +    nullable = Some(43);
            +
            +    // such construction is rare, but possible
            +    let mut double_nullable: Option<Option<i32>> = Some(Some(42));
            +    // assert_ne!(double_nullable, Some(42)); // this won't even compile because it's a different type!
            +    double_nullable = None;
            +    double_nullable = Some(None);
            +
            +    // None and Some(None) are different!
            +    assert_ne!(double_nullable, None);
            +
            +    // Now recall that division by 0 *panics*
            +    // A panic is an unrecoverable error
            +    // It is not an exception!
            +    // And in Rust there are no exceptions, so there are no try/catch blocks
            +    // Now let's imagine that we want to divide one number by another
            +    fn divide(dividend: i32, divisor: i32) -> i32 {
            +        dividend / divisor
            +    }
            +
            +    // We get the divisor from the user, so it can be 0
            +    // We want to handle this situation gracefully - we don't want to crash the program!
            +    // We can do this by using the Option<T> type
            +    fn safe_divide(dividend: i32, divisor: i32) -> Option<i32> {
            +        if divisor == 0 {
            +            None
            +        } else {
            +            Some(dividend / divisor)
            +        }
            +    }
            +
            +    // Fortunately, such a function is already included in the standard library
            +    let number: i32 = 42;
            +    // We need to specify the type explicitly
            +    // because checked_div is implemented for all integer types
            +    // and Rust won't know which type we want to use
            +    assert_eq!(number.checked_div(2), Some(21));
            +    assert_eq!(number.checked_div(0), None);
            +
            +    // Now let's imagine we search for a value in an array.
            +    let numbers = [1, 2, 3, 4, 5];
            +    let three = numbers.iter().copied().find(|&x| x == 3);
            +    assert_eq!(three, Some(3));
            +    let seven = numbers.iter().copied().find(|&x| x == 7);
            +    assert_eq!(seven, None);
            +    // We won't delve deeper into the details of how iterators work for now,
            +    // but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust
            +
            +    // Usually there are two kinds of methods:
            +    // ones that will panic if the argument is incorrect,
            +    // numbers[8]; // this will panic!
            +    // and `checked` ones that return an Option
            +    assert_eq!(numbers.get(8), None);
            +
            +    // We can use `unwrap` to get the value out of an Option
            +    // but we must be absolutely sure that the Option is Some, otherwise we'll get a panic
            +    // numbers.get(8).unwrap(); // this will panic!
            +    assert_eq!(numbers.get(8).copied().unwrap_or(0), 0); // or we can provide a default value
            +
            +    // Usually instead of unwrapping we use pattern matching, we'll get to this in a minute
            +    // but first let's see what else we can do with an option
            +    let number: Option<i32> = Some(42);
            +    // We can use `map` to transform the value inside an Option
            +    let doubled = number.map(|x| x * 2);
            +    assert_eq!(doubled, Some(84));
            +    // We can use flatten to reduce one level of nesting
            +    let nested = Some(Some(42));
            +    assert_eq!(nested.flatten(), Some(42));
            +    // We can use `and_then` to chain multiple options
            +    // This operation is called `flatmap` in some languages
            +    let chained = number
            +        .and_then(|x| x.checked_div(0))
            +        .and_then(|x| x.checked_div(2));
            +    assert_eq!(chained, None);
            +
            +    // The last two things we'll cover here are `take` and `replace`
            +    // They are important when dealing with non-Copy types
            +    // `take` will return the value inside an Option and leave a None in its place
            +    let mut option: Option<i32> = None;
            +    // Again, we need to specify the type
            +    // Even though we want to say that there is no value inside the Option,
            +    // this absent value must have a concrete type!
            +    assert_eq!(option.take(), None);
            +    assert_eq!(option, None);
            +
            +    let mut x = Some(2);
            +    let y = x.take();
            +    assert_eq!(x, None);
            +    assert_eq!(y, Some(2));
            +
            +    // `replace` can be used to swap the value inside an Option
            +    let mut x = Some(2);
            +    let old = x.replace(5);
            +    assert_eq!(x, Some(5));
            +    assert_eq!(old, Some(2));
            +
            +    let mut x = None;
            +    let old = x.replace(3);
            +    assert_eq!(x, Some(3));
            +    assert_eq!(old, None);
            +}
            +
            +
            +

            (Download the source code for this example: option.rs)

            +

            Pattern matching

            +

            Pattern matching is a powerful feature of Rust and many functional languages, but it's slowly making +its way into imperative languages like Java and Python as well.

            +
            #![allow(dead_code)]
            +#![allow(unused_variables)]
            +
            +fn main() {
            +    // Pattern matching is basically a switch on steroids.
            +    let number = rand::random::<i32>();
            +    match number % 7 {
            +        0 => println!("{number} is divisible by 7"),
            +        1 => println!("{number} is *almost* divisible by 7"),
            +        _ => println!("{number} is not divisible by 7"),
            +    }
            +
            +    #[derive(Debug)]
            +    enum Color {
            +        Pink,
            +        Brown,
            +        Lime,
            +    }
            +
            +    let color = Color::Lime;
            +    match color {
            +        Color::Pink => println!("My favorite color!"),
            +        _ => println!("Not my favorite color!"), // _ is a wildcard
            +                                                 // Rust will statically check that we covered all cases or included a default case.
            +    }
            +
            +    // We can also use pattern matching to match on multiple values.
            +    match (color, number % 7) {
            +        (Color::Pink, 0) => println!("My favorite color and number!"),
            +        (Color::Pink, _) => println!("My favorite color!"),
            +        (_, 0) => println!("My favorite number!"),
            +        (_, _) => println!("Not my favorite color or number!"),
            +    }
            +    // (This is not special syntax, we're just pattern matching tuples.)
            +
            +    // But we can also *destructure* the value
            +    struct Human {
            +        age: u8,
            +        favorite_color: Color,
            +    }
            +
            +    let john = Human {
            +        age: 42,
            +        favorite_color: Color::Pink,
            +    };
            +
            +    match &john {
            +        Human {
            +            age: 42,
            +            favorite_color: Color::Pink,
            +        } => println!("Okay, that's John!"),
            +        Human {
            +            favorite_color: Color::Pink,
            +            ..
            +        } => println!("Not John, but still his favorite color!"),
            +        _ => println!("Somebody else?"),
            +    }
            +
            +    // Note two things:
            +    // 1. Color is *not* Eq, so we can't use == to compare it, but pattern matching is fine.
            +    // 2. We *borrowed* the value, so we can use it after the match.
            +
            +    println!("John is {} years old and still kicking!", john.age);
            +
            +    // To save some time, we can use `if let` to match against only one thing
            +    // We could also use `while let ... {}` in the same way
            +    if let Color::Pink = &john.favorite_color {
            +        println!("He's also a man of great taste");
            +    }
            +
            +    // We can match ranges...
            +    match john.age {
            +        0..=12 => println!("John is a kid!"),
            +        13..=19 => println!("John is a teenager!"),
            +        20..=29 => println!("John is a young adult!"),
            +        30..=49 => println!("John is an adult!"),
            +        50..=69 => println!("John is mature!"),
            +        _ => println!("John is old!"),
            +    }
            +
            +    // We can use match and capture the value at the same time.
            +    match john.age {
            +        age @ 0..=12 => println!("John is a kid, age {}", age),
            +        age @ 13..=19 => println!("John is a teenager, age {}", age),
            +        age @ 20..=29 => println!("John is a young adult, age {}", age),
            +        age @ 30..=49 => println!("John is an adult, age {}", age),
            +        age @ 50..=69 => println!("John is mature, age {}", age),
            +        age => println!("John is old, age {}", age),
            +    }
            +
            +    // We can use guards to check for multiple conditions.
            +    match john.age {
            +        age @ 12..=19 if age % 2 == 1 => println!("John is an *odd* teenager, age {}", age),
            +        age if age % 2 == 0 => println!("John is an *even* man, age {}", age),
            +        _ => println!("John is normal"),
            +    }
            +
            +    // Finally, let's look at some references now
            +    let reference: &i32 = &4;
            +
            +    match reference {
            +        &val => println!("Value under reference is: {}", val),
            +    }
            +
            +    // `ref` can be used to create a reference when destructuring
            +    let Human {
            +        age,
            +        ref favorite_color,
            +    } = john;
            +    // `john` is still valid, because we borrowed using `ref`
            +    if let Color::Pink = &john.favorite_color {
            +        println!("John still has his color - {:?}!", favorite_color);
            +    }
            +
            +    let mut john = john;
            +
            +    // `ref mut` borrows mutably
            +    let Human {
            +        age,
            +        ref mut favorite_color,
            +    } = john;
            +    // We use `*` to dereference
            +    *favorite_color = Color::Brown;
            +    println!(
            +        "Tastes do change with time and John likes {:?} now.",
            +        john.favorite_color
            +    );
            +}
            +
            +
            +

            (Download the source code for this example: pattern_matching.rs)

            +

            Result

            +

            We said there are no exceptions in Rust and panics mean errors which cannot be caught. +So how do we handle situations which can fail? That's where the Result type comes in.

            +
            #![allow(dead_code)]
            +#![allow(unused_variables)]
            +
            +use std::fs::File;
            +use std::io;
            +use std::io::Read;
            +
            +// Let's try reading from a file.
            +// Obviously this can fail.
            +fn first_try() -> io::Result<String> {
            +    let file = File::open("/dev/random");
            +    match file {
            +        Ok(mut file) => {
            +            // We got a file!
            +            let mut buffer = vec![0; 128];
            +            // Matching each result quickly become tedious...
            +            match file.read_exact(&mut buffer) {
            +                Ok(_) => {
            +                    let gibberish = String::from_utf8_lossy(&buffer);
            +                    Ok(gibberish.to_string())
            +                }
            +                Err(error) => Err(error),
            +            }
            +        }
            +        Err(error) => {
            +            Err(error) // This is needed in order to change the type from `io::Result<File>` to `io::Result<()>`
            +        }
            +    }
            +}
            +
            +// The '?' operator allows us to return early in case of an error
            +// (it automatically converts the error type)
            +fn second_try(filename: &'static str) -> io::Result<String> {
            +    let mut file = File::open(filename)?;
            +    let mut buffer = vec![0; 128];
            +    file.read_exact(&mut buffer)?;
            +    let gibberish = String::from_utf8_lossy(&buffer);
            +    Ok(gibberish.to_string())
            +}
            +
            +fn main() {
            +    let filenames = [
            +        "/dev/random",
            +        "/dev/null",
            +        "/dev/cpu",
            +        "/dev/fuse",
            +        "there_certainly_is_no_such_file",
            +    ];
            +    for filename in filenames {
            +        println!("Trying to read from '{}'", filename);
            +        match second_try(filename) {
            +            Ok(gibberish) => println!("{}", gibberish),
            +            Err(error) => println!("Error: {}", error),
            +        }
            +    }
            +}
            +
            +
            +

            (Download the source code for this example: result.rs)

            +

            Obligatory reading

            + +

            Assignment 2 (graded)

            +

            Communications

            +

            Deadline: 23.10.2024 23:59

            + + +
            +
            + + + + +
            + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/03-data-types/module_system/module_system.html b/lessons/03-data-types/module_system/module_system.html new file mode 100644 index 0000000..104212c --- /dev/null +++ b/lessons/03-data-types/module_system/module_system.html @@ -0,0 +1,253 @@ +
            +

            Module system

            +

            Managing code structure in a growing project.
            +Testing and sharing code conveniently.

            +
            +
            +

            Module system consists of:

            +
              +
            • Packages: A Cargo feature that lets you build, test, and share crates
            • +
            • Crates: A tree of modules that produces a library or executable
            • +
            • Modules and use: Let you control the organization, scope, and privacy of paths
            • +
            • Paths: A way of naming an item, such as a struct, function, or module
            • +
            +
            +
            +

            Package structure

            +
            my-project
            +├── Cargo.lock             <-- actual dependencies' versions
            +├── Cargo.toml             <-- package configuration, dependency version requirements
            +└── src
            +    ├── configuration
            +    │   ├── run.rs
            +    │   └── mod.rs
            +    ├── lib.rs             <-- root of the lib crate
            +    ├── bin1
            +    │   ├── distribution.rs
            +    │   └── main.rs        <-- root of bin crate `bin1`
            +    └── bin2.rs            <-- root of bin crate `bin2`
            +
            +
            +
            +

            Lib crates can be shared

            +
              +
            • crates.io is the main crate repository.
            • +
            • If you specify a dependency in Cargo.toml, it's fetched from crates.io automatically by Cargo.
            • +
            • lib.rs is the root of a lib crate.
            • +
            +
            +
            +

            Binary crates can be executed

            +
              +
            • cargo run executes the bin crate in your package.
            • +
            • If you have multiple bin crates, you have to specify which to run:
              +cargo run --bin <bin_name>
            • +
            • Each bin crate in a package can import code from the lib crate there.
            • +
            +
            +
            + +
            mod front_of_house {
            +    mod hosting {
            +        fn add_to_waitlist() {}
            +        fn seat_at_table() {}
            +    }
            +
            +    // Alternatively, this could be located in `serving.rs` file and imported.
            +    mod serving {
            +        fn take_order() {}
            +        fn serve_order() {}
            +        fn take_payment() {}
            +    }
            +}
            +
            +
            +
            + +
            crate
            + └── front_of_house
            +     ├── hosting
            +     │   ├── add_to_waitlist
            +     │   └── seat_at_table
            +     └── serving
            +         ├── take_order
            +         ├── serve_order
            +         └── take_payment
            +
            +
            +
            +

            Exports & imports

            +
              +
            • exports: using privacy modifier (pub, pub(crate), [no modifier])
            • +
            +
            mod some_mod {
            +    struct ModulePublic;
            +    pub(super) struct ParentModulePublic;
            +    pub(crate) struct CratePublic;
            +    pub struct WorldPublic;
            +}
            +
            +
              +
            • imports: using use statement
            • +
            +
            use some_mod::CratePublic;
            +pub use some_mod::WorldPublic; // <-- re-export
            +
            +
            +
            \ No newline at end of file diff --git a/lessons/03-data-types/module_system/module_system.pdf b/lessons/03-data-types/module_system/module_system.pdf new file mode 100644 index 0000000..e58da88 Binary files /dev/null and b/lessons/03-data-types/module_system/module_system.pdf differ diff --git a/lessons/03-data-types/option.rs b/lessons/03-data-types/option.rs new file mode 100644 index 0000000..82faef9 --- /dev/null +++ b/lessons/03-data-types/option.rs @@ -0,0 +1,112 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +fn main() { + let mut not_null: i32 = 42; + not_null = 43; + // not_null = None; // this won't compile because it's a different type! + + let mut nullable: Option = Some(42); + nullable = None; + nullable = Some(43); + + // such construction is rare, but possible + let mut double_nullable: Option> = Some(Some(42)); + // assert_ne!(double_nullable, Some(42)); // this won't even compile because it's a different type! + double_nullable = None; + double_nullable = Some(None); + + // None and Some(None) are different! + assert_ne!(double_nullable, None); + + // Now recall that division by 0 *panics* + // A panic is an unrecoverable error + // It is not an exception! + // And in Rust there are no exceptions, so there are no try/catch blocks + // Now let's imagine that we want to divide one number by another + fn divide(dividend: i32, divisor: i32) -> i32 { + dividend / divisor + } + + // We get the divisor from the user, so it can be 0 + // We want to handle this situation gracefully - we don't want to crash the program! + // We can do this by using the Option type + fn safe_divide(dividend: i32, divisor: i32) -> Option { + if divisor == 0 { + None + } else { + Some(dividend / divisor) + } + } + + // Fortunately, such a function is already included in the standard library + let number: i32 = 42; + // We need to specify the type explicitly + // because checked_div is implemented for all integer types + // and Rust won't know which type we want to use + assert_eq!(number.checked_div(2), Some(21)); + assert_eq!(number.checked_div(0), None); + + // Now let's imagine we search for a value in an array. + let numbers = [1, 2, 3, 4, 5]; + let three = numbers.iter().copied().find(|&x| x == 3); + assert_eq!(three, Some(3)); + let seven = numbers.iter().copied().find(|&x| x == 7); + assert_eq!(seven, None); + // We won't delve deeper into the details of how iterators work for now, + // but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust + + // Usually there are two kinds of methods: + // ones that will panic if the argument is incorrect, + // numbers[8]; // this will panic! + // and `checked` ones that return an Option + assert_eq!(numbers.get(8), None); + + // We can use `unwrap` to get the value out of an Option + // but we must be absolutely sure that the Option is Some, otherwise we'll get a panic + // numbers.get(8).unwrap(); // this will panic! + assert_eq!(numbers.get(8).copied().unwrap_or(0), 0); // or we can provide a default value + + // Usually instead of unwrapping we use pattern matching, we'll get to this in a minute + // but first let's see what else we can do with an option + let number: Option = Some(42); + // We can use `map` to transform the value inside an Option + let doubled = number.map(|x| x * 2); + assert_eq!(doubled, Some(84)); + // We can use flatten to reduce one level of nesting + let nested = Some(Some(42)); + assert_eq!(nested.flatten(), Some(42)); + // We can use `and_then` to chain multiple options + // This operation is called `flatmap` in some languages + let chained = number + .and_then(|x| x.checked_div(0)) + .and_then(|x| x.checked_div(2)); + assert_eq!(chained, None); + + // The last two things we'll cover here are `take` and `replace` + // They are important when dealing with non-Copy types + // `take` will return the value inside an Option and leave a None in its place + let mut option: Option = None; + // Again, we need to specify the type + // Even though we want to say that there is no value inside the Option, + // this absent value must have a concrete type! + assert_eq!(option.take(), None); + assert_eq!(option, None); + + let mut x = Some(2); + let y = x.take(); + assert_eq!(x, None); + assert_eq!(y, Some(2)); + + // `replace` can be used to swap the value inside an Option + let mut x = Some(2); + let old = x.replace(5); + assert_eq!(x, Some(5)); + assert_eq!(old, Some(2)); + + let mut x = None; + let old = x.replace(3); + assert_eq!(x, Some(3)); + assert_eq!(old, None); +} diff --git a/lessons/03-data-types/pattern_matching.rs b/lessons/03-data-types/pattern_matching.rs new file mode 100644 index 0000000..215a74a --- /dev/null +++ b/lessons/03-data-types/pattern_matching.rs @@ -0,0 +1,128 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +fn main() { + // Pattern matching is basically a switch on steroids. + let number = rand::random::(); + match number % 7 { + 0 => println!("{number} is divisible by 7"), + 1 => println!("{number} is *almost* divisible by 7"), + _ => println!("{number} is not divisible by 7"), + } + + #[derive(Debug)] + enum Color { + Pink, + Brown, + Lime, + } + + let color = Color::Lime; + match color { + Color::Pink => println!("My favorite color!"), + _ => println!("Not my favorite color!"), // _ is a wildcard + // Rust will statically check that we covered all cases or included a default case. + } + + // We can also use pattern matching to match on multiple values. + match (color, number % 7) { + (Color::Pink, 0) => println!("My favorite color and number!"), + (Color::Pink, _) => println!("My favorite color!"), + (_, 0) => println!("My favorite number!"), + (_, _) => println!("Not my favorite color or number!"), + } + // (This is not special syntax, we're just pattern matching tuples.) + + // But we can also *destructure* the value + struct Human { + age: u8, + favorite_color: Color, + } + + let john = Human { + age: 42, + favorite_color: Color::Pink, + }; + + match &john { + Human { + age: 42, + favorite_color: Color::Pink, + } => println!("Okay, that's John!"), + Human { + favorite_color: Color::Pink, + .. + } => println!("Not John, but still his favorite color!"), + _ => println!("Somebody else?"), + } + + // Note two things: + // 1. Color is *not* Eq, so we can't use == to compare it, but pattern matching is fine. + // 2. We *borrowed* the value, so we can use it after the match. + + println!("John is {} years old and still kicking!", john.age); + + // To save some time, we can use `if let` to match against only one thing + // We could also use `while let ... {}` in the same way + if let Color::Pink = &john.favorite_color { + println!("He's also a man of great taste"); + } + + // We can match ranges... + match john.age { + 0..=12 => println!("John is a kid!"), + 13..=19 => println!("John is a teenager!"), + 20..=29 => println!("John is a young adult!"), + 30..=49 => println!("John is an adult!"), + 50..=69 => println!("John is mature!"), + _ => println!("John is old!"), + } + + // We can use match and capture the value at the same time. + match john.age { + age @ 0..=12 => println!("John is a kid, age {}", age), + age @ 13..=19 => println!("John is a teenager, age {}", age), + age @ 20..=29 => println!("John is a young adult, age {}", age), + age @ 30..=49 => println!("John is an adult, age {}", age), + age @ 50..=69 => println!("John is mature, age {}", age), + age => println!("John is old, age {}", age), + } + + // We can use guards to check for multiple conditions. + match john.age { + age @ 12..=19 if age % 2 == 1 => println!("John is an *odd* teenager, age {}", age), + age if age % 2 == 0 => println!("John is an *even* man, age {}", age), + _ => println!("John is normal"), + } + + // Finally, let's look at some references now + let reference: &i32 = &4; + + match reference { + &val => println!("Value under reference is: {}", val), + } + + // `ref` can be used to create a reference when destructuring + let Human { + age, + ref favorite_color, + } = john; + // `john` is still valid, because we borrowed using `ref` + if let Color::Pink = &john.favorite_color { + println!("John still has his color - {:?}!", favorite_color); + } + + let mut john = john; + + // `ref mut` borrows mutably + let Human { + age, + ref mut favorite_color, + } = john; + // We use `*` to dereference + *favorite_color = Color::Brown; + println!( + "Tastes do change with time and John likes {:?} now.", + john.favorite_color + ); +} diff --git a/lessons/03-data-types/result.rs b/lessons/03-data-types/result.rs new file mode 100644 index 0000000..ffd0797 --- /dev/null +++ b/lessons/03-data-types/result.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::fs::File; +use std::io; +use std::io::Read; + +// Let's try reading from a file. +// Obviously this can fail. +fn first_try() -> io::Result { + let file = File::open("/dev/random"); + match file { + Ok(mut file) => { + // We got a file! + let mut buffer = vec![0; 128]; + // Matching each result quickly become tedious... + match file.read_exact(&mut buffer) { + Ok(_) => { + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) + } + Err(error) => Err(error), + } + } + Err(error) => { + Err(error) // This is needed in order to change the type from `io::Result` to `io::Result<()>` + } + } +} + +// The '?' operator allows us to return early in case of an error +// (it automatically converts the error type) +fn second_try(filename: &'static str) -> io::Result { + let mut file = File::open(filename)?; + let mut buffer = vec![0; 128]; + file.read_exact(&mut buffer)?; + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) +} + +fn main() { + let filenames = [ + "/dev/random", + "/dev/null", + "/dev/cpu", + "/dev/fuse", + "there_certainly_is_no_such_file", + ]; + for filename in filenames { + println!("Trying to read from '{}'", filename); + match second_try(filename) { + Ok(gibberish) => println!("{}", gibberish), + Err(error) => println!("Error: {}", error), + } + } +} diff --git a/lessons/03-data-types/tagged_union.cpp b/lessons/03-data-types/tagged_union.cpp new file mode 100644 index 0000000..ae07b6e --- /dev/null +++ b/lessons/03-data-types/tagged_union.cpp @@ -0,0 +1,35 @@ +#include + +// Taken from: https://en.cppreference.com/w/cpp/language/union + +// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +// and three variant members (c, i, d) +struct S +{ + enum{CHAR, INT, DOUBLE} tag; + union + { + char c; + int i; + double d; + }; +}; + +void print_s(const S& s) +{ + switch(s.tag) + { + case S::CHAR: std::cout << s.c << '\n'; break; + case S::INT: std::cout << s.i << '\n'; break; + case S::DOUBLE: std::cout << s.d << '\n'; break; + } +} + +int main() +{ + S s = {S::CHAR, 'a'}; + print_s(s); + s.tag = S::INT; + s.i = 123; + print_s(s); +} diff --git a/lessons/03_data_types/module_system/module-system/index.html b/lessons/03_data_types/module_system/module-system/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/03_data_types/module_system/module-system/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
            +

            Welcome to Zola!

            +

            + You're seeing this page because we couldn't find a template to render. +

            +

            + To modify this page, create a page.html file in the templates directory or + install a theme. +
            + You can find what variables are available in this template in the documentation. +

            +
            + + + + diff --git a/lessons/04-feedback-1/index.html b/lessons/04-feedback-1/index.html new file mode 100644 index 0000000..4f7b607 --- /dev/null +++ b/lessons/04-feedback-1/index.html @@ -0,0 +1,301 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
            + + +
            + +
            + +
            +
            +
              +
              +
              + +
              + +

              Feedback #1

              +

              + 2022-10-31 (last edit: 2022-10-31) +

              +

              Feedback

              +

              Unwrapping

              +

              Instead of this:

              +
              if self.favorite_color.is_some() {
              +    self.favorite_color.as_mut().unwrap().lighten();
              +}
              +
              +

              do this:

              +
              if let Some(ref mut color) = self.favorite_color {
              +    color.lighten();
              +}
              +
              +

              or

              +
              if let Some(color) = &mut self.favorite_color {
              +    color.lighten();
              +}
              +
              +

              (unwrapping is a code smell)

              +

              Spot the overflow

              +
              Color::Rgb(r, g, b) => *b > (*r + *g) / 2,
              +
              +

              1/3

              +
              Color::Rgb(r, g, b) => (*b as u16) * 3 > (*r as u16) + (*g as u16) + (*b as u16),
              +
              +

              No need to cast to u16. If b accounts for 1/3 of the sum, it's enough to check that it's bigger than both r and g.

              +

              Format

              +
              Color::Named(ref mut name) => *name = "light ".to_string() + name,
              +
              +

              There's a format! macro for this.

              +
              Color::Named(ref mut name) => *name = format!("light {name}"),
              +
              +

              From vs Into vs as

              +
              let tmp1: u32 = <u8 as Into<u32>>::into(*c) * 2;
              +
              +

              This could be written as

              +
              let tmp1: u32 = (*c).into() * 2;
              +
              +

              or even simpler (note the omission of the type annotation):

              +
              let tmp1 = u32::from(*c) * 2;
              +
              +

              However in most cases of numeric conversion you can just use as:

              +
              let tmp1 = *c as u32 * 2;
              +
              +

              Into trait docs

              +

              Saturating addition

              +

              There's a saturating_add method on u8 which does exactly what we wanted. +But it was fun watching you struggle with it :)

              +
              fn lighten(&mut self) {
              +    match self {
              +        Color::Named(name) => *name = "light ".to_string() + name,
              +        Color::Rgb(r, g, b) => {
              +            *r = r.saturating_add(10);
              +            *g = g.saturating_add(10);
              +            *b = b.saturating_add(10);
              +        }
              +    }
              +}
              +
              +

              Exchange

              +
              fn exchange_items(robot1: &mut Robot, robot2: &mut Robot) {
              +    mem::swap(&mut robot1.held_item, &mut robot2.held_item);
              +}
              +
              +

              Swap is the preferred way to exchange the contents of two variables.

              +

              Regex? Nope

              +

              There's no need to use a regex here. String has a contains method.

              +

              If you really want to use a regex, +you can use the lazy_static crate to avoid recompiling the regex every time you call the function.

              + + +
              +
              + + + + +
              + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/05-types-reasoning/basic_trait.rs b/lessons/05-types-reasoning/basic_trait.rs new file mode 100644 index 0000000..1c76202 --- /dev/null +++ b/lessons/05-types-reasoning/basic_trait.rs @@ -0,0 +1,38 @@ +#![allow(dead_code)] + +trait Summary { + fn summarize(&self) -> String; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Summary for NewsArticle { + fn summarize(&self) -> String { + format!("{}, by {} ({})", self.headline, self.author, self.location) + } +} + +struct Tweet { + username: String, + content: String, +} + +impl Summary for Tweet { + fn summarize(&self) -> String { + format!("{}: {}", self.username, self.content) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet.summarize()); +} diff --git a/lessons/05-types-reasoning/basic_trait_display.rs b/lessons/05-types-reasoning/basic_trait_display.rs new file mode 100644 index 0000000..07cf611 --- /dev/null +++ b/lessons/05-types-reasoning/basic_trait_display.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] + +use std::fmt::Display; + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Display for NewsArticle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}, by {} ({})", + self.headline, self.author, self.location + ) + } +} + +struct Tweet { + username: String, + content: String, +} + +impl Display for Tweet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.username, self.content) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet); +} diff --git a/lessons/05-types-reasoning/generic_largest.rs b/lessons/05-types-reasoning/generic_largest.rs new file mode 100644 index 0000000..7aa6a3b --- /dev/null +++ b/lessons/05-types-reasoning/generic_largest.rs @@ -0,0 +1,23 @@ +fn largest(list: &[T]) -> T { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/05-types-reasoning/generics.rs b/lessons/05-types-reasoning/generics.rs new file mode 100644 index 0000000..837f52f --- /dev/null +++ b/lessons/05-types-reasoning/generics.rs @@ -0,0 +1,89 @@ +#![allow(dead_code)] + +use std::fmt::Debug; + +// generic enums +enum OurOption { + Some(T), + None, +} + +// generic structs +struct Tuple2 { + x: T, + y: U, +} + +// generic implementation +impl Tuple2 { + fn new(x: T, y: U) -> Self { + Self { x, y } + } +} + +struct Pair { + x: T, + y: T, +} + +// conditional implementation +impl Pair { + fn largest(&self) -> T { + if self.x > self.y { + self.x + } else { + self.y + } + } +} + +// alternative syntax +impl Pair +where + T: PartialOrd + Copy, +{ + fn smallest(&self) -> T { + if self.x < self.y { + self.x + } else { + self.y + } + } +} + +// Here information about the concrete underlying type is preserved. +fn cloning_machine(item: &T) -> T { + item.clone() +} + +// Here information about the concrete underlying type is erased. +// We can only either format or clone the result. +fn erasing_cloning_machine1(item: &(impl Clone + Debug)) -> impl Clone + Debug { + item.clone() +} + +// Ditto. +fn erasing_cloning_machine2(item: &T) -> impl Clone + Debug { + item.clone() +} + +fn main() { + let _opt = OurOption::Some(10); + + let _p1 = Tuple2 { x: 5, y: 10 }; + let _p2 = Tuple2::new(1, 2.5); + + let arr = [1, 2, 3]; + + let arr2 = cloning_machine(&arr); + let _x = arr2[0]; // This compiles, because `cloning_machine` preserves the type. + println!("{:?}", arr2); + + let arr3 = erasing_cloning_machine1(&arr); + // arr3[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` + println!("{:?}", arr3); + + let arr4 = erasing_cloning_machine2(&arr); + // arr4[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` + println!("{:?}", arr4); +} diff --git a/lessons/05-types-reasoning/generics_fun.rs b/lessons/05-types-reasoning/generics_fun.rs new file mode 100644 index 0000000..241c389 --- /dev/null +++ b/lessons/05-types-reasoning/generics_fun.rs @@ -0,0 +1,35 @@ +use std::fmt::{Display, Formatter}; + +trait DefaultishablyPrintable { + fn defaultish_print() + where + T: Display + Default, + { + println!("{}", T::default()) + } +} + +struct Foo; + +struct Bar; + +impl Display for Bar { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("this is a bar") + } +} + +impl Default for Bar { + fn default() -> Self { + Bar // well, we have no other choice + } +} + +impl DefaultishablyPrintable for Foo {} + +impl DefaultishablyPrintable for Foo {} + +fn main() { + >::defaultish_print(); + >::defaultish_print(); +} diff --git a/lessons/05-types-reasoning/impl_trait.rs b/lessons/05-types-reasoning/impl_trait.rs new file mode 100644 index 0000000..e42329f --- /dev/null +++ b/lessons/05-types-reasoning/impl_trait.rs @@ -0,0 +1,73 @@ +#![allow(dead_code)] + +use std::fmt::Display; + +trait Summary { + fn summarize(&self) -> impl Display; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Display for NewsArticle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + "{}, by {} ({})", + self.headline, self.author, self.location + )?; + f.write_str(&self.content) + } +} + +struct NewsArticleSummarizer<'a>(&'a NewsArticle); + +impl Display for NewsArticleSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let article = self.0; + write!( + f, + "{}, by {} ({})", + article.headline, article.author, article.location + ) + } +} + +impl Summary for NewsArticle { + fn summarize(&self) -> impl Display { + NewsArticleSummarizer(self) + } +} + +struct Tweet { + username: String, + content: String, +} + +impl Summary for Tweet { + fn summarize(&self) -> impl Display { + struct TweetSummarizer<'a>(&'a Tweet); + + impl Display for TweetSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let tweet = self.0; + write!(f, "{}: {}", tweet.username, tweet.content) + } + } + + TweetSummarizer(self) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet.summarize()); +} diff --git a/lessons/05-types-reasoning/index.html b/lessons/05-types-reasoning/index.html new file mode 100644 index 0000000..b65bd1f --- /dev/null +++ b/lessons/05-types-reasoning/index.html @@ -0,0 +1,1194 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
              + + +
              + +
              + +
              +
              +
                +
                +
                + +
                + +

                Reasoning About Types

                +

                + 2024-10-24 (last edit: 2024-10-24) +

                +

                Type traits

                +

                Traits are a way to defined common behavior between different types. They can be compared to interfaces from many other mainstream languages or to type classes from Haskell, however, Rust is not an object-oriented language and there are some notable differences between type traits and Java interfaces.

                +

                The way we describe behavior in Rust is through methods. Traits consist of a set of these methods which then should be implemented by a type. We've already encountered examples of these, like the Clone trait which specified that the clone() method can be called on some given type. Now, let's take a deeper look and try defining our own trait.

                +
                #![allow(dead_code)]
                +
                +trait Summary {
                +    fn summarize(&self) -> String;
                +}
                +
                +struct NewsArticle {
                +    headline: String,
                +    location: String,
                +    author: String,
                +    content: String,
                +}
                +
                +impl Summary for NewsArticle {
                +    fn summarize(&self) -> String {
                +        format!("{}, by {} ({})", self.headline, self.author, self.location)
                +    }
                +}
                +
                +struct Tweet {
                +    username: String,
                +    content: String,
                +}
                +
                +impl Summary for Tweet {
                +    fn summarize(&self) -> String {
                +        format!("{}: {}", self.username, self.content)
                +    }
                +}
                +
                +fn main() {
                +    let tweet = Tweet {
                +        username: String::from("horse_ebooks"),
                +        content: String::from("of course, as you probably already know, people"),
                +    };
                +
                +    println!("1 new tweet: {}", tweet.summarize());
                +}
                +
                +
                +

                (Download the source code for this example: basic_trait.rs)

                +

                Default implementations

                +

                Trait definitions can also be provided with default implementations of behaviors.

                +
                #![allow(dead_code)]
                +
                +struct Upload {
                +    filename: String,
                +}
                +
                +#[allow(dead_code)]
                +struct Photo {
                +    filename: String,
                +    width: u32,
                +    height: u32,
                +}
                +
                +trait Description {
                +    fn describe(&self) -> String {
                +        String::from("No description available.")
                +    }
                +}
                +
                +// All default implementations
                +impl Description for Upload {}
                +
                +// Default implementations can be overwritten
                +impl Description for Photo {
                +    fn describe(&self) -> String {
                +        format!("{} ({} x {})", self.filename, self.width, self.height)
                +    }
                +}
                +
                +// Default implementations can rely on methods with no defaults
                +trait Size {
                +    fn width(&self) -> u32;
                +    fn height(&self) -> u32;
                +
                +    fn size(&self) -> u32 {
                +        self.width() * self.height()
                +    }
                +}
                +
                +impl Size for Photo {
                +    fn width(&self) -> u32 {
                +        self.width
                +    }
                +
                +    fn height(&self) -> u32 {
                +        self.height
                +    }
                +
                +    // Using default impl of `size()`
                +}
                +
                +fn main() {
                +    let upload = Upload {
                +        filename: String::from("notes.txt"),
                +    };
                +
                +    println!("Upload: {}", upload.describe());
                +
                +    let photo = Photo {
                +        filename: String::from("stock_crustacean.png"),
                +        width: 100,
                +        height: 150,
                +    };
                +
                +    println!("Photo: {}", photo.describe());
                +    println!("Size: {}", photo.size());
                +}
                +
                +
                +

                (Download the source code for this example: trait_default.rs)

                +

                What about derive?

                +

                There is a trait-related thing we have used quite extensively and not explained yet, namely the #[derive] attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.

                +

                Derivable traits:

                +
                  +
                • +

                  Equality traits: Eq, PartialEq and comparison traits: Ord and PartialOrd. The Partial- versions exist because there are types which don't fulfill the reflexivity requirement of equality (NaN != NaN) or do not form a total order ( NaN < 0.0 == false and NaN >= 0.0 == false).

                  +
                • +
                • +

                  Data duplication traits: Clone and Copy

                  +
                • +
                • +

                  Hash - allows using values of that type as keys in a hashmap

                  +
                • +
                • +

                  Default - provides a zero-arg constructor function

                  +
                • +
                • +

                  Debug - provides a formatting of the value which can be used in debugging context. It should NOT be implemented manually. In general, if it's possible to derive the Debug, there are no reasons against doing it.

                  +
                • +
                +

                When is it possible to derive a trait?

                +

                When all fields of a struct/variants of an enum implement that trait.

                +

                Should all traits always be derived if it is possible?

                +

                No. Although it may be tempting to just slap #[derive(Clone, Copy)] everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.

                +

                Generics

                +

                Suppose we want to find the largest element in a sequence and return it. Very much on purpose, we didn't specify what type these elements would be - ideally, we would love it to work on all types that have a defined notion of a largest element. However, to make things simpler for now, let's focus only on two primitive types: i32 and char. Let's try to write the code:

                +
                fn largest_i32(list: &[i32]) -> i32 {
                +    let mut largest = list[0];
                +
                +    for &item in list {
                +        if item > largest {
                +            largest = item;
                +        }
                +    }
                +
                +    largest
                +}
                +
                +fn largest_char(list: &[char]) -> char {
                +    let mut largest = list[0];
                +
                +    for &item in list {
                +        if item > largest {
                +            largest = item;
                +        }
                +    }
                +
                +    largest
                +}
                +
                +fn main() {
                +    let number_list = vec![34, 50, 25, 100, 65];
                +
                +    let result = largest_i32(&number_list);
                +    println!("The largest number is {}", result);
                +
                +    let char_list = vec!['y', 'm', 'a', 'q'];
                +
                +    let result = largest_char(&char_list);
                +    println!("The largest char is {}", result);
                +}
                +
                +
                +

                (Download the source code for this example: non_generic.rs)

                +

                Perfect, it works! Now only twenty more types to go...

                +

                Fortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.

                +
                fn largest<T>(list: &[T]) -> T {
                +    let mut largest = list[0];
                +
                +    for &item in list {
                +        if item > largest {
                +            largest = item;
                +        }
                +    }
                +
                +    largest
                +}
                +
                +

                Cleaner already - we merged possibly very many implementations into one. But, when we try to compile this:

                +
                error[E0369]: binary operation `>` cannot be applied to type `T`
                + --> src/main.rs:5:17
                +  |
                +5 |         if item > largest {
                +  |            ---- ^ ------- T
                +  |            |
                +  |            T
                +  |
                +help: consider restricting type parameter `T`
                +  |
                +1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
                +  |             ++++++++++++++++++++++
                +
                +

                Since T can be of absolutely any type now, the compiler cannot be sure that operator > is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:

                +
                fn largest<T: PartialOrd>(list: &[T]) -> T {
                +    let mut largest = list[0];
                +
                +    for &item in list {
                +        if item > largest {
                +            largest = item;
                +        }
                +    }
                +
                +    largest
                +}
                +
                +

                We call this a trait bound, a way to provide constraints on what kind of types we are talking about in a given context. This implementation almost works now. Let's look at the new error.

                +
                error[E0508]: cannot move out of type `[T]`, a non-copy slice
                + --> src/main.rs:2:23
                +  |
                +2 |     let mut largest = list[0];
                +  |                       ^^^^^^^
                +  |                       |
                +  |                       cannot move out of here
                +  |                       move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
                +  |                       help: consider borrowing here: `&list[0]`
                +
                +error[E0507]: cannot move out of a shared reference
                + --> src/main.rs:4:18
                +  |
                +4 |     for &item in list {
                +  |         -----    ^^^^
                +  |         ||
                +  |         |data moved here
                +  |         |move occurs because `item` has type `T`, which does not implement the `Copy` trait
                +  |         help: consider removing the `&`: `item`
                +
                +

                Our function attempts to take ownership, but, again, the compiler doesn't know whether T can just be trivially copied. Rust allows us to combine multiple trait bounds together:

                +
                fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
                +    let mut largest = list[0];
                +
                +    for &item in list {
                +        if item > largest {
                +            largest = item;
                +        }
                +    }
                +
                +    largest
                +}
                +
                +fn main() {
                +    let number_list = vec![34, 50, 25, 100, 65];
                +
                +    let result = largest(&number_list);
                +    println!("The largest number is {}", result);
                +
                +    let char_list = vec!['y', 'm', 'a', 'q'];
                +
                +    let result = largest(&char_list);
                +    println!("The largest char is {}", result);
                +}
                +
                +
                +

                (Download the source code for this example: generic_largest.rs)

                +

                A powerful tool

                +

                There's a lot more that we can do with generics:

                +
                #![allow(dead_code)]
                +
                +use std::fmt::Debug;
                +
                +// generic enums
                +enum OurOption<T> {
                +    Some(T),
                +    None,
                +}
                +
                +// generic structs
                +struct Tuple2<T, U> {
                +    x: T,
                +    y: U,
                +}
                +
                +// generic implementation
                +impl<T, U> Tuple2<T, U> {
                +    fn new(x: T, y: U) -> Self {
                +        Self { x, y }
                +    }
                +}
                +
                +struct Pair<T> {
                +    x: T,
                +    y: T,
                +}
                +
                +// conditional implementation
                +impl<T: PartialOrd + Copy> Pair<T> {
                +    fn largest(&self) -> T {
                +        if self.x > self.y {
                +            self.x
                +        } else {
                +            self.y
                +        }
                +    }
                +}
                +
                +// alternative syntax
                +impl<T> Pair<T>
                +where
                +    T: PartialOrd + Copy,
                +{
                +    fn smallest(&self) -> T {
                +        if self.x < self.y {
                +            self.x
                +        } else {
                +            self.y
                +        }
                +    }
                +}
                +
                +// Here information about the concrete underlying type is preserved.
                +fn cloning_machine<T: Clone + Debug>(item: &T) -> T {
                +    item.clone()
                +}
                +
                +// Here information about the concrete underlying type is erased.
                +// We can only either format or clone the result.
                +fn erasing_cloning_machine1(item: &(impl Clone + Debug)) -> impl Clone + Debug {
                +    item.clone()
                +}
                +
                +// Ditto.
                +fn erasing_cloning_machine2<T: Clone + Debug>(item: &T) -> impl Clone + Debug {
                +    item.clone()
                +}
                +
                +fn main() {
                +    let _opt = OurOption::Some(10);
                +
                +    let _p1 = Tuple2 { x: 5, y: 10 };
                +    let _p2 = Tuple2::new(1, 2.5);
                +
                +    let arr = [1, 2, 3];
                +
                +    let arr2 = cloning_machine(&arr);
                +    let _x = arr2[0]; // This compiles, because `cloning_machine` preserves the type.
                +    println!("{:?}", arr2);
                +
                +    let arr3 = erasing_cloning_machine1(&arr);
                +    // arr3[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug`
                +    println!("{:?}", arr3);
                +
                +    let arr4 = erasing_cloning_machine2(&arr);
                +    // arr4[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug`
                +    println!("{:?}", arr4);
                +}
                +
                +
                +

                (Download the source code for this example: generics.rs)

                +

                A bit more involved example:

                +
                use std::fmt::{Display, Formatter};
                +
                +trait DefaultishablyPrintable<T> {
                +    fn defaultish_print()
                +    where
                +        T: Display + Default,
                +    {
                +        println!("{}", T::default())
                +    }
                +}
                +
                +struct Foo;
                +
                +struct Bar;
                +
                +impl Display for Bar {
                +    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
                +        f.write_str("this is a bar")
                +    }
                +}
                +
                +impl Default for Bar {
                +    fn default() -> Self {
                +        Bar // well, we have no other choice
                +    }
                +}
                +
                +impl DefaultishablyPrintable<i32> for Foo {}
                +
                +impl DefaultishablyPrintable<Bar> for Foo {}
                +
                +fn main() {
                +    <Foo as DefaultishablyPrintable<i32>>::defaultish_print();
                +    <Foo as DefaultishablyPrintable<Bar>>::defaultish_print();
                +}
                +
                +
                +

                (Download the source code for this example: generics_fun.rs)

                +

                Static vs dynamic dispatch

                +
                trait Speak {
                +    fn speak(&self) -> &'static str;
                +}
                +
                +struct Dog;
                +
                +impl Speak for Dog {
                +    fn speak(&self) -> &'static str {
                +        "Hau hau" // it's a Polish dog!
                +    }
                +}
                +
                +struct Human;
                +
                +impl Speak for Human {
                +    fn speak(&self) -> &'static str {
                +        "Hello world"
                +    }
                +}
                +
                +// It works like templates in C++
                +// A different function will be generated for each T during compilation
                +// This process is called "monomorphization"
                +fn static_dispatch<T: Speak>(speaking: &T) {
                +    println!("{}!", speaking.speak());
                +}
                +
                +// Only one copy of that function will exist in the compiled binary
                +fn dynamic_dispatch(speaking: &dyn Speak) {
                +    println!("{}!", speaking.speak());
                +}
                +
                +fn main() {
                +    let dog = Dog;
                +    let human = Human;
                +
                +    static_dispatch(&dog);
                +    static_dispatch(&human);
                +
                +    dynamic_dispatch(&dog);
                +    dynamic_dispatch(&human);
                +
                +    // The observable behavior is identical
                +    // Static dispatch in general is a bit faster,
                +    // because there is no need to perform a "vtable lookup".
                +    // But it can also result in bigger binary sizes.
                +}
                +
                +
                +

                (Download the source code for this example: static_dynamic_dispatch.rs)

                +

                Lifetimes

                +

                Going back to the lesson about ownership, if we try to compile the following code:

                +
                {
                +    let r;
                +
                +    {
                +        let x = 5;
                +        r = &x;
                +    }
                +
                +    println!("r: {}", r);
                +}
                +
                +

                we should expect to get an error:

                +
                error[E0597]: `x` does not live long enough
                +  --> src/main.rs:7:17
                +   |
                +7  |             r = &x;
                +   |                 ^^ borrowed value does not live long enough
                +8  |         }
                +   |         - `x` dropped here while still borrowed
                +9  |
                +10 |         println!("r: {}", r);
                +   |                           - borrow later used here
                +
                +

                Courtesy of the borrow checker, we didn't end up with a dangling reference. But what exactly is happening behind the scenes? Rust introduces a concept of annotated lifetimes, where the lifetime of each value is being marked and tracked by the checker. Let's look at some examples:

                +
                {
                +    let r;                  // ---------+-- 'a
                +                            //          |
                +    {                       //          |
                +        let x = 5;          // -+-- 'b  |
                +        r = &x;             //  |       |
                +    }                       // -+       |
                +                            //          |
                +    println!("r: {}", r);   //          |
                +}                           // ---------+
                +
                +
                {
                +    let x = 5;              // ----------+-- 'b
                +                            //           |
                +    let r = &x;             // --+-- 'a  |
                +                            //   |       |
                +    println!("r: {}", r);   //   |       |
                +                            // --+       |
                +}                           // ----------+
                +
                +

                Annotations

                +

                Let's consider the following code finding the longer out of two strings:

                +
                fn longest(x: &str, y: &str) -> &str {
                +    if x.len() > y.len() {
                +        x
                +    } else {
                +        y
                +    }
                +}
                +
                +fn main() {
                +    let string1 = String::from("abcd");
                +    let string2 = "xyz";
                +
                +    let result = longest(string1.as_str(), string2);
                +    println!("The longest string is {}", result);
                +}
                +
                +

                If we try to compile this, we will get an error:

                +
                error[E0106]: missing lifetime specifier
                + --> src/main.rs:9:33
                +  |
                +9 | fn longest(x: &str, y: &str) -> &str {
                +  |               ----     ----     ^ expected named lifetime parameter
                +  |
                +  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
                +help: consider introducing a named lifetime parameter
                +  |
                +9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
                +  |           ++++     ++          ++          ++
                +
                +

                This is because Rust doesn't know which of the two provided strings (x or y) will be returned from the function. And because they potentially have different lifetimes, the lifetime of what we are returning remains unclear to the compiler - it needs our help.

                +

                Rust provides syntax for specifying lifetimes. The lifetime parameter name from the example (a) doesn't have any concrete meaning - it's just an arbitrary name for this one lifetime.

                +
                &i32        // a reference
                +&'a i32     // a reference with an explicit lifetime
                +&'a mut i32 // a mutable reference with an explicit lifetime
                +
                +

                So, knowing this, let's address the compiler's demands.

                +
                fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
                +    if x.len() > y.len() {
                +        x
                +    } else {
                +        y
                +    }
                +}
                +
                +

                When working with lifetimes, our work will usually revolve around specifying relationships between lifetimes of different values so that the compiler can successfully reason about the program's safety. In the context of the example above, this signature means that both of the function's arguments and its output will live at least as long as lifetime 'a. In practice, this means that the output's lifetime will be equal to the smaller of the two inputs' lifetimes.

                +
                fn longest<'a>(first: &'a str, second: &'a str) -> &'a str {
                +    if first.len() > second.len() {
                +        first
                +    } else {
                +        second
                +    }
                +}
                +
                +fn main() {
                +    let string1 = String::from("long string is long");
                +
                +    {
                +        let string2 = String::from("xyz");
                +        let result = longest(string1.as_str(), string2.as_str());
                +        println!("The longest string is {}", result);
                +    }
                +
                +    // This doesn't compile - incorrect lifetimes
                +    //
                +    // let string1 = String::from("long string is long");
                +    // let result;
                +    // {
                +    //     let string2 = String::from("xyz");
                +    //     result = longest(string1.as_str(), string2.as_str());
                +    // }
                +    // println!("The longest string is {}", result);
                +}
                +
                +
                +

                (Download the source code for this example: lifetimes_basic.rs)

                +

                Trying to compile the second variant displeases the compiler (just like we hoped).

                +
                error[E0597]: `string2` does not live long enough
                + --> src/main.rs:6:44
                +  |
                +6 |         result = longest(string1.as_str(), string2.as_str());
                +  |                                            ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
                +7 |     }
                +  |     - `string2` dropped here while still borrowed
                +8 |     println!("The longest string is {}", result);
                +  |                                          ------ borrow later used here
                +
                +

                Lifetime elision

                +

                We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this lifetime elision.

                +
                fn first_two(seq: &[u32]) -> &[u32] {
                +    if seq.len() < 2 {
                +        seq
                +    } else {
                +        &seq[..2]
                +    }
                +}
                +
                +fn main() {
                +    let seq = [1, 2, 3, 4];
                +
                +    println!(
                +        "First two elements of the sequence: {:?}",
                +        first_two(&seq[..])
                +    );
                +}
                +
                +
                +

                (Download the source code for this example: lifetimes_elision.rs)

                +

                The above works, even though we didn't specify any lifetime parameters at all. The reason lies in the rules we mentioned, which are as follows (where input lifetimes are lifetimes on parameters and output lifetimes are lifetimes on return values):

                +
                  +
                • +

                  Each parameter that is a reference gets its own lifetime parameter.

                  +
                • +
                • +

                  If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.

                  +
                • +
                • +

                  If there are multiple input lifetime parameters, but one of them is &self or &mut self, the lifetime of self is assigned to all output lifetime parameters.

                  +
                • +
                +

                Let's try to understand how the compiler inferred the lifetimes of our first_two functions. We start with the following signature:

                +
                fn first_two(seq: &[u32]) -> &[u32] {
                +
                +

                Then, we apply the first rule:

                +
                fn first_two<'a>(seq: &'a [u32]) -> &[u32] {
                +
                +

                Next, we check the second rule. It applies here as well.

                +
                fn first_two<'a>(seq: &'a [u32]) -> &'a [u32] {
                +
                +

                With that, we arrive at a state where all lifetimes are specified.

                +

                Static lifetime

                +

                There exists one special lifetime called 'static, which means that a reference can live for the entire duration of the program. All string literals are annotated with this lifetime as they are stored directly in the program's binary. Full type annotation of a string literal in Rust is therefore as follows:

                +
                let s: &'static str = "I have a static lifetime.";
                +
                +

                Trait + lifetimes - a challenging tandem

                +

                Let's go back to our basic_trait.rs example. The Summary trait was really wasteful: it always allocated the Strings on heap, even though we only needed to display the formatted string, and we could do that without allocations. How? By using Display trait, of course.

                +

                The simplest possible optimisation would be like this:

                +
                #![allow(dead_code)]
                +
                +use std::fmt::Display;
                +
                +struct NewsArticle {
                +    headline: String,
                +    location: String,
                +    author: String,
                +    content: String,
                +}
                +
                +impl Display for NewsArticle {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        write!(
                +            f,
                +            "{}, by {} ({})",
                +            self.headline, self.author, self.location
                +        )
                +    }
                +}
                +
                +struct Tweet {
                +    username: String,
                +    content: String,
                +}
                +
                +impl Display for Tweet {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        write!(f, "{}: {}", self.username, self.content)
                +    }
                +}
                +
                +fn main() {
                +    let tweet = Tweet {
                +        username: String::from("horse_ebooks"),
                +        content: String::from("of course, as you probably already know, people"),
                +    };
                +
                +    println!("1 new tweet: {}", tweet);
                +}
                +
                +
                +

                (Download the source code for this example: basic_trait_display.rs)

                +

                This eliminates the heap allocations, but there's another catch. What if NewsArticle already had another (non-summarizing) Display implementation? We would end up in a double-trait-implementation conflict, which is a compile-time error.

                +

                We can solve the one-type-one-trait-impl problem by introducing another type just for summarizing. The first attempt could be to use generics in traits:

                +
                #![allow(dead_code)]
                +
                +use std::fmt::Display;
                +
                +trait Summary<'a, Summarizer: Display> {
                +    fn summarize(&'a self) -> Summarizer;
                +}
                +
                +struct NewsArticle {
                +    headline: String,
                +    location: String,
                +    author: String,
                +    content: String,
                +}
                +
                +impl Display for NewsArticle {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        writeln!(
                +            f,
                +            "{}, by {} ({})",
                +            self.headline, self.author, self.location
                +        )?;
                +        f.write_str(&self.content)
                +    }
                +}
                +
                +struct NewsArticleSummarizer<'a>(&'a NewsArticle);
                +
                +impl Display for NewsArticleSummarizer<'_> {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        let article = self.0;
                +        write!(
                +            f,
                +            "{}, by {} ({})",
                +            article.headline, article.author, article.location
                +        )
                +    }
                +}
                +
                +impl<'a> Summary<'a, NewsArticleSummarizer<'a>> for NewsArticle {
                +    fn summarize(&'a self) -> NewsArticleSummarizer<'a> {
                +        NewsArticleSummarizer(self)
                +    }
                +}
                +
                +struct Tweet {
                +    username: String,
                +    content: String,
                +}
                +
                +struct TweetSummarizer<'a>(&'a Tweet);
                +
                +impl Display for TweetSummarizer<'_> {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        let tweet = self.0;
                +        write!(f, "{}: {}", tweet.username, tweet.content)
                +    }
                +}
                +
                +impl<'a> Summary<'a, TweetSummarizer<'a>> for Tweet {
                +    fn summarize(&'a self) -> TweetSummarizer<'a> {
                +        TweetSummarizer(self)
                +    }
                +}
                +
                +impl<'a> Summary<'a, NewsArticleSummarizer<'a>> for Tweet {
                +    fn summarize(&'a self) -> NewsArticleSummarizer<'a> {
                +        unimplemented!("This is only to make code type-check and compile.");
                +    }
                +}
                +
                +fn main() {
                +    let empty_article = NewsArticle {
                +        headline: "".into(),
                +        location: String::new(),
                +        author: String::default(),
                +        content: Default::default(),
                +    };
                +    println!("1 new article: {}", empty_article.summarize());
                +
                +    let tweet = Tweet {
                +        username: String::from("horse_ebooks"),
                +        content: String::from("of course, as you probably already know, people"),
                +    };
                +
                +    // Compile error: `type annotations needed; multiple `impl`s satisfying `Tweet: Summary<'_, _>` found`
                +    // println!("1 new tweet: {}", tweet.summarize());
                +    println!(
                +        "1 new tweet: {}",
                +        <Tweet as Summary<'_, TweetSummarizer>>::summarize(&tweet)
                +    );
                +    println!(
                +        "1 new tweet: {}",
                +        <Tweet as Summary<'_, NewsArticleSummarizer>>::summarize(&tweet)
                +    );
                +}
                +
                +
                +

                (Download the source code for this example: trait_generic_type.rs)

                +

                The problem here is that nothing hinders us from implement the trait (with various type parameters) for the same type, which leads to awkward ambiguity when calling the trait's methods (see main fn).

                +

                The use of generic types in Summary trait makes it semantics like this:

                +
                +

                A type can be summarized with any type supporting it.

                +
                +

                When we want the trait to require exactly one possible generic implementation for a given type, we can leverage associated types. Example here:

                +
                #![allow(dead_code)]
                +
                +use std::fmt::Display;
                +
                +trait Summary<'a> {
                +    type Summarizer: Display;
                +
                +    fn summarize(&'a self) -> Self::Summarizer;
                +}
                +
                +struct NewsArticle {
                +    headline: String,
                +    location: String,
                +    author: String,
                +    content: String,
                +}
                +
                +impl Display for NewsArticle {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        writeln!(
                +            f,
                +            "{}, by {} ({})",
                +            self.headline, self.author, self.location
                +        )?;
                +        f.write_str(&self.content)
                +    }
                +}
                +
                +struct NewsArticleSummarizer<'a>(&'a NewsArticle);
                +
                +impl Display for NewsArticleSummarizer<'_> {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        let article = self.0;
                +        write!(
                +            f,
                +            "{}, by {} ({})",
                +            article.headline, article.author, article.location
                +        )
                +    }
                +}
                +
                +impl<'a> Summary<'a> for NewsArticle {
                +    type Summarizer = NewsArticleSummarizer<'a>;
                +    fn summarize(&'a self) -> Self::Summarizer {
                +        NewsArticleSummarizer(self)
                +    }
                +}
                +
                +struct Tweet {
                +    username: String,
                +    content: String,
                +}
                +
                +struct TweetSummarizer<'a>(&'a Tweet);
                +
                +impl Display for TweetSummarizer<'_> {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        let tweet = self.0;
                +        write!(f, "{}: {}", tweet.username, tweet.content)
                +    }
                +}
                +
                +impl<'a> Summary<'a> for Tweet {
                +    type Summarizer = TweetSummarizer<'a>;
                +    fn summarize(&'a self) -> Self::Summarizer {
                +        TweetSummarizer(self)
                +    }
                +}
                +
                +fn main() {
                +    let tweet = Tweet {
                +        username: String::from("horse_ebooks"),
                +        content: String::from("of course, as you probably already know, people"),
                +    };
                +
                +    println!("1 new tweet: {}", tweet.summarize());
                +}
                +
                +
                +

                (Download the source code for this example: trait_associated_type.rs)

                +

                The use of associated types in Summary trait makes it semantics like this:

                +
                +

                A type can be summarized with at most one specific type.

                +
                +

                Yet another approach (arguably, the cleanest one) would be to use the impl trait syntax in a trait (quite recently stabilized!). +Example:

                +
                #![allow(dead_code)]
                +
                +use std::fmt::Display;
                +
                +trait Summary {
                +    fn summarize(&self) -> impl Display;
                +}
                +
                +struct NewsArticle {
                +    headline: String,
                +    location: String,
                +    author: String,
                +    content: String,
                +}
                +
                +impl Display for NewsArticle {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        writeln!(
                +            f,
                +            "{}, by {} ({})",
                +            self.headline, self.author, self.location
                +        )?;
                +        f.write_str(&self.content)
                +    }
                +}
                +
                +struct NewsArticleSummarizer<'a>(&'a NewsArticle);
                +
                +impl Display for NewsArticleSummarizer<'_> {
                +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +        let article = self.0;
                +        write!(
                +            f,
                +            "{}, by {} ({})",
                +            article.headline, article.author, article.location
                +        )
                +    }
                +}
                +
                +impl Summary for NewsArticle {
                +    fn summarize(&self) -> impl Display {
                +        NewsArticleSummarizer(self)
                +    }
                +}
                +
                +struct Tweet {
                +    username: String,
                +    content: String,
                +}
                +
                +impl Summary for Tweet {
                +    fn summarize(&self) -> impl Display {
                +        struct TweetSummarizer<'a>(&'a Tweet);
                +
                +        impl Display for TweetSummarizer<'_> {
                +            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                +                let tweet = self.0;
                +                write!(f, "{}: {}", tweet.username, tweet.content)
                +            }
                +        }
                +
                +        TweetSummarizer(self)
                +    }
                +}
                +
                +fn main() {
                +    let tweet = Tweet {
                +        username: String::from("horse_ebooks"),
                +        content: String::from("of course, as you probably already know, people"),
                +    };
                +
                +    println!("1 new tweet: {}", tweet.summarize());
                +}
                +
                +
                +

                (Download the source code for this example: impl_trait.rs)

                +

                Obligatory reading

                + +

                Assignment 3 (graded)

                +

                Passage Pathing

                +

                Deadline: 30.10.2024 23:59

                + + +
                +
                + + + + +
                + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/05-types-reasoning/lifetimes_basic.rs b/lessons/05-types-reasoning/lifetimes_basic.rs new file mode 100644 index 0000000..a4eaa2b --- /dev/null +++ b/lessons/05-types-reasoning/lifetimes_basic.rs @@ -0,0 +1,27 @@ +fn longest<'a>(first: &'a str, second: &'a str) -> &'a str { + if first.len() > second.len() { + first + } else { + second + } +} + +fn main() { + let string1 = String::from("long string is long"); + + { + let string2 = String::from("xyz"); + let result = longest(string1.as_str(), string2.as_str()); + println!("The longest string is {}", result); + } + + // This doesn't compile - incorrect lifetimes + // + // let string1 = String::from("long string is long"); + // let result; + // { + // let string2 = String::from("xyz"); + // result = longest(string1.as_str(), string2.as_str()); + // } + // println!("The longest string is {}", result); +} diff --git a/lessons/05-types-reasoning/lifetimes_elision.rs b/lessons/05-types-reasoning/lifetimes_elision.rs new file mode 100644 index 0000000..9633909 --- /dev/null +++ b/lessons/05-types-reasoning/lifetimes_elision.rs @@ -0,0 +1,16 @@ +fn first_two(seq: &[u32]) -> &[u32] { + if seq.len() < 2 { + seq + } else { + &seq[..2] + } +} + +fn main() { + let seq = [1, 2, 3, 4]; + + println!( + "First two elements of the sequence: {:?}", + first_two(&seq[..]) + ); +} diff --git a/lessons/05-types-reasoning/non_generic.rs b/lessons/05-types-reasoning/non_generic.rs new file mode 100644 index 0000000..c983e3b --- /dev/null +++ b/lessons/05-types-reasoning/non_generic.rs @@ -0,0 +1,35 @@ +fn largest_i32(list: &[i32]) -> i32 { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn largest_char(list: &[char]) -> char { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest_i32(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest_char(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/05-types-reasoning/static_dynamic_dispatch.rs b/lessons/05-types-reasoning/static_dynamic_dispatch.rs new file mode 100644 index 0000000..2d9eeda --- /dev/null +++ b/lessons/05-types-reasoning/static_dynamic_dispatch.rs @@ -0,0 +1,47 @@ +trait Speak { + fn speak(&self) -> &'static str; +} + +struct Dog; + +impl Speak for Dog { + fn speak(&self) -> &'static str { + "Hau hau" // it's a Polish dog! + } +} + +struct Human; + +impl Speak for Human { + fn speak(&self) -> &'static str { + "Hello world" + } +} + +// It works like templates in C++ +// A different function will be generated for each T during compilation +// This process is called "monomorphization" +fn static_dispatch(speaking: &T) { + println!("{}!", speaking.speak()); +} + +// Only one copy of that function will exist in the compiled binary +fn dynamic_dispatch(speaking: &dyn Speak) { + println!("{}!", speaking.speak()); +} + +fn main() { + let dog = Dog; + let human = Human; + + static_dispatch(&dog); + static_dispatch(&human); + + dynamic_dispatch(&dog); + dynamic_dispatch(&human); + + // The observable behavior is identical + // Static dispatch in general is a bit faster, + // because there is no need to perform a "vtable lookup". + // But it can also result in bigger binary sizes. +} diff --git a/lessons/05-types-reasoning/trait_associated_type.rs b/lessons/05-types-reasoning/trait_associated_type.rs new file mode 100644 index 0000000..1c108bf --- /dev/null +++ b/lessons/05-types-reasoning/trait_associated_type.rs @@ -0,0 +1,77 @@ +#![allow(dead_code)] + +use std::fmt::Display; + +trait Summary<'a> { + type Summarizer: Display; + + fn summarize(&'a self) -> Self::Summarizer; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Display for NewsArticle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + "{}, by {} ({})", + self.headline, self.author, self.location + )?; + f.write_str(&self.content) + } +} + +struct NewsArticleSummarizer<'a>(&'a NewsArticle); + +impl Display for NewsArticleSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let article = self.0; + write!( + f, + "{}, by {} ({})", + article.headline, article.author, article.location + ) + } +} + +impl<'a> Summary<'a> for NewsArticle { + type Summarizer = NewsArticleSummarizer<'a>; + fn summarize(&'a self) -> Self::Summarizer { + NewsArticleSummarizer(self) + } +} + +struct Tweet { + username: String, + content: String, +} + +struct TweetSummarizer<'a>(&'a Tweet); + +impl Display for TweetSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let tweet = self.0; + write!(f, "{}: {}", tweet.username, tweet.content) + } +} + +impl<'a> Summary<'a> for Tweet { + type Summarizer = TweetSummarizer<'a>; + fn summarize(&'a self) -> Self::Summarizer { + TweetSummarizer(self) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet.summarize()); +} diff --git a/lessons/05-types-reasoning/trait_default.rs b/lessons/05-types-reasoning/trait_default.rs new file mode 100644 index 0000000..84ba1b3 --- /dev/null +++ b/lessons/05-types-reasoning/trait_default.rs @@ -0,0 +1,67 @@ +#![allow(dead_code)] + +struct Upload { + filename: String, +} + +#[allow(dead_code)] +struct Photo { + filename: String, + width: u32, + height: u32, +} + +trait Description { + fn describe(&self) -> String { + String::from("No description available.") + } +} + +// All default implementations +impl Description for Upload {} + +// Default implementations can be overwritten +impl Description for Photo { + fn describe(&self) -> String { + format!("{} ({} x {})", self.filename, self.width, self.height) + } +} + +// Default implementations can rely on methods with no defaults +trait Size { + fn width(&self) -> u32; + fn height(&self) -> u32; + + fn size(&self) -> u32 { + self.width() * self.height() + } +} + +impl Size for Photo { + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } + + // Using default impl of `size()` +} + +fn main() { + let upload = Upload { + filename: String::from("notes.txt"), + }; + + println!("Upload: {}", upload.describe()); + + let photo = Photo { + filename: String::from("stock_crustacean.png"), + width: 100, + height: 150, + }; + + println!("Photo: {}", photo.describe()); + println!("Size: {}", photo.size()); +} diff --git a/lessons/05-types-reasoning/trait_generic_type.rs b/lessons/05-types-reasoning/trait_generic_type.rs new file mode 100644 index 0000000..dea0170 --- /dev/null +++ b/lessons/05-types-reasoning/trait_generic_type.rs @@ -0,0 +1,96 @@ +#![allow(dead_code)] + +use std::fmt::Display; + +trait Summary<'a, Summarizer: Display> { + fn summarize(&'a self) -> Summarizer; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Display for NewsArticle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + "{}, by {} ({})", + self.headline, self.author, self.location + )?; + f.write_str(&self.content) + } +} + +struct NewsArticleSummarizer<'a>(&'a NewsArticle); + +impl Display for NewsArticleSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let article = self.0; + write!( + f, + "{}, by {} ({})", + article.headline, article.author, article.location + ) + } +} + +impl<'a> Summary<'a, NewsArticleSummarizer<'a>> for NewsArticle { + fn summarize(&'a self) -> NewsArticleSummarizer<'a> { + NewsArticleSummarizer(self) + } +} + +struct Tweet { + username: String, + content: String, +} + +struct TweetSummarizer<'a>(&'a Tweet); + +impl Display for TweetSummarizer<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let tweet = self.0; + write!(f, "{}: {}", tweet.username, tweet.content) + } +} + +impl<'a> Summary<'a, TweetSummarizer<'a>> for Tweet { + fn summarize(&'a self) -> TweetSummarizer<'a> { + TweetSummarizer(self) + } +} + +impl<'a> Summary<'a, NewsArticleSummarizer<'a>> for Tweet { + fn summarize(&'a self) -> NewsArticleSummarizer<'a> { + unimplemented!("This is only to make code type-check and compile."); + } +} + +fn main() { + let empty_article = NewsArticle { + headline: "".into(), + location: String::new(), + author: String::default(), + content: Default::default(), + }; + println!("1 new article: {}", empty_article.summarize()); + + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + // Compile error: `type annotations needed; multiple `impl`s satisfying `Tweet: Summary<'_, _>` found` + // println!("1 new tweet: {}", tweet.summarize()); + println!( + "1 new tweet: {}", + >::summarize(&tweet) + ); + println!( + "1 new tweet: {}", + >::summarize(&tweet) + ); +} diff --git a/lessons/06-closures-iterators/closures_capturing.rs b/lessons/06-closures-iterators/closures_capturing.rs new file mode 100644 index 0000000..c42a640 --- /dev/null +++ b/lessons/06-closures-iterators/closures_capturing.rs @@ -0,0 +1,87 @@ +fn main() { + borrowing_immutably_closure(); + borrowing_mutably_closure(); + moving_in_nonmutating_closure(); + moving_in_mutating_closure(); + moving_in_moving_out_closure(); +} + +fn borrowing_immutably_closure() { + let list = vec![1, 2, 3]; + println!("Before defining closure: {:?}", list); + + let only_borrows = || println!("From closure: {:?}", list); + + // This would not really only borrow... (it needs Vec by value). + // let only_borrows = || std::mem::drop::>(list); + + println!("Before calling closure: {:?}", list); + only_borrows(); + println!("After calling closure: {:?}", list); +} + +fn borrowing_mutably_closure() { + let mut list = vec![1, 2, 3]; + println!("Before defining closure: {:?}", list); + + let mut borrows_mutably = || list.push(7); + + // println!("Before calling closure: {:?}", list); + borrows_mutably(); + println!("After calling closure: {:?}", list); +} + +fn moving_in_nonmutating_closure() { + let list = vec![1, 2, 3]; + println!("Before defining closure: {:?}", list); + + // This closure would just borrow the list, because it only prints it. + // However, as spawning threads require passing `impl FnOnce + 'static`, + // we need to use `move` keyword to force the closure to move `list` + // into its captured environment. + std::thread::spawn(move || println!("From thread: {:?}", list)) + .join() + .unwrap(); +} + +fn moving_in_mutating_closure() { + fn append_42(mut appender: impl FnMut(i32)) { + appender(42); + } + + let mut appender = { + let mut list = vec![1, 2, 3]; + println!("Before defining closure: {:?}", list); + + // The `move` keyword is necessary to prevent dangling reference to `list`. + // Of course, the borrow checker protects us from compiling code without `move`. + move |num| list.push(num) + }; + + append_42(&mut appender); + append_42(&mut appender); +} + +fn moving_in_moving_out_closure() { + fn append_multiple_times(appender: impl FnOnce(&mut Vec) + Clone) { + let mut list = Vec::new(); + + // We can clone this `FnOnce`, because we additionally require `Clone`. + // If we didn't clone it, we couldn't call it more than *once*. + appender.clone()(&mut list); + appender(&mut list); + } + + let appender = { + let string = String::from("Ala"); + println!("Before defining closure: {:?}", string); + + // The `move` keyword is necessary to prevent dangling reference to `list`. + // Of course, the borrow checker protects us from compiling code without `move`. + move |list: &mut Vec| list.push(string) + }; + + // As `appender` is only `FnOnce`, we need to clone before we consume it by calling it. + append_multiple_times(appender.clone()); + append_multiple_times(appender); +} diff --git a/lessons/06-closures-iterators/closures_fun.rs b/lessons/06-closures-iterators/closures_fun.rs new file mode 100644 index 0000000..9f2ae91 --- /dev/null +++ b/lessons/06-closures-iterators/closures_fun.rs @@ -0,0 +1,64 @@ +fn main() { + fn some_function() -> String { + String::new() + } + + let v1 = String::from("v1"); + let mut borrowing_immutably_closure = || v1.clone(); + + let mut v2 = String::from("v2"); + let mut borrowing_mutably_closure = || { + v2.push('.'); + v2.clone() + }; + + let v3 = String::from("v3"); + let mut moving_in_nonmutating_closure = move || v3.clone(); + + let mut v4 = String::from("v4"); + let mut moving_in_mutating_closure = move || { + v4.push('.'); + v4.clone() + }; + let v5 = String::from("v5"); + let moving_in_moving_out_closure = || v5; + + let fn_once_callables: [&dyn FnOnce() -> String; 5] = [ + &some_function, + &borrowing_immutably_closure, + &borrowing_mutably_closure, + &moving_in_nonmutating_closure, + &moving_in_moving_out_closure, + ]; + + #[allow(unused_variables)] + for fn_once_callable in fn_once_callables { + // Cannot move a value of type `dyn FnOnce() -> String`. + // The size of `dyn FnOnce() -> String` cannot be statically determined. + // println!("{}", fn_once_callable()); + + // So, for FnOnce, we need to be their owners to be able to call them, + // and we can't have a `dyn` object owned on stack. + // We will solve this problem soon with smart pointers (e.g., Box). + // This will give us `std::function` -like experience. + } + + // Mutable reference to FnMut is required to be able to call it. + let fn_mut_callables: [&mut dyn FnMut() -> String; 4] = [ + &mut borrowing_immutably_closure, + &mut borrowing_mutably_closure, + &mut moving_in_nonmutating_closure, + &mut moving_in_mutating_closure, + ]; + + for fn_mut_callable in fn_mut_callables { + println!("{}", fn_mut_callable()); + } + + let fn_callables: &[&dyn Fn() -> String] = + &[&borrowing_immutably_closure, &moving_in_nonmutating_closure]; + + for fn_callable in fn_callables { + println!("{}", fn_callable()); + } +} diff --git a/lessons/06-closures-iterators/closures_syntax.rs b/lessons/06-closures-iterators/closures_syntax.rs new file mode 100644 index 0000000..4b07bd2 --- /dev/null +++ b/lessons/06-closures-iterators/closures_syntax.rs @@ -0,0 +1,20 @@ +fn main() { + #[rustfmt::skip] + { + // This is formatted so that without rust-analyzer it renders as well-aligned. + + fn add_one_v1 (x: u32) -> u32 { x + 1 } // This is an ordinary function. + let add_one_v2 = |x: u32| -> u32 { x + 1 }; // Closures use pipes instead of parentheses. + let add_one_v3 = |x| { x + 1 }; // Both parameters and return value can have their types inferred. + let add_one_v4 = |x| x + 1 ; // If the body is a single expression, braces can be omitted. + + let _res = add_one_v1(0_u32); + let _res = add_one_v2(0_u32); + let _res = add_one_v3(0_u32); + let _res = add_one_v4(0_u32); + + // This does not compile, because closures are not generic. + // Their type is inferred once and stays the same. + // let _res = add_one_v4(0_i32); + }; +} diff --git a/lessons/06-closures-iterators/index.html b/lessons/06-closures-iterators/index.html new file mode 100644 index 0000000..e99ad45 --- /dev/null +++ b/lessons/06-closures-iterators/index.html @@ -0,0 +1,536 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                + + +
                + +
                + +
                +
                +
                  +
                  +
                  + +
                  + +

                  Closures and Iterators

                  +

                  + 2024-10-31 (last edit: 2024-10-30) +

                  +

                  Closures

                  +

                  Closures (Polish: "domknięcia") are anonymous functions that can access variables from the scope in which they were defined.

                  +

                  Closure syntax

                  +
                  fn main() {
                  +    #[rustfmt::skip]
                  +    {
                  +        // This is formatted so that without rust-analyzer it renders as well-aligned.
                  +
                  +        fn  add_one_v1   (x: u32) -> u32 { x + 1 }  // This is an ordinary function.
                  +        let add_one_v2 = |x: u32| -> u32 { x + 1 }; // Closures use pipes instead of parentheses.
                  +        let add_one_v3 = |x|             { x + 1 }; // Both parameters and return value can have their types inferred.
                  +        let add_one_v4 = |x|               x + 1  ; // If the body is a single expression, braces can be omitted.
                  +
                  +        let _res = add_one_v1(0_u32);
                  +        let _res = add_one_v2(0_u32);
                  +        let _res = add_one_v3(0_u32);
                  +        let _res = add_one_v4(0_u32);
                  +        
                  +        // This does not compile, because closures are not generic.
                  +        // Their type is inferred once and stays the same.
                  +        // let _res = add_one_v4(0_i32);
                  +    };
                  +}
                  +
                  +
                  +

                  (Download the source code for this example: closures_syntax.rs)

                  +

                  Closures' types

                  +

                  Closures are unnameable types. That is, each closure gets its own unique type from the compiler, +but we cannot name it. Therefore, closures' types must be inferred. +We will often use impl keyword with closure traits (e.g., impl Fn) - those traits are described below.

                  +

                  Closures capture environment

                  +

                  Closures can capture variables from the environment where they are defined. They can do that in two ways:

                  +
                    +
                  • Capturing References (borrowing), or
                  • +
                  • Moving Ownership.
                  • +
                  +

                  HOW closures capture variables is one thing. +But even more important is WHAT closures do with their captures.

                  +

                  Functions & closures hierarchy

                  +

                  Based on WHAT a closure does with its captures, it implements closure traits:

                  +
                    +
                  • FnOnce - closures that may move out of their captures environment (and thus called once).
                  • +
                  • FnMut - closures that may mutate their captures, but don't move out of their captures environment (so can be called multiple times, but require a mutable reference);
                  • +
                  • Fn - closures that do not mutate their captures (so can be called multiple times through an immutable reference).
                  • +
                  +

                  For completeness, there is a (concrete) type of function pointers:

                  +
                    +
                  • fn - functions, closures with no captures.
                  • +
                  +

                  Those traits and the fn type form a hierarchy: fnFnFnMutFnOnce

                  + $$ fn \subseteq Fn \subseteq FnMut \subseteq FnOnce $$ --> +

                  The following code sample demonstrates various ways to capture environment (borrowing or moving) and various kinds of closures, based on what they do with their captures:

                  +
                  fn main() {
                  +    borrowing_immutably_closure();
                  +    borrowing_mutably_closure();
                  +    moving_in_nonmutating_closure();
                  +    moving_in_mutating_closure();
                  +    moving_in_moving_out_closure();
                  +}
                  +
                  +fn borrowing_immutably_closure() {
                  +    let list = vec![1, 2, 3];
                  +    println!("Before defining closure: {:?}", list);
                  +
                  +    let only_borrows = || println!("From closure: {:?}", list);
                  +
                  +    // This would not really only borrow... (it needs Vec by value).
                  +    // let only_borrows = || std::mem::drop::<Vec<_>>(list);
                  +
                  +    println!("Before calling closure: {:?}", list);
                  +    only_borrows();
                  +    println!("After calling closure: {:?}", list);
                  +}
                  +
                  +fn borrowing_mutably_closure() {
                  +    let mut list = vec![1, 2, 3];
                  +    println!("Before defining closure: {:?}", list);
                  +
                  +    let mut borrows_mutably = || list.push(7);
                  +
                  +    // println!("Before calling closure: {:?}", list);
                  +    borrows_mutably();
                  +    println!("After calling closure: {:?}", list);
                  +}
                  +
                  +fn moving_in_nonmutating_closure() {
                  +    let list = vec![1, 2, 3];
                  +    println!("Before defining closure: {:?}", list);
                  +
                  +    // This closure would just borrow the list, because it only prints it.
                  +    // However, as spawning threads require passing `impl FnOnce + 'static`,
                  +    // we need to use `move` keyword to force the closure to move `list`
                  +    // into its captured environment.
                  +    std::thread::spawn(move || println!("From thread: {:?}", list))
                  +        .join()
                  +        .unwrap();
                  +}
                  +
                  +fn moving_in_mutating_closure() {
                  +    fn append_42(mut appender: impl FnMut(i32)) {
                  +        appender(42);
                  +    }
                  +
                  +    let mut appender = {
                  +        let mut list = vec![1, 2, 3];
                  +        println!("Before defining closure: {:?}", list);
                  +
                  +        // The `move` keyword is necessary to prevent dangling reference to `list`.
                  +        // Of course, the borrow checker protects us from compiling code without `move`.
                  +        move |num| list.push(num)
                  +    };
                  +
                  +    append_42(&mut appender);
                  +    append_42(&mut appender);
                  +}
                  +
                  +fn moving_in_moving_out_closure() {
                  +    fn append_multiple_times(appender: impl FnOnce(&mut Vec<String>) + Clone) {
                  +        let mut list = Vec::new();
                  +
                  +        // We can clone this `FnOnce`, because we additionally require `Clone`.
                  +        // If we didn't clone it, we couldn't call it more than *once*.
                  +        appender.clone()(&mut list);
                  +        appender(&mut list);
                  +    }
                  +
                  +    let appender = {
                  +        let string = String::from("Ala");
                  +        println!("Before defining closure: {:?}", string);
                  +
                  +        // The `move` keyword is necessary to prevent dangling reference to `list`.
                  +        // Of course, the borrow checker protects us from compiling code without `move`.
                  +        move |list: &mut Vec<String>| list.push(string)
                  +    };
                  +
                  +    // As `appender` is only `FnOnce`, we need to clone before we consume it by calling it.
                  +    append_multiple_times(appender.clone());
                  +    append_multiple_times(appender);
                  +}
                  +
                  +
                  +

                  (Download the source code for this example: closures_capturing.rs)

                  +

                  Closures as trait objects (in dynamic dispatch)

                  +

                  The following code sample shows how one can use closures as dyn Trait objects, bypassing the problem of them having anonymous types:

                  +
                  fn main() {
                  +    fn some_function() -> String {
                  +        String::new()
                  +    }
                  +
                  +    let v1 = String::from("v1");
                  +    let mut borrowing_immutably_closure = || v1.clone();
                  +
                  +    let mut v2 = String::from("v2");
                  +    let mut borrowing_mutably_closure = || {
                  +        v2.push('.');
                  +        v2.clone()
                  +    };
                  +
                  +    let v3 = String::from("v3");
                  +    let mut moving_in_nonmutating_closure = move || v3.clone();
                  +
                  +    let mut v4 = String::from("v4");
                  +    let mut moving_in_mutating_closure = move || {
                  +        v4.push('.');
                  +        v4.clone()
                  +    };
                  +    let v5 = String::from("v5");
                  +    let moving_in_moving_out_closure = || v5;
                  +
                  +    let fn_once_callables: [&dyn FnOnce() -> String; 5] = [
                  +        &some_function,
                  +        &borrowing_immutably_closure,
                  +        &borrowing_mutably_closure,
                  +        &moving_in_nonmutating_closure,
                  +        &moving_in_moving_out_closure,
                  +    ];
                  +
                  +    #[allow(unused_variables)]
                  +    for fn_once_callable in fn_once_callables {
                  +        // Cannot move a value of type `dyn FnOnce() -> String`.
                  +        // The size of `dyn FnOnce() -> String` cannot be statically determined.
                  +        // println!("{}", fn_once_callable());
                  +
                  +        // So, for FnOnce, we need to be their owners to be able to call them,
                  +        // and we can't have a `dyn` object owned on stack.
                  +        // We will solve this problem soon with smart pointers (e.g., Box).
                  +        // This will give us `std::function` -like experience.
                  +    }
                  +
                  +    // Mutable reference to FnMut is required to be able to call it.
                  +    let fn_mut_callables: [&mut dyn FnMut() -> String; 4] = [
                  +        &mut borrowing_immutably_closure,
                  +        &mut borrowing_mutably_closure,
                  +        &mut moving_in_nonmutating_closure,
                  +        &mut moving_in_mutating_closure,
                  +    ];
                  +
                  +    for fn_mut_callable in fn_mut_callables {
                  +        println!("{}", fn_mut_callable());
                  +    }
                  +
                  +    let fn_callables: &[&dyn Fn() -> String] =
                  +        &[&borrowing_immutably_closure, &moving_in_nonmutating_closure];
                  +
                  +    for fn_callable in fn_callables {
                  +        println!("{}", fn_callable());
                  +    }
                  +}
                  +
                  +
                  +

                  (Download the source code for this example: closures_fun.rs)

                  +

                  Examples

                  +

                  We'll go through the examples from Rust by Example. +More examples will be seen when working with iterators.

                  +

                  Iterators

                  +

                  In Rust, there is no hierarchy of types for collections (because there is no inheritance in general). +Instead, what makes a collection is that it can be iterated over.

                  +

                  A usual way in Rust to perform an iteration over something, be it a range of values or items in a collection, is creating a (lazy) iterator over it and transforming it using iterator adaptors. For example, if T: Iterator, then T::map() creates a Map<T> adaptor. Once a final iterator is created, it has to be actually activated (iterated over), which is most commonly done by:

                  +
                    +
                  • exhausting it with the for loop,
                  • +
                  • manually iterating over it using next() calls,
                  • +
                  • collecting its contents into inferred collection (collect()),
                  • +
                  • consuming it with a consuming adaptor (e.g., sum(), count),
                  • +
                  +
                  use std::collections::HashSet;
                  +
                  +fn main() {
                  +    // Various ways to create a String.
                  +    let mut strings = [
                  +        String::new(),
                  +        String::from("a"),
                  +        "b".into(),
                  +        "c".to_owned(),
                  +        "d".to_string(),
                  +        "e".chars().collect(),
                  +    ];
                  +
                  +    // `iter()` is a usual method that creates an iterator over immutable references to the collection's items.
                  +    let _all_len_0_or_1 = strings
                  +        .iter()
                  +        .filter(|s| !s.is_empty())
                  +        .all(|s| s.len() == 1);
                  +
                  +    // `iter_mut()` is a usual method that creates an iterator over mutable references to the collection's items.
                  +    for s in strings.iter_mut().map_while(|s| match s.as_str() {
                  +        "c" => None,
                  +        _ => Some(s),
                  +    }) {
                  +        *s = s.replace("b", "aba");
                  +    }
                  +
                  +    // This code is equivalent to the `for` above.
                  +    // `for` is usually more idiomatic, but `for_each` is sometimes cleaner and sometimes faster.
                  +    strings
                  +        .iter_mut()
                  +        .map_while(|s| match s.as_str() {
                  +            "c" => None,
                  +            _ => Some(s),
                  +        })
                  +        .for_each(|s| *s = s.replace("b", "aba"));
                  +
                  +    // `into_iter()` is a method from `IntoIterator` trait that converts a collection to an iterator
                  +    let mut empty_strings_iter = strings.into_iter().map(|mut s| {
                  +        s.clear();
                  +        s
                  +    });
                  +
                  +    // This is a set of empty Strings...
                  +    let empty_strings_set = empty_strings_iter.clone().collect::<HashSet<_>>();
                  +
                  +    // And this is a Vec of immutable references to empty Strings.
                  +    let empty_string_refs_vec = empty_strings_set.iter().collect::<Vec<_>>();
                  +
                  +    // equivalent to `empty_string_refs_vec.into_iter()`
                  +    for s in empty_string_refs_vec {
                  +        println!("{}", s)
                  +    }
                  +
                  +    while let Some(s) = empty_strings_iter.next_back() {
                  +        assert!(s.is_empty());
                  +    }
                  +}
                  +
                  +
                  +

                  (Download the source code for this example: iterator_exhaustion.rs)

                  +

                  Iterators are highly optimised, so they are high-level code that compiles down to simple and optimised machine code (intended as zero-cost abstractions).

                  +

                  We'll go through the official docs.

                  +
                    +
                  • Most methods are defined in the Iterator trait.
                  • +
                  • IntoIterator is also worth noting, because it makes types work with the for loop.
                  • +
                  • For completeness, there is FromIterator, which is required for collect() to work.
                  • +
                  +

                  Reading

                  + +

                  Assignment 4 (graded)

                  +

                  Lazy

                  +

                  Deadline: 06.11.2024 23:59

                  + + +
                  +
                  + + + + +
                  + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/06-closures-iterators/iterator_exhaustion.rs b/lessons/06-closures-iterators/iterator_exhaustion.rs new file mode 100644 index 0000000..7374cd3 --- /dev/null +++ b/lessons/06-closures-iterators/iterator_exhaustion.rs @@ -0,0 +1,58 @@ +use std::collections::HashSet; + +fn main() { + // Various ways to create a String. + let mut strings = [ + String::new(), + String::from("a"), + "b".into(), + "c".to_owned(), + "d".to_string(), + "e".chars().collect(), + ]; + + // `iter()` is a usual method that creates an iterator over immutable references to the collection's items. + let _all_len_0_or_1 = strings + .iter() + .filter(|s| !s.is_empty()) + .all(|s| s.len() == 1); + + // `iter_mut()` is a usual method that creates an iterator over mutable references to the collection's items. + for s in strings.iter_mut().map_while(|s| match s.as_str() { + "c" => None, + _ => Some(s), + }) { + *s = s.replace("b", "aba"); + } + + // This code is equivalent to the `for` above. + // `for` is usually more idiomatic, but `for_each` is sometimes cleaner and sometimes faster. + strings + .iter_mut() + .map_while(|s| match s.as_str() { + "c" => None, + _ => Some(s), + }) + .for_each(|s| *s = s.replace("b", "aba")); + + // `into_iter()` is a method from `IntoIterator` trait that converts a collection to an iterator + let mut empty_strings_iter = strings.into_iter().map(|mut s| { + s.clear(); + s + }); + + // This is a set of empty Strings... + let empty_strings_set = empty_strings_iter.clone().collect::>(); + + // And this is a Vec of immutable references to empty Strings. + let empty_string_refs_vec = empty_strings_set.iter().collect::>(); + + // equivalent to `empty_string_refs_vec.into_iter()` + for s in empty_string_refs_vec { + println!("{}", s) + } + + while let Some(s) = empty_strings_iter.next_back() { + assert!(s.is_empty()); + } +} diff --git a/lessons/07-smart-pointers/box.rs b/lessons/07-smart-pointers/box.rs new file mode 100644 index 0000000..9a9f99e --- /dev/null +++ b/lessons/07-smart-pointers/box.rs @@ -0,0 +1,18 @@ +fn box_simple() { + let b = Box::new(5); + println!("b = {}", b); + + let _x = 10 + *b; +} + +// `Box` gives us the indirection required to define +// recursive types +#[allow(dead_code)] +enum List { + Cons(i32, Box), + Nil, +} + +fn main() { + box_simple(); +} diff --git a/lessons/07-smart-pointers/deref_coercion.rs b/lessons/07-smart-pointers/deref_coercion.rs new file mode 100644 index 0000000..c5b14c7 --- /dev/null +++ b/lessons/07-smart-pointers/deref_coercion.rs @@ -0,0 +1,39 @@ +use std::ops::Deref; + +struct MyBox(T); + +// We won't be allocating anything on the heap here as it is not important here. +// We're only focusing on the dereference mechanisms. +impl MyBox { + fn new(x: T) -> MyBox { + MyBox(x) + } +} + +impl Deref for MyBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn hello(name: &str) { + println!("Hello, {}!", name); +} + +fn main() { + let x = 5; + let int_box = MyBox::new(x); + + assert_eq!(5, *int_box); + + // String also implements the `Deref` trait. + // In fact, String actually is a smart pointer. + let s = String::from("I'm a smart pointer too"); + hello(&s); + + // Deref coercion can deal with multiple levels of indirection. + let str_box = MyBox::new(String::from("Rust")); + hello(&str_box); +} diff --git a/lessons/07-smart-pointers/index.html b/lessons/07-smart-pointers/index.html new file mode 100644 index 0000000..d8607c6 --- /dev/null +++ b/lessons/07-smart-pointers/index.html @@ -0,0 +1,417 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                  + + +
                  + +
                  + +
                  +
                  +
                    +
                    +
                    + +
                    + +

                    Smart Pointers

                    +

                    + 2024-11-07 (last edit: 2022-11-21) +

                    +

                    Working with the heap

                    +

                    So far we've only used heap allocated memory indirectly by working with containers such as vectors, maps or the String type, otherwise allocating our variables on the stack. We didn't really have to be aware of the fact that these collections used the heap, as all that memory management details were hidden away from us. In this lesson we'll take a closer look at what is really happening there and how we can do that ourselves.

                    +

                    To work with heap-allocated memory, Rust features smart pointers. You should have already heard this term as it is a very important feature in C++ and the concept is virtually the same here - they are wrappers around raw allocated memory that provide additional, safety-ensuring mechanism. What defines a smart pointer in Rust is generally the implementation of two traits: Drop and Deref.

                    +

                    The Drop trait is pretty straightforward as it consists of one method - fn drop(&mut self) - that is, basically, the destructor, invoked during stack unwinding.

                    +

                    The Deref trait allows us to overload the dereference (*) operator.

                    +

                    Deref coercion

                    +

                    Apart from enabling access to the underlying value, implementing the Deref trait enables Rust to perform deref coercion on the pointer - trying to remove as many levels of indirection as it can. What it means in practice is that we will be able to use it with any code working on plain references.

                    +
                    use std::ops::Deref;
                    +
                    +struct MyBox<T>(T);
                    +
                    +// We won't be allocating anything on the heap here as it is not important here.
                    +// We're only focusing on the dereference mechanisms.
                    +impl<T> MyBox<T> {
                    +    fn new(x: T) -> MyBox<T> {
                    +        MyBox(x)
                    +    }
                    +}
                    +
                    +impl<T> Deref for MyBox<T> {
                    +    type Target = T;
                    +
                    +    fn deref(&self) -> &Self::Target {
                    +        &self.0
                    +    }
                    +}
                    +
                    +fn hello(name: &str) {
                    +    println!("Hello, {}!", name);
                    +}
                    +
                    +fn main() {
                    +    let x = 5;
                    +    let int_box = MyBox::new(x);
                    +
                    +    assert_eq!(5, *int_box);
                    +
                    +    // String also implements the `Deref` trait.
                    +    // In fact, String actually is a smart pointer.
                    +    let s = String::from("I'm a smart pointer too");
                    +    hello(&s);
                    +
                    +    // Deref coercion can deal with multiple levels of indirection.
                    +    let str_box = MyBox::new(String::from("Rust"));
                    +    hello(&str_box);
                    +}
                    +
                    +
                    +

                    (Download the source code for this example: deref_coercion.rs)

                    +

                    In general, there are three possible coercions that Rust can perform:

                    +
                      +
                    • +

                      From &T to &U when T: Deref<Target=U>

                      +
                    • +
                    • +

                      From &mut T to &mut U when T: DerefMut<Target=U>

                      +
                    • +
                    • +

                      From &mut T to &U when T: Deref<Target=U>

                      +
                    • +
                    +

                    While the first two coercions are straightforward, the third one is possible because treating a mutable reference as an immutable one does not break the rules of ownership.

                    +

                    Box - simple wrapper

                    +

                    The Box<T> type is the most basic out of Rust's smart pointers, equivalent to C++'s std::unique_ptr<T>. It's a simple wrapper that makes sure the underlying memory gets allocated and freed properly.

                    +
                    fn box_simple() {
                    +    let b = Box::new(5);
                    +    println!("b = {}", b);
                    +
                    +    let _x = 10 + *b;
                    +}
                    +
                    +// `Box` gives us the indirection required to define
                    +// recursive types
                    +#[allow(dead_code)]
                    +enum List {
                    +    Cons(i32, Box<List>),
                    +    Nil,
                    +}
                    +
                    +fn main() {
                    +    box_simple();
                    +}
                    +
                    +
                    +

                    (Download the source code for this example: box.rs)

                    +

                    Reference counting

                    +

                    The Rc<T> type is the equivalent of std::shared_ptr<T> from C++. There is one caveat to this though - because we're creating multiple references to the same object, those references have to be immutable in accordance with the ownership rules.

                    +
                    use std::rc::Rc;
                    +
                    +struct LoudInt(i32);
                    +
                    +impl Drop for LoudInt {
                    +    fn drop(&mut self) {
                    +        println!("[{}] Farewell!", self.0);
                    +    }
                    +}
                    +
                    +fn main() {
                    +    {
                    +        let outer_ref;
                    +
                    +        {
                    +            let inner_ref = Rc::new(LoudInt(5));
                    +
                    +            // strong_count represents the number of owning references pointing
                    +            // to data
                    +            assert_eq!(Rc::strong_count(&inner_ref), 1);
                    +
                    +            outer_ref = Rc::clone(&inner_ref);
                    +
                    +            assert_eq!(Rc::strong_count(&inner_ref), Rc::strong_count(&outer_ref));
                    +            assert_eq!(Rc::strong_count(&inner_ref), 2);
                    +        }
                    +
                    +        println!("The {} still lives!", outer_ref.0);
                    +        assert_eq!(Rc::strong_count(&outer_ref), 1);
                    +    }
                    +}
                    +
                    +
                    +

                    (Download the source code for this example: ref_count.rs)

                    +

                    Rust also provides a non-owning pointer in the form of Weak<T> (equivalent to std::weak_ptr<T>) that can be obtained from an instance of Rc<T>.

                    +
                    use std::rc::Rc;
                    +
                    +struct LoudInt(i32);
                    +
                    +impl Drop for LoudInt {
                    +    fn drop(&mut self) {
                    +        println!("[{}] Farewell!", self.0);
                    +    }
                    +}
                    +
                    +fn main() {
                    +    let weak_ref;
                    +
                    +    {
                    +        let shared_ref = Rc::new(LoudInt(5));
                    +
                    +        // weak_count keeps track of the non-owning reference to the data
                    +        assert_eq!(Rc::weak_count(&shared_ref), 0);
                    +
                    +        // `downgrade()` obtains a weak pointer to Rc's data
                    +        weak_ref = Rc::downgrade(&shared_ref);
                    +
                    +        assert_eq!(Rc::weak_count(&shared_ref), 1);
                    +        assert_eq!(Rc::strong_count(&shared_ref), 1);
                    +
                    +        // In order to use the the data underneath the weak pointer
                    +        // we need to obtain a new shared pointer from it.
                    +        // The `upgrade()` method returns `Option<Rc<T>>`.
                    +        let temp = weak_ref.upgrade();
                    +        assert_eq!(Rc::strong_count(&shared_ref), 2);
                    +        println!("The value is {}", temp.unwrap().0);
                    +    }
                    +
                    +    println!("The value should be deallocated by now.");
                    +    assert!(weak_ref.upgrade().is_none());
                    +}
                    +
                    +
                    +

                    (Download the source code for this example: weak_ref.rs)

                    +

                    Mutating the immutable

                    +

                    Good examples and explanation of the interior mutability pattern and runtime borrow checking can be found in the book.

                    +

                    Alongside the RefCell<T> type described above, there is an analogous Cell<T> type that operates on values instead of references.

                    +

                    Convenient handling of dyn objects

                    +

                    In previous labs you learned about dynamic dispatch and its strengths. The largest drawback you noticed is most likely that they are unsized (!Sized, where ! being syntax signifying lack of trait implementation).

                    +

                    When storing an object on a heap, however, we can use it as a dyn object seamlessly.

                    +

                    Obligatory reading

                    + +

                    Additional reading

                    + +

                    Assignment 5 (graded)

                    +

                    Corporations

                    +

                    Deadline: 13.11.2024 23:59

                    + + +
                    +
                    + + + + +
                    + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/07-smart-pointers/ref_count.rs b/lessons/07-smart-pointers/ref_count.rs new file mode 100644 index 0000000..0f97932 --- /dev/null +++ b/lessons/07-smart-pointers/ref_count.rs @@ -0,0 +1,31 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + { + let outer_ref; + + { + let inner_ref = Rc::new(LoudInt(5)); + + // strong_count represents the number of owning references pointing + // to data + assert_eq!(Rc::strong_count(&inner_ref), 1); + + outer_ref = Rc::clone(&inner_ref); + + assert_eq!(Rc::strong_count(&inner_ref), Rc::strong_count(&outer_ref)); + assert_eq!(Rc::strong_count(&inner_ref), 2); + } + + println!("The {} still lives!", outer_ref.0); + assert_eq!(Rc::strong_count(&outer_ref), 1); + } +} diff --git a/lessons/07-smart-pointers/weak_ref.rs b/lessons/07-smart-pointers/weak_ref.rs new file mode 100644 index 0000000..6fa8e79 --- /dev/null +++ b/lessons/07-smart-pointers/weak_ref.rs @@ -0,0 +1,36 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + let weak_ref; + + { + let shared_ref = Rc::new(LoudInt(5)); + + // weak_count keeps track of the non-owning reference to the data + assert_eq!(Rc::weak_count(&shared_ref), 0); + + // `downgrade()` obtains a weak pointer to Rc's data + weak_ref = Rc::downgrade(&shared_ref); + + assert_eq!(Rc::weak_count(&shared_ref), 1); + assert_eq!(Rc::strong_count(&shared_ref), 1); + + // In order to use the the data underneath the weak pointer + // we need to obtain a new shared pointer from it. + // The `upgrade()` method returns `Option>`. + let temp = weak_ref.upgrade(); + assert_eq!(Rc::strong_count(&shared_ref), 2); + println!("The value is {}", temp.unwrap().0); + } + + println!("The value should be deallocated by now."); + assert!(weak_ref.upgrade().is_none()); +} diff --git a/lessons/08-feedback-2/index.html b/lessons/08-feedback-2/index.html new file mode 100644 index 0000000..927d4e1 --- /dev/null +++ b/lessons/08-feedback-2/index.html @@ -0,0 +1,256 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                    + + +
                    + +
                    + +
                    +
                    +
                      +
                      +
                      + +
                      + +

                      Feedback #2

                      +

                      + 2022-11-21 (last edit: 2022-11-21) +

                      +

                      Feedback

                      +

                      Conditional implementation

                      +
                      impl<const N: usize> Shape for SphereN<N> {
                      +    type Volume = VolumeN<N>;
                      +    fn volume(&self) -> Self::Volume {
                      +        let mut volume: u32 = (f64::from(self.radius) * f64::from(self.radius) * PI) as u32;
                      +        if N == 3 {
                      +            volume = (f64::from(self.radius)
                      +                * f64::from(self.radius)
                      +                * f64::from(self.radius)
                      +                * PI
                      +                * 4.0_f64
                      +                / 3.0_f64) as u32;
                      +        }
                      +        Self::Volume::new(volume)
                      +    }
                      +}
                      +
                      +

                      Instead of checking N == 3, you can provide different impls for SphereN<2> and +SphereN<3> (as they are different types).

                      +

                      u32 and u64

                      +

                      They are different types, but because you can easily cast one to another, +it was not sufficient to make the implementation type-safe.

                      + + +
                      +
                      + + + + +
                      + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/09-concurrency/index.html b/lessons/09-concurrency/index.html new file mode 100644 index 0000000..b3a7cc6 --- /dev/null +++ b/lessons/09-concurrency/index.html @@ -0,0 +1,389 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                      + + +
                      + +
                      + +
                      +
                      +
                        +
                        +
                        + +
                        + +

                        Fearless concurrency

                        +

                        + 2024-11-14 (last edit: 2024-11-14) +

                        +

                        Parallelism vs Concurrency

                        +

                        Concurrency is when tasks can make progress independently of each other.

                        +

                        Parallelism is when multiple tasks make progress at the same time.

                        +

                        Concurrency models in Rust

                        +

                        Threads

                        +

                        Nothing unusual here.

                        +

                        Threads can be created with the thread::spawn function docs - please read them!.

                        +

                        This method returns a JoinHandle<T> which can be used to wait for the thread to finish. T is the type of the thread's return value.

                        +

                        Another way to spawn threads is using scope. Threads created in such way are mandatorily joined at the end of the scope, which guarantees that they will borrow items for no longer that the lifetime of the scope. Hence, they can borrow non-'static items!

                        +

                        Propagating panics

                        +

                        In Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).

                        +

                        Closures

                        +

                        Closures which are used to create threads must take ownership of any values they use. It can be forced with the move keyword.

                        +
                        use std::thread;
                        +
                        +fn main() {
                        +    let v = vec![1, 2, 3];
                        +
                        +    let handle = thread::spawn(move || {
                        +        println!("Here's a vector: {:?}", v);
                        +    });
                        +
                        +    handle.join().unwrap();
                        +}
                        +
                        +

                        Normal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!

                        +

                        But what if we need to share some state?

                        +

                        Send and Sync

                        +

                        They are marker traits used to indicate that a type or a reference to it can be used across threads. See the nomicon for more information.

                        +
                        +
                          +
                        • A type is Send if it is safe to move it (send it) to another thread.
                        • +
                        • A type is Sync if it is safe to share (sync) between threads (T is Sync if and only if &T is Send).
                        • +
                        +
                        +

                        This makes sense, because Sync is about sharing object between threads, and & is the shared reference.

                        +

                        There is also a great answer on Rust forum, listing + explaining example types that are !Send or !Sync.

                        +

                        For more convenient analysis, examples are listed here:

                        +

                        Send + !Sync:

                        +
                          +
                        • UnsafeCell (=> Cell, RefCell);
                        • +
                        +

                        !Send + !Sync:

                        +
                          +
                        • Rc
                        • +
                        • *const T, *mut T (raw pointers)
                        • +
                        +

                        !Send + Sync:

                        +
                          +
                        • MutexGuard (hint: !Send for POSIX reasons)
                        • +
                        +

                        Exercise for the reader: explain reasons for all limitations of the above types.

                        +

                        Sharing state between threads

                        +

                        Message passing

                        +

                        One possible way is to use message passing. We can use a blocking queue (called mpsc - "multi producer single consumer FIFO queue") to do it. +We talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.

                        +

                        Mutexes

                        +

                        In Rust, a mutex wraps a value and makes it thread-safe. +Because it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the RefCell type.

                        +

                        Arc is a smart pointer like Rc but it can be shared between threads.

                        +

                        Please read more about them in the book.

                        +

                        The docs also mention poisoning.

                        +

                        RwLocks

                        +

                        RwLocks are similar to mutexes, but they distinguish between read and write locks.

                        +

                        Atomic types

                        +

                        Atomic types are described in the docs.

                        +
                        use std::sync::Arc;
                        +use std::sync::atomic::{AtomicUsize, Ordering};
                        +use std::{hint, thread};
                        +
                        +fn main() {
                        +    let spinlock = Arc::new(AtomicUsize::new(1));
                        +
                        +    let spinlock_clone = Arc::clone(&spinlock);
                        +    let thread = thread::spawn(move|| {
                        +        spinlock_clone.store(0, Ordering::SeqCst);
                        +    });
                        +
                        +    // Wait for the other thread to release the lock
                        +    while spinlock.load(Ordering::SeqCst) != 0 {
                        +        hint::spin_loop();
                        +    }
                        +
                        +    if let Err(panic) = thread.join() {
                        +        println!("Thread had an error: {:?}", panic);
                        +    }
                        +}
                        +
                        +

                        Note that atomic values don't have to be wrapped in a mutex when shared across threads.

                        +

                        Wait...

                        +

                        If most types are Sync + Send, then what stops us from using a standard, non-atomic integer in the example above?

                        +
                        let spinlock = Arc::new(1);
                        +
                        +let spinlock_clone = Arc::clone(&spinlock);
                        +let thread = thread::spawn(move|| {
                        +    *spinlock_clone += 1;
                        +});
                        +
                        +while *spinlock != 0 {
                        +    hint::spin_loop();
                        +}
                        +
                        +
                        error[E0594]: cannot assign to data in an `Arc`
                        + --> src/main.rs:9:9
                        +  |
                        +9 |         *spinlock_clone += 1;
                        +  |         ^^^^^^^^^^^^^^^^^^^^ cannot assign
                        +  |
                        +  = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<i32>`
                        +
                        +

                        ...so we would have to use a RefCell to be able to modify the value through a shared reference...

                        +
                        let spinlock = Arc::new(RefCell::new(1));
                        +
                        +let spinlock_clone = Arc::clone(&spinlock);
                        +let thread = thread::spawn(move|| {
                        +    *spinlock_clone.borrow_mut() += 1;
                        +});
                        +
                        +// Wait for the other thread to release the lock
                        +while *spinlock.borrow() != 0 {
                        +    hint::spin_loop();
                        +}
                        +
                        +

                        ...but RefCell isn't Sync:

                        +
                        error[E0277]: `RefCell<i32>` cannot be shared between threads safely
                        +   --> src/main.rs:9:18
                        +    |
                        +9   |     let thread = thread::spawn(move|| {
                        +    |                  ^^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
                        +    |
                        +    = help: the trait `Sync` is not implemented for `RefCell<i32>`
                        +    = note: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>`
                        +    = note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]`
                        +note: required by a bound in `spawn`
                        +
                        +

                        And that bound mentioned in the last line looks like this:

                        +
                        pub fn spawn<F, T>(f: F) -> JoinHandle<T> where
                        +    F: FnOnce() -> T,
                        +    F: Send + 'static,
                        +    T: Send + 'static,
                        +
                        +

                        Exercise for the reader

                        +

                        Why is it impossible to share a reference to a Mutex between threads spawned with std::thread::spawn?

                        +

                        Data parallelism with Rayon

                        +

                        Rayon is a library for parallelization of data processing. +It can be used to parallelize the execution of functions over a collection of data by switching the standard Iterator to a ParallelIterator. +It works very similar to Java's parallel streams.

                        +

                        Why do that? Because thread synchronization is hard! Rust prevents data races, but logical races and deadlocks are impossible to prevent!!

                        +

                        Rayon's FAQ is worth reading.

                        +

                        Reading

                        + +

                        No assignment this week

                        +

                        Please work on the first iteration of the big project instead.

                        + + +
                        +
                        + + + + +
                        + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/10-design-patterns/index.html b/lessons/10-design-patterns/index.html new file mode 100644 index 0000000..39eb103 --- /dev/null +++ b/lessons/10-design-patterns/index.html @@ -0,0 +1,246 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                        + + +
                        + +
                        + +
                        +
                        +
                          +
                          +
                          + +
                          + +

                          Design patterns

                          +

                          + 2024-11-21 (last edit: 2022-12-05) +

                          +

                          Object-oriented programming and Rust

                          +

                          The book has a chapter dedicated to it. +Especially the "typestate" pattern is very interesting. +You can read more about it here.

                          +

                          How to build a good library

                          +

                          These guidelines have been created by the Rust library team.

                          +

                          How to handle errors

                          +

                          This post is from 2020, but the libraries it mentions (anyhow and thiserror) are still the most popular.

                          +

                          Serde

                          +

                          Serde is the most popular serialization library for Rust.

                          +

                          Assignment

                          +

                          This week's assignment is to write a "distributed" calculator. +You should base your solution on the final project from the book.

                          + + +
                          +
                          + + + + +
                          + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/11-async-1/index.html b/lessons/11-async-1/index.html new file mode 100644 index 0000000..edf8ec1 --- /dev/null +++ b/lessons/11-async-1/index.html @@ -0,0 +1,240 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                          + + +
                          + +
                          + +
                          +
                          +
                            +
                            +
                            + +
                            + +

                            Async: Part 1

                            +

                            + 2024-11-28 (last edit: 2024-11-27) +

                            +

                            Tokio

                            +

                            We'll use the Tokio tutorial (chapters Overview-Channels).

                            +

                            Common Rust Lifetime Misconceptions

                            +

                            Please read this blogpost.

                            +

                            Assignment 6 (graded)

                            +

                            Calculator

                            +

                            Deadline: 04.12.2024 23:59

                            + + +
                            +
                            + + + + +
                            + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/12-project-feedback/index.html b/lessons/12-project-feedback/index.html new file mode 100644 index 0000000..6feaa23 --- /dev/null +++ b/lessons/12-project-feedback/index.html @@ -0,0 +1,396 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                            + + +
                            + +
                            + +
                            +
                            +
                              +
                              +
                              + +
                              + +

                              Project feedback

                              +

                              + 2022-12-29 (last edit: 2022-12-29) +

                              +

                              Project feedback

                              +

                              Unwrapping options/results

                              +

                              Always ask yourself twice if you really need to unwrap. In most cases, you don't have to. Use pattern matching instead, +as it provides a static guarantee that the value is present.

                              +

                              Pattern matching prevents you from writing code like this:

                              +
                              fn main() {
                              +    let x: Option<i32> = some_function();
                              +
                              +    if x.is_some() {
                              +        println!("x is {}", x.unwrap());
                              +    }
                              +
                              +    // Let's say this line was added later and/or you forgot to put it in the if statement.
                              +    do_something(x.unwrap()); // this will blow up if x == None!
                              +}
                              +
                              +

                              Instead, you can write:

                              +
                              fn main() {
                              +    let x: Option<i32> = some_function();
                              +
                              +    if let Some(x) = x {
                              +        println!("x is {}", x);
                              +        do_something(x);
                              +    }
                              +}
                              +
                              +

                              Question mark operator

                              +

                              In methods that return Result or Option, you can use the question mark operator to return early if the value is None or Err. +See: https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html

                              +

                              Logging

                              +

                              You can use the log crate to log messages. It's better than println! because it +can be easily turned off. It also allows you to use different severity levels (e.g. info, warn, error) and only +log messages above a certain level.

                              +

                              &String vs &str

                              +

                              See https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters +In general, if you want to pass a reference to a string, use &str instead of &String.

                              +

                              Use current versions of dependencies

                              +

                              You can use cargo upgrades to check for outdated dependencies.

                              +

                              If your project has separate binaries, use multiple binaries or a workspace

                              +

                              You can have multiple binaries in a single cargo project. Simply place them in the src/bin directory. +You can run them with cargo run --bin <name>. Alternatively, you can setup a +workspace.

                              +

                              Run clippy & cargo fmt

                              +

                              This should have become a habit by now. You can disable clippy warnings for a single item with #[allow(clippy::...)], +but in most cases you shouldn't do that.

                              +

                              If you need to escape characters in a string, use raw strings

                              +

                              See https://doc.rust-lang.org/reference/tokens.html#raw-string-literals

                              +

                              How to handle errors?

                              +

                              Short: https://kerkour.com/rust-error-handling

                              +

                              Long: https://www.lpalmieri.com/posts/error-handling-rust/

                              +

                              Don't pass around locked mutex's contents

                              +

                              If you have a mutex, you can use lock() to get a guard that will unlock the mutex when it goes out of scope. +But don't pass the contents of the guard to functions that can block (unless the mutex must be locked for +the entire duration of the function). +Instead of:

                              +
                              use std::sync::Mutex;
                              +use std::thread;
                              +use std::time::Duration;
                              +use std::time::Instant;
                              +
                              +fn handle_function(counter: &mut i32) {
                              +    thread::sleep(Duration::from_secs(1));
                              +    *counter += 1;
                              +    thread::sleep(Duration::from_secs(1));
                              +}
                              +
                              +fn main() {
                              +    let counter = Mutex::new(1);
                              +
                              +    thread::scope(|s| {
                              +        for i in 0..10 {
                              +            let counter = &counter;
                              +            s.spawn(move || {
                              +                println!("Thread {i} started");
                              +                let now = Instant::now();
                              +                let mut counter = counter.lock().unwrap();
                              +                handle_function(&mut counter); // lock is held for 2 seconds
                              +                println!("Thread {i} finished after {}s", now.elapsed().as_secs());
                              +            });
                              +        }
                              +    })
                              +}
                              +
                              +

                              You should do this:

                              +
                              use std::sync::Mutex;
                              +use std::thread;
                              +use std::time::Duration;
                              +use std::time::Instant;
                              +
                              +fn handle_function(counter: &Mutex<i32>) { // <-- changed
                              +    thread::sleep(Duration::from_secs(1));
                              +    {
                              +        let mut counter = counter.lock().unwrap(); // <-- changed
                              +        *counter += 1;
                              +        // lock is held only for the duration of the block
                              +        // it is important to create a new scope here, otherwise the lock would be held for another second
                              +    }
                              +    thread::sleep(Duration::from_secs(1));
                              +}
                              +
                              +fn main() {
                              +    let counter = Mutex::new(1);
                              +
                              +    thread::scope(|s| {
                              +        for i in 0..10 {
                              +            let counter = &counter;
                              +            s.spawn(move || {
                              +                println!("Thread {i} started");
                              +                let now = Instant::now();
                              +                handle_function(counter); // <-- changed! we don't lock here
                              +                println!("Thread {i} finished after {}s", now.elapsed().as_secs());
                              +            });
                              +        }
                              +    })
                              +}
                              +
                              +
                              +

                              Compare the output of the two programs. The first one will take 20 seconds to finish, while the second one will take 2 seconds.

                              +

                              First one:

                              +
                              Thread 1 started
                              +Thread 0 started
                              +Thread 2 started
                              +Thread 3 started
                              +Thread 4 started
                              +Thread 5 started
                              +Thread 6 started
                              +Thread 7 started
                              +Thread 8 started
                              +Thread 9 started
                              +Thread 1 finished after 2s
                              +Thread 0 finished after 4s
                              +Thread 2 finished after 6s
                              +Thread 3 finished after 8s
                              +Thread 4 finished after 10s
                              +Thread 5 finished after 12s
                              +Thread 6 finished after 14s
                              +Thread 7 finished after 16s
                              +Thread 8 finished after 18s
                              +Thread 9 finished after 20s
                              +
                              +
                              +

                              Second one:

                              +
                              Thread 0 started
                              +Thread 2 started
                              +Thread 1 started
                              +Thread 3 started
                              +Thread 4 started
                              +Thread 5 started
                              +Thread 6 started
                              +Thread 7 started
                              +Thread 8 started
                              +Thread 9 started
                              +Thread 1 finished after 2s
                              +Thread 2 finished after 2s
                              +Thread 0 finished after 2s
                              +Thread 3 finished after 2s
                              +Thread 4 finished after 2s
                              +Thread 5 finished after 2s
                              +Thread 6 finished after 2s
                              +Thread 7 finished after 2s
                              +Thread 8 finished after 2s
                              +Thread 9 finished after 2s
                              +
                              + + +
                              +
                              + + + + +
                              + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/13-async-2/index.html b/lessons/13-async-2/index.html new file mode 100644 index 0000000..d26f921 --- /dev/null +++ b/lessons/13-async-2/index.html @@ -0,0 +1,388 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                              + + +
                              + +
                              + +
                              +
                              +
                                +
                                +
                                + +
                                + +

                                Async: Part 2

                                +

                                + 2024-12-05 (last edit: 2022-05-23) +

                                +

                                Reinventing futures

                                +

                                We recently got our feet wet with the async/await functionality of Rust by using the Tokio library. With this basic understanding of what we expect out of futures, let's try to come up with their details ourselves.

                                +

                                We know that, when asked, a future can either give us a ready value or still be waiting for it. Asking about the future's result is called polling. Our future could look something like this:

                                +
                                trait SimpleFuture {
                                +    type Output;
                                +    fn poll(&mut self) -> Poll<Self::Output>;
                                +}
                                +
                                +enum Poll<T> {
                                +    Ready(T),
                                +    Pending,
                                +}
                                +
                                +

                                The poll method can be called to check for the result of the future. There is a flaw in this however - whatever is coordinating our future-based computations will have to constantly poll each of them in hope they are ready to do some work.

                                +
                                trait SimpleFuture {
                                +    type Output;
                                +    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
                                +}
                                +
                                +
                                +

                                We can solve this by attaching a callback to our polling. The wake function passed to poll can be used to notify whoever issued the poll that the future is ready to make some progress and should be polled.

                                +

                                Let's picture a quick example of how our SimpleFuture could be used.

                                +
                                pub struct SocketRead<'a> {
                                +    socket: &'a Socket,
                                +}
                                +
                                +impl SimpleFuture for SocketRead<'_> {
                                +    type Output = Vec<u8>;
                                +
                                +    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
                                +        if self.socket.has_data_to_read() {
                                +            // The socket has data -- read it into a buffer and return it.
                                +            Poll::Ready(self.socket.read_buf())
                                +        } else {
                                +            // The socket does not yet have data.
                                +            //
                                +            // Arrange for `wake` to be called once data is available.
                                +            // When data becomes available, `wake` will be called, and the
                                +            // user of this `Future` will know to call `poll` again and
                                +            // receive data.
                                +            self.socket.set_readable_callback(wake);
                                +            Poll::Pending
                                +        }
                                +    }
                                +}
                                +
                                +

                                Combining futures

                                +

                                With the SimpleFuture at our disposal we can easily model more advanced concurrent computations.

                                +
                                /// Concurrency is achieved via the fact that calls to `poll` each future
                                +/// may be interleaved, allowing each future to advance itself at its own pace.
                                +pub struct Join<FutureA, FutureB> {
                                +    // Each field may contain a future that should be run to completion.
                                +    // If the future has already completed, the field is set to `None`.
                                +    // This prevents us from polling a future after it has completed, which
                                +    // would violate the contract of the `Future` trait.
                                +    a: Option<FutureA>,
                                +    b: Option<FutureB>,
                                +}
                                +
                                +impl<FutureA, FutureB> SimpleFuture for Join<FutureA, FutureB>
                                +where
                                +    FutureA: SimpleFuture<Output = ()>,
                                +    FutureB: SimpleFuture<Output = ()>,
                                +{
                                +    type Output = ();
                                +    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
                                +        // Attempt to complete future `a`.
                                +        if let Some(a) = &mut self.a {
                                +            if let Poll::Ready(()) = a.poll(wake) {
                                +                self.a.take();
                                +            }
                                +        }
                                +
                                +        // Attempt to complete future `b`.
                                +        if let Some(b) = &mut self.b {
                                +            if let Poll::Ready(()) = b.poll(wake) {
                                +                self.b.take();
                                +            }
                                +        }
                                +
                                +        if self.a.is_none() && self.b.is_none() {
                                +            // Both futures have completed -- we can return successfully
                                +            Poll::Ready(())
                                +        } else {
                                +            // One or both futures returned `Poll::Pending` and still have
                                +            // work to do. They will call `wake()` when progress can be made.
                                +            Poll::Pending
                                +        }
                                +    }
                                +}
                                +
                                +

                                We can also queue futures like this:

                                +
                                pub struct AndThenFut<FutureA, FutureB> {
                                +    first: Option<FutureA>,
                                +    second: FutureB,
                                +}
                                +
                                +impl<FutureA, FutureB> SimpleFuture for AndThenFut<FutureA, FutureB>
                                +where
                                +    FutureA: SimpleFuture<Output = ()>,
                                +    FutureB: SimpleFuture<Output = ()>,
                                +{
                                +    type Output = ();
                                +    fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
                                +        if let Some(first) = &mut self.first {
                                +            match first.poll(wake) {
                                +                // We've completed the first future -- remove it and start on
                                +                // the second!
                                +                Poll::Ready(()) => self.first.take(),
                                +                // We couldn't yet complete the first future.
                                +                Poll::Pending => return Poll::Pending,
                                +            };
                                +        }
                                +        // Now that the first future is done, attempt to complete the second.
                                +        self.second.poll(wake)
                                +    }
                                +}
                                +
                                +

                                Exercise

                                +

                                The last example assumes that both futures are already constructed. In practice, however, we often want to chain futures that use the results of their predecessors, like this - get_breakfast().and_then(|food| eat(food));. Try implementing this behavior by adding a new method to the SimpleFuture trait called and_then and something that models this sequential computation (like the previous AndThenFut future).

                                +

                                The real deal

                                +

                                We weren't far from the actual way Rust's futures are structured. The Future trait looks as follows:

                                +
                                trait Future {
                                +    type Output;
                                +    fn poll(
                                +        // Note the change from `&mut self` to `Pin<&mut Self>`:
                                +        self: Pin<&mut Self>,
                                +        // and the change from `wake: fn()` to `cx: &mut Context<'_>`:
                                +        cx: &mut Context<'_>,
                                +    ) -> Poll<Self::Output>;
                                +}
                                +
                                +

                                There are two differences here. Firstly, we use a context instead of a standalone wake method. Since this callback was just a simple function pointer, there was no way for it to hold any data pertaining to which future called it. +Secondly, we take self as a Pin<>. This enables us to create immovable futures - we will go into it later.

                                +

                                Coordinating futures - waker & executor

                                +

                                Using wakers and context

                                +

                                We will follow the steps in the book to make a future that runs a separate thread that sleeps for a given duration and only then returns a result.

                                +

                                Executor

                                +

                                We will follow the steps in the book to create our own executor to run our futures on.

                                +

                                Obligatory reading

                                + +

                                Additional reading

                                + +

                                Assignment 7 (graded)

                                +

                                Calculator

                                +

                                Deadline: 18.12.2024 23:59

                                + + +
                                +
                                + + + + +
                                + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/14-macros/index.html b/lessons/14-macros/index.html new file mode 100644 index 0000000..a3d1702 --- /dev/null +++ b/lessons/14-macros/index.html @@ -0,0 +1,245 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                                + + +
                                + +
                                + +
                                +
                                +
                                  +
                                  +
                                  + +
                                  + +

                                  Macros

                                  +

                                  + 2024-12-12 (last edit: 2022-06-07) +

                                  +

                                  Reading

                                  + + + +
                                  +
                                  + + + + +
                                  + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/index.html b/lessons/index.html new file mode 100644 index 0000000..c0f6355 --- /dev/null +++ b/lessons/index.html @@ -0,0 +1,328 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/old/2021L/00-organizational/index.html b/lessons/old/2021L/00-organizational/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/00-organizational/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/01-introduction/clippy.jpg b/lessons/old/2021L/01-introduction/clippy.jpg new file mode 100644 index 0000000..cf490ba Binary files /dev/null and b/lessons/old/2021L/01-introduction/clippy.jpg differ diff --git a/lessons/old/2021L/01-introduction/conditionals.rs b/lessons/old/2021L/01-introduction/conditionals.rs new file mode 100644 index 0000000..441aba6 --- /dev/null +++ b/lessons/old/2021L/01-introduction/conditionals.rs @@ -0,0 +1,20 @@ +#![allow(unused_variables)] + +fn main() { + let x = 42; + + if x == 42 { + println!("x is 42"); + } else if x == 43 { + println!("x is 43"); + } else { + println!("x is not 42 or 43"); + } + + // we can also use ifs as expressions + let a_or_b = if x == 0 { + "a" // notice no semicolon at the end + } else { + "b" + }; +} diff --git a/lessons/old/2021L/01-introduction/errors_demo.c b/lessons/old/2021L/01-introduction/errors_demo.c new file mode 100644 index 0000000..936ac9c --- /dev/null +++ b/lessons/old/2021L/01-introduction/errors_demo.c @@ -0,0 +1,35 @@ +#include + +#define LENGTH 3 + +int main() { + //# Syntax error + // printf("hello world"; + + //char* array[LENGTH] = {"hello", "ancient", "world!"}; + + //# Array out-of-bounds with a statically known index + // printf("%s\n", array[LENGTH]); + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= LENGTH; i++) { + // printf("%s\n", array[i]); + // } + + //# Doing God knows what + // char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = 0 ? 1 : 0; + // int joy_division = 1/joy; + + // printf("joy division equals %d", joy_division); + + return 0; +} diff --git a/lessons/old/2021L/01-introduction/errors_demo.cpp b/lessons/old/2021L/01-introduction/errors_demo.cpp new file mode 100644 index 0000000..1c7d88b --- /dev/null +++ b/lessons/old/2021L/01-introduction/errors_demo.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +using std::cout; +using std::endl; + +int main() { + //# Syntax error + // cout < "hello world"; + + // constexpr int length = 3; + // std::array array = {"hello", "old", "world!"}; + + //# Array access with an out of bounds index and bounds checking during compile time + // cout << std::get(array) << endl; + + //# Array access with an out of bounds index and bounds checking during runtime + // cout << array.at(length) << endl; + + //# Most common access without any checks + // cout << array[length] << endl; + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= length; i++) { + // cout << array.at(i) << endl; + // } + + //# This will be there in Clang 14 ... + // std::string format = std::format("a very innocent hello {}"); + // cout << format << endl; + + //# ... but for now, this is doing God knows what + // const char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = false ? 1 : 0; + // int joy_division = 1/joy; + + // cout << "joy division equals " << joy_division << endl; + + return 0; +} diff --git a/lessons/old/2021L/01-introduction/errors_demo.rs b/lessons/old/2021L/01-introduction/errors_demo.rs new file mode 100644 index 0000000..bf5a394 --- /dev/null +++ b/lessons/old/2021L/01-introduction/errors_demo.rs @@ -0,0 +1,29 @@ +fn main() { + //# Syntax error + // println("hello world"); + + // let array = ["hello", "new", "world!"]; + + //# Array out-of-bounds with a statically known index + // println!("{}", array[3]); + + //# Array out-of-bounds with a dynamically computed index + // for i in 0..=array.len() { + // println!("{}", array[i]); + // } + + //# An unsuccessful attempt at emulating C++'s ability to read the memory we're not supposed to access + // let format = "a very innocent hello {}"; + // println!(format); + + //# Division by zero + // let joy_division = 0/0; + + // let joy = 0; + // let joy_division = 0/joy; + + // let joy = if false {1} else {0}; + // let joy_division = 0/joy; + + // println!("{}", joy_division); +} diff --git a/lessons/old/2021L/01-introduction/functions.rs b/lessons/old/2021L/01-introduction/functions.rs new file mode 100644 index 0000000..2987a36 --- /dev/null +++ b/lessons/old/2021L/01-introduction/functions.rs @@ -0,0 +1,12 @@ +fn get_5() -> u32 { + 5 // we could also write "return 5;" +} + +fn print_sum(a: u32, b: u32) { + println!("a + b = {}", a + b); +} + +fn main() { + let a = 100; + print_sum(a, get_5()); +} diff --git a/lessons/old/2021L/01-introduction/hello_world.rs b/lessons/old/2021L/01-introduction/hello_world.rs new file mode 100644 index 0000000..fa9fb84 --- /dev/null +++ b/lessons/old/2021L/01-introduction/hello_world.rs @@ -0,0 +1,4 @@ +fn main() { + let name = "World"; + println!("Hello, {}!", name); // using the println! macro +} diff --git a/lessons/old/2021L/01-introduction/index.html b/lessons/old/2021L/01-introduction/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/01-introduction/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/01-introduction/loops.rs b/lessons/old/2021L/01-introduction/loops.rs new file mode 100644 index 0000000..8a02d72 --- /dev/null +++ b/lessons/old/2021L/01-introduction/loops.rs @@ -0,0 +1,55 @@ +#![allow(unused_variables)] + +fn main() { + for i in 0..10 { + println!("i is {}", i); // i in [0, 10) + } + + let mut x = 0; + + while x < 50 { + x += 1; + } + + let mut y = 0; + let mut iterations = 0; + loop { + iterations += 1; + if iterations % 2 == 0 { + continue; + } + y += 1; + if y == 10 { + break; + } + } + + // we can use labels to refer to a specific loop + let mut count = 0; + 'counting_up: loop { + let mut remaining = 10; + + loop { + if remaining == 9 { + break; + } + if count == 2 { + break 'counting_up; // ends the outer loop + } + remaining -= 1; + } + + count += 1; + } + + // We can use break with a value. + // Because loops are expressions too, + // the value we break with will be returned from the functions + let mut counter = 0; + let value = loop { + counter += 1; + if counter == 10 { + break 32; + } + }; +} diff --git a/lessons/old/2021L/01-introduction/variables.rs b/lessons/old/2021L/01-introduction/variables.rs new file mode 100644 index 0000000..3350568 --- /dev/null +++ b/lessons/old/2021L/01-introduction/variables.rs @@ -0,0 +1,18 @@ +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn main() { + let x = 40; // inferred type + let y: i32 = 100; // specified type + + { + let x = 40 + 2; // shadowing + println!("x is {}", x); // prints 42 + } + + // x = 0; // compilation error, variables are by default immutable + let mut x = 40; // declare as mutable + x = 0; // now we can reassign + + x += 1; // x = x + 1 +} diff --git a/lessons/old/2021L/02-ownership/index.html b/lessons/old/2021L/02-ownership/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/02-ownership/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/03-data-types/data_types.rs b/lessons/old/2021L/03-data-types/data_types.rs new file mode 100644 index 0000000..78cd053 --- /dev/null +++ b/lessons/old/2021L/03-data-types/data_types.rs @@ -0,0 +1,82 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct Position(i32, i32); // tuple struct + +// Could Hero derive the Copy trait? +#[derive(Clone, Debug, Eq, PartialEq)] +struct Hero { + name: String, + level: u32, + experience: u32, + position: Position, +} + +// we can add methods to structs using the 'impl' keyword +impl Hero { + // static method + fn new(name: String) -> Hero { + Hero { + name, + level: 1, + experience: 0, + position: Position(0, 0), + } + } +} + +// multiple impl blocks are possible for one struct +impl Hero { + // instance method, first argument (self) is the calling instance + fn distance(&self, pos: Position) -> u32 { + // fields of tuples and tuple structs can be accessed through 'tuple.[i]' + (pos.0 - self.position.0).unsigned_abs() + (pos.1 - self.position.1).unsigned_abs() + } + + // mutable borrow of self allows to change instance fields + fn level_up(&mut self) { + self.experience = 0; + self.level += 1; + } + + // 'self' is not borrowed here and will be moved into the method + fn die(self) { + println!( + "Here lies {}, a hero who reached level {}. RIP.", + self.name, self.level + ); + } +} + +fn main() { + let mut hero: Hero = Hero::new(String::from("Marty The Brave")); + hero.level_up(); // 'self' is always passed implicitly + + // fields other than 'name' will be the same as in 'hero' + let steve = Hero { + name: String::from("Steve The Normal Guy"), + ..hero + }; + + assert_eq!(hero.level, steve.level); + + let mut twin = hero.clone(); + + // we can compare Hero objects because it derives the PartialEq trait + assert_eq!(hero, twin); + twin.level_up(); + assert_ne!(hero, twin); + hero.level_up(); + assert_eq!(hero, twin); + + // we can print out a the struct's debug string with '{:?}' + println!("print to stdout: {:?}", hero); + + hero.die(); // 'hero' is not usable after this invocation, see the method's definiton + + // the dbg! macro prints debug strings to stderr along with file and line number + dbg!("print to stderr: {}", twin); + + let pos = Position(42, 0); + let dist = steve.distance(pos); // no clone here as Position derives the Copy trait + println!("{:?}", pos); + assert_eq!(dist, 42); +} diff --git a/lessons/old/2021L/03-data-types/index.html b/lessons/old/2021L/03-data-types/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/03-data-types/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/04-enums/enums.c b/lessons/old/2021L/04-enums/enums.c new file mode 100644 index 0000000..9e60c40 --- /dev/null +++ b/lessons/old/2021L/04-enums/enums.c @@ -0,0 +1,29 @@ +#include + +enum shirt_size { + small, + medium, + large, + xlarge +}; + +void print_size(enum shirt_size size) { + printf("my size is "); + if (size == small) { + printf("small"); + } else if (size == medium) { + printf("medium"); + } else if (size == large) { + printf("large"); + } else if (size == xlarge) { + printf("xlarge"); + } else { + printf("unknown"); + } + printf("\n"); +} + +int main() { + enum shirt_size my_size = medium; + print_size(my_size); +} diff --git a/lessons/old/2021L/04-enums/enums.rs b/lessons/old/2021L/04-enums/enums.rs new file mode 100644 index 0000000..ceab783 --- /dev/null +++ b/lessons/old/2021L/04-enums/enums.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +#[derive(Debug)] +enum NamedSize { + Small, + Medium, + Large, + XL, +} + +#[derive(Debug)] +enum ShirtSize { + Named(NamedSize), + Numeric(u32), +} + +fn main() { + println!( + "Isn't it strange that some clothes' sizes are adjectives like {:?},", + ShirtSize::Named(NamedSize::Small) + ); + println!( + "but sometimes they are numbers like {:?}?", + ShirtSize::Numeric(42) + ); +} diff --git a/lessons/old/2021L/04-enums/index.html b/lessons/old/2021L/04-enums/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/04-enums/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/04-enums/option.rs b/lessons/old/2021L/04-enums/option.rs new file mode 100644 index 0000000..3c73877 --- /dev/null +++ b/lessons/old/2021L/04-enums/option.rs @@ -0,0 +1,112 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +fn main() { + let mut not_null: i32 = 42; + not_null = 43; + // not_null = None; // this won't compile because it's a different type! + + let mut nullable: Option = Some(42); + nullable = None; + nullable = Some(43); + + // such construction is rare, but possible + let mut double_nullable: Option> = Some(Some(42)); + // assert_ne!(double_nullable, Some(42)); // this won't even compile because it's a different type! + double_nullable = None; + double_nullable = Some(None); + + // None and Some(None) are different! + assert_ne!(double_nullable, None); + + // Now recall that division by 0 *panics* + // A panic is an unrecoverable error + // It is not an exception! + // And in Rust there are no exceptions, so there are no try/catch blocks + // Now let's imagine that we want to divide one number by another + fn divide(dividend: i32, divisor: i32) -> i32 { + dividend / divisor + } + + // We get the divisor from the user, so it can be 0 + // We want to handle this situation gracefully - we don't want to crash the program! + // We can do this by using the Option type + fn safe_divide(dividend: i32, divisor: i32) -> Option { + if divisor == 0 { + None + } else { + Some(dividend / divisor) + } + } + + // Fortunately, such a function is already included in the standard library + let number: i32 = 42; + // We need to specify the type explicitly + // because checked_div is implemented for all integer types + // and Rust won't know which type we want to use + assert_eq!(number.checked_div(2), Some(21)); + assert_eq!(number.checked_div(0), None); + + // Now let's imagine we search for a value in a vector + let numbers = vec![1, 2, 3, 4, 5]; + let three = numbers.iter().copied().find(|&x| x == 3); + assert_eq!(three, Some(3)); + let seven = numbers.iter().copied().find(|&x| x == 7); + assert_eq!(seven, None); + // We won't delve deeper into the details of how iterators work for now, + // but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust + + // Usually there are two kinds of methods: + // ones that will panic if the argument is incorrect, + // numbers[8]; // this will panic! + // and `checked` ones that return an Option + assert_eq!(numbers.get(8), None); + + // We can use `unwrap` to get the value out of an Option + // but we must be absolutely sure that the Option is Some, otherwise we'll get a panic + // numbers.get(8).unwrap(); // this will panic! + assert_eq!(numbers.get(8).copied().unwrap_or(0), 0); // or we can provide a default value + + // Usually instead of unwrapping we use pattern matching, we'll get to this in a minute + // but first let's see what else we can do with an option + let number: Option = Some(42); + // We can use `map` to transform the value inside an Option + let doubled = number.map(|x| x * 2); + assert_eq!(doubled, Some(84)); + // We can use flatten to reduce one level of nesting + let nested = Some(Some(42)); + assert_eq!(nested.flatten(), Some(42)); + // We can use `and_then` to chain multiple options + // This operation is called `flatmap` in some languages + let chained = number + .and_then(|x| x.checked_div(0)) + .and_then(|x| x.checked_div(2)); + assert_eq!(chained, None); + + // The last two things we'll cover here are `take` and `replace` + // They are important when dealing with non-Copy types + // `take` will return the value inside an Option and leave a None in its place + let mut option: Option = None; + // Again, we need to specify the type + // Even though we want to say that there is no value inside the Option, + // this absent value must have a concrete type! + assert_eq!(option.take(), None); + assert_eq!(option, None); + + let mut x = Some(2); + let y = x.take(); + assert_eq!(x, None); + assert_eq!(y, Some(2)); + + // `replace` can be used to swap the value inside an Option + let mut x = Some(2); + let old = x.replace(5); + assert_eq!(x, Some(5)); + assert_eq!(old, Some(2)); + + let mut x = None; + let old = x.replace(3); + assert_eq!(x, Some(3)); + assert_eq!(old, None); +} diff --git a/lessons/old/2021L/04-enums/pattern_matching.rs b/lessons/old/2021L/04-enums/pattern_matching.rs new file mode 100644 index 0000000..215a74a --- /dev/null +++ b/lessons/old/2021L/04-enums/pattern_matching.rs @@ -0,0 +1,128 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +fn main() { + // Pattern matching is basically a switch on steroids. + let number = rand::random::(); + match number % 7 { + 0 => println!("{number} is divisible by 7"), + 1 => println!("{number} is *almost* divisible by 7"), + _ => println!("{number} is not divisible by 7"), + } + + #[derive(Debug)] + enum Color { + Pink, + Brown, + Lime, + } + + let color = Color::Lime; + match color { + Color::Pink => println!("My favorite color!"), + _ => println!("Not my favorite color!"), // _ is a wildcard + // Rust will statically check that we covered all cases or included a default case. + } + + // We can also use pattern matching to match on multiple values. + match (color, number % 7) { + (Color::Pink, 0) => println!("My favorite color and number!"), + (Color::Pink, _) => println!("My favorite color!"), + (_, 0) => println!("My favorite number!"), + (_, _) => println!("Not my favorite color or number!"), + } + // (This is not special syntax, we're just pattern matching tuples.) + + // But we can also *destructure* the value + struct Human { + age: u8, + favorite_color: Color, + } + + let john = Human { + age: 42, + favorite_color: Color::Pink, + }; + + match &john { + Human { + age: 42, + favorite_color: Color::Pink, + } => println!("Okay, that's John!"), + Human { + favorite_color: Color::Pink, + .. + } => println!("Not John, but still his favorite color!"), + _ => println!("Somebody else?"), + } + + // Note two things: + // 1. Color is *not* Eq, so we can't use == to compare it, but pattern matching is fine. + // 2. We *borrowed* the value, so we can use it after the match. + + println!("John is {} years old and still kicking!", john.age); + + // To save some time, we can use `if let` to match against only one thing + // We could also use `while let ... {}` in the same way + if let Color::Pink = &john.favorite_color { + println!("He's also a man of great taste"); + } + + // We can match ranges... + match john.age { + 0..=12 => println!("John is a kid!"), + 13..=19 => println!("John is a teenager!"), + 20..=29 => println!("John is a young adult!"), + 30..=49 => println!("John is an adult!"), + 50..=69 => println!("John is mature!"), + _ => println!("John is old!"), + } + + // We can use match and capture the value at the same time. + match john.age { + age @ 0..=12 => println!("John is a kid, age {}", age), + age @ 13..=19 => println!("John is a teenager, age {}", age), + age @ 20..=29 => println!("John is a young adult, age {}", age), + age @ 30..=49 => println!("John is an adult, age {}", age), + age @ 50..=69 => println!("John is mature, age {}", age), + age => println!("John is old, age {}", age), + } + + // We can use guards to check for multiple conditions. + match john.age { + age @ 12..=19 if age % 2 == 1 => println!("John is an *odd* teenager, age {}", age), + age if age % 2 == 0 => println!("John is an *even* man, age {}", age), + _ => println!("John is normal"), + } + + // Finally, let's look at some references now + let reference: &i32 = &4; + + match reference { + &val => println!("Value under reference is: {}", val), + } + + // `ref` can be used to create a reference when destructuring + let Human { + age, + ref favorite_color, + } = john; + // `john` is still valid, because we borrowed using `ref` + if let Color::Pink = &john.favorite_color { + println!("John still has his color - {:?}!", favorite_color); + } + + let mut john = john; + + // `ref mut` borrows mutably + let Human { + age, + ref mut favorite_color, + } = john; + // We use `*` to dereference + *favorite_color = Color::Brown; + println!( + "Tastes do change with time and John likes {:?} now.", + john.favorite_color + ); +} diff --git a/lessons/old/2021L/04-enums/result.rs b/lessons/old/2021L/04-enums/result.rs new file mode 100644 index 0000000..ffd0797 --- /dev/null +++ b/lessons/old/2021L/04-enums/result.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::fs::File; +use std::io; +use std::io::Read; + +// Let's try reading from a file. +// Obviously this can fail. +fn first_try() -> io::Result { + let file = File::open("/dev/random"); + match file { + Ok(mut file) => { + // We got a file! + let mut buffer = vec![0; 128]; + // Matching each result quickly become tedious... + match file.read_exact(&mut buffer) { + Ok(_) => { + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) + } + Err(error) => Err(error), + } + } + Err(error) => { + Err(error) // This is needed in order to change the type from `io::Result` to `io::Result<()>` + } + } +} + +// The '?' operator allows us to return early in case of an error +// (it automatically converts the error type) +fn second_try(filename: &'static str) -> io::Result { + let mut file = File::open(filename)?; + let mut buffer = vec![0; 128]; + file.read_exact(&mut buffer)?; + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) +} + +fn main() { + let filenames = [ + "/dev/random", + "/dev/null", + "/dev/cpu", + "/dev/fuse", + "there_certainly_is_no_such_file", + ]; + for filename in filenames { + println!("Trying to read from '{}'", filename); + match second_try(filename) { + Ok(gibberish) => println!("{}", gibberish), + Err(error) => println!("Error: {}", error), + } + } +} diff --git a/lessons/old/2021L/04-enums/tagged_union.cpp b/lessons/old/2021L/04-enums/tagged_union.cpp new file mode 100644 index 0000000..ae07b6e --- /dev/null +++ b/lessons/old/2021L/04-enums/tagged_union.cpp @@ -0,0 +1,35 @@ +#include + +// Taken from: https://en.cppreference.com/w/cpp/language/union + +// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +// and three variant members (c, i, d) +struct S +{ + enum{CHAR, INT, DOUBLE} tag; + union + { + char c; + int i; + double d; + }; +}; + +void print_s(const S& s) +{ + switch(s.tag) + { + case S::CHAR: std::cout << s.c << '\n'; break; + case S::INT: std::cout << s.i << '\n'; break; + case S::DOUBLE: std::cout << s.d << '\n'; break; + } +} + +int main() +{ + S s = {S::CHAR, 'a'}; + print_s(s); + s.tag = S::INT; + s.i = 123; + print_s(s); +} diff --git a/lessons/old/2021L/05-tests/index.html b/lessons/old/2021L/05-tests/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/05-tests/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/05-tests/test.rs b/lessons/old/2021L/05-tests/test.rs new file mode 100644 index 0000000..e78bb7f --- /dev/null +++ b/lessons/old/2021L/05-tests/test.rs @@ -0,0 +1,30 @@ +// This function is going to be used only in the tests, so we add the `#[cfg(test)]` attribute. +// It means that it won't be compiled in the final executable. +#[cfg(test)] +fn return_42() -> i32 { + 42 +} + +fn frobnicate(x: i32) -> i32 { + println!("frobicating...!"); + x + 40 +} + +fn main() { + frobnicate(2); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + assert_eq!(return_42(), 42); + } + + #[test] + fn test_frobnicate() { + assert_eq!(frobnicate(2), 42); + } +} diff --git a/lessons/old/2021L/06-types-reasoning/basic_trait.rs b/lessons/old/2021L/06-types-reasoning/basic_trait.rs new file mode 100644 index 0000000..1c76202 --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/basic_trait.rs @@ -0,0 +1,38 @@ +#![allow(dead_code)] + +trait Summary { + fn summarize(&self) -> String; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Summary for NewsArticle { + fn summarize(&self) -> String { + format!("{}, by {} ({})", self.headline, self.author, self.location) + } +} + +struct Tweet { + username: String, + content: String, +} + +impl Summary for Tweet { + fn summarize(&self) -> String { + format!("{}: {}", self.username, self.content) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet.summarize()); +} diff --git a/lessons/old/2021L/06-types-reasoning/generic_largest.rs b/lessons/old/2021L/06-types-reasoning/generic_largest.rs new file mode 100644 index 0000000..7aa6a3b --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/generic_largest.rs @@ -0,0 +1,23 @@ +fn largest(list: &[T]) -> T { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/old/2021L/06-types-reasoning/generics.rs b/lessons/old/2021L/06-types-reasoning/generics.rs new file mode 100644 index 0000000..3198df0 --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/generics.rs @@ -0,0 +1,70 @@ +#![allow(dead_code)] + +use std::fmt::Debug; + +// generic enums +enum OurOption { + Some(T), + None, +} + +// generic structs +struct Tuple2 { + x: T, + y: U, +} + +// generic implementation +impl Tuple2 { + fn new(x: T, y: U) -> Self { + Self { x, y } + } +} + +struct Pair { + x: T, + y: T, +} + +// conditional implementation +impl Pair { + fn largest(&self) -> T { + if self.x > self.y { + self.x + } else { + self.y + } + } +} + +// alternative syntax +impl Pair +where + T: PartialOrd + Copy, +{ + fn smallest(&self) -> T { + if self.x < self.y { + self.x + } else { + self.y + } + } +} + +// Here information about the concrete underlying type is erased +// We can only either format or clone the result +fn cloning_machine(item: &(impl Clone + Debug)) -> impl Clone + Debug { + item.clone() +} + +fn main() { + let _opt = OurOption::Some(10); + + let _p1 = Tuple2 { x: 5, y: 10 }; + let _p2 = Tuple2::new(1, 2.5); + + let arr = [1, 2, 3]; + let arr2 = cloning_machine(&arr); + // arr2[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` + println!("{:?}", arr2) +} diff --git a/lessons/old/2021L/06-types-reasoning/generics_fun.rs b/lessons/old/2021L/06-types-reasoning/generics_fun.rs new file mode 100644 index 0000000..241c389 --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/generics_fun.rs @@ -0,0 +1,35 @@ +use std::fmt::{Display, Formatter}; + +trait DefaultishablyPrintable { + fn defaultish_print() + where + T: Display + Default, + { + println!("{}", T::default()) + } +} + +struct Foo; + +struct Bar; + +impl Display for Bar { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("this is a bar") + } +} + +impl Default for Bar { + fn default() -> Self { + Bar // well, we have no other choice + } +} + +impl DefaultishablyPrintable for Foo {} + +impl DefaultishablyPrintable for Foo {} + +fn main() { + >::defaultish_print(); + >::defaultish_print(); +} diff --git a/lessons/old/2021L/06-types-reasoning/index.html b/lessons/old/2021L/06-types-reasoning/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/06-types-reasoning/lifetimes_basic.rs b/lessons/old/2021L/06-types-reasoning/lifetimes_basic.rs new file mode 100644 index 0000000..a4eaa2b --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/lifetimes_basic.rs @@ -0,0 +1,27 @@ +fn longest<'a>(first: &'a str, second: &'a str) -> &'a str { + if first.len() > second.len() { + first + } else { + second + } +} + +fn main() { + let string1 = String::from("long string is long"); + + { + let string2 = String::from("xyz"); + let result = longest(string1.as_str(), string2.as_str()); + println!("The longest string is {}", result); + } + + // This doesn't compile - incorrect lifetimes + // + // let string1 = String::from("long string is long"); + // let result; + // { + // let string2 = String::from("xyz"); + // result = longest(string1.as_str(), string2.as_str()); + // } + // println!("The longest string is {}", result); +} diff --git a/lessons/old/2021L/06-types-reasoning/lifetimes_elision.rs b/lessons/old/2021L/06-types-reasoning/lifetimes_elision.rs new file mode 100644 index 0000000..9633909 --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/lifetimes_elision.rs @@ -0,0 +1,16 @@ +fn first_two(seq: &[u32]) -> &[u32] { + if seq.len() < 2 { + seq + } else { + &seq[..2] + } +} + +fn main() { + let seq = [1, 2, 3, 4]; + + println!( + "First two elements of the sequence: {:?}", + first_two(&seq[..]) + ); +} diff --git a/lessons/old/2021L/06-types-reasoning/non_generic.rs b/lessons/old/2021L/06-types-reasoning/non_generic.rs new file mode 100644 index 0000000..c983e3b --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/non_generic.rs @@ -0,0 +1,35 @@ +fn largest_i32(list: &[i32]) -> i32 { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn largest_char(list: &[char]) -> char { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest_i32(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest_char(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/old/2021L/06-types-reasoning/static_dynamic_dispatch.rs b/lessons/old/2021L/06-types-reasoning/static_dynamic_dispatch.rs new file mode 100644 index 0000000..2d9eeda --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/static_dynamic_dispatch.rs @@ -0,0 +1,47 @@ +trait Speak { + fn speak(&self) -> &'static str; +} + +struct Dog; + +impl Speak for Dog { + fn speak(&self) -> &'static str { + "Hau hau" // it's a Polish dog! + } +} + +struct Human; + +impl Speak for Human { + fn speak(&self) -> &'static str { + "Hello world" + } +} + +// It works like templates in C++ +// A different function will be generated for each T during compilation +// This process is called "monomorphization" +fn static_dispatch(speaking: &T) { + println!("{}!", speaking.speak()); +} + +// Only one copy of that function will exist in the compiled binary +fn dynamic_dispatch(speaking: &dyn Speak) { + println!("{}!", speaking.speak()); +} + +fn main() { + let dog = Dog; + let human = Human; + + static_dispatch(&dog); + static_dispatch(&human); + + dynamic_dispatch(&dog); + dynamic_dispatch(&human); + + // The observable behavior is identical + // Static dispatch in general is a bit faster, + // because there is no need to perform a "vtable lookup". + // But it can also result in bigger binary sizes. +} diff --git a/lessons/old/2021L/06-types-reasoning/trait_default.rs b/lessons/old/2021L/06-types-reasoning/trait_default.rs new file mode 100644 index 0000000..84ba1b3 --- /dev/null +++ b/lessons/old/2021L/06-types-reasoning/trait_default.rs @@ -0,0 +1,67 @@ +#![allow(dead_code)] + +struct Upload { + filename: String, +} + +#[allow(dead_code)] +struct Photo { + filename: String, + width: u32, + height: u32, +} + +trait Description { + fn describe(&self) -> String { + String::from("No description available.") + } +} + +// All default implementations +impl Description for Upload {} + +// Default implementations can be overwritten +impl Description for Photo { + fn describe(&self) -> String { + format!("{} ({} x {})", self.filename, self.width, self.height) + } +} + +// Default implementations can rely on methods with no defaults +trait Size { + fn width(&self) -> u32; + fn height(&self) -> u32; + + fn size(&self) -> u32 { + self.width() * self.height() + } +} + +impl Size for Photo { + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } + + // Using default impl of `size()` +} + +fn main() { + let upload = Upload { + filename: String::from("notes.txt"), + }; + + println!("Upload: {}", upload.describe()); + + let photo = Photo { + filename: String::from("stock_crustacean.png"), + width: 100, + height: 150, + }; + + println!("Photo: {}", photo.describe()); + println!("Size: {}", photo.size()); +} diff --git a/lessons/old/2021L/07-feedback/clippy.png b/lessons/old/2021L/07-feedback/clippy.png new file mode 100644 index 0000000..9d76971 Binary files /dev/null and b/lessons/old/2021L/07-feedback/clippy.png differ diff --git a/lessons/old/2021L/07-feedback/constructor.rs b/lessons/old/2021L/07-feedback/constructor.rs new file mode 100644 index 0000000..a7ec205 --- /dev/null +++ b/lessons/old/2021L/07-feedback/constructor.rs @@ -0,0 +1,34 @@ +mod one { + pub struct Point { + x: i32, + } + + impl Point { + pub fn new(x: i32) -> Point { + Point { x } + } + + pub fn x(&self) -> i32 { + self.x + } + } + + impl Default for Point { + fn default() -> Point { + Point { x: 10 } + } + } +} + +fn main() { + // won't compile, can't initialize private fields + // let p = one::Point { + // x: 1, + // }; + let p = one::Point::new(1); + // won't compile, x is private + // println!("{}", p.x); + println!("{}", p.x()); + let p = one::Point::default(); + println!("{}", p.x()); +} diff --git a/lessons/old/2021L/07-feedback/index.html b/lessons/old/2021L/07-feedback/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/07-feedback/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/07-feedback/number_conversions.rs b/lessons/old/2021L/07-feedback/number_conversions.rs new file mode 100644 index 0000000..9dd324b --- /dev/null +++ b/lessons/old/2021L/07-feedback/number_conversions.rs @@ -0,0 +1,9 @@ +fn main() { + let small_number: u32 = u32::MAX; + // dbg!(small_number + 1); // this will panic (in debug builds, in release build it will wrap) + assert_eq!(small_number as u8, 255); + assert_eq!(small_number as i8, -1); + assert_eq!(small_number as i64, 4294967295); + let converted: Result = small_number.try_into(); + assert!(converted.is_err()); +} diff --git a/lessons/old/2021L/07-feedback/rustfmt.png b/lessons/old/2021L/07-feedback/rustfmt.png new file mode 100644 index 0000000..a09e6ff Binary files /dev/null and b/lessons/old/2021L/07-feedback/rustfmt.png differ diff --git a/lessons/old/2021L/08-closures-iterators/index.html b/lessons/old/2021L/08-closures-iterators/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/08-closures-iterators/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/09-feedback2/combinators.rs b/lessons/old/2021L/09-feedback2/combinators.rs new file mode 100644 index 0000000..f82b7f7 --- /dev/null +++ b/lessons/old/2021L/09-feedback2/combinators.rs @@ -0,0 +1,37 @@ +fn main() { + let mut res: Result = Ok(5); + + // `map` allows us to transform the value inside `Ok()`, + // while leaving the `Err` untouched + assert_eq!(res.map(|v| v * v), Ok(25)); + + res = Err(5); + assert_eq!(res.map(|v| v * v), Err(5)); + + // With most combinators there are mirrored ones that work on `Err` + // variants instead of `Ok`s. + assert_eq!(res.map_err(|v| v * v), Err(25)); + + // We can swap an `Ok` value for a different one with `and()`. + // Analogously for `Err` and `or()`. + res = Ok(5); + assert_eq!(res.and(Ok(100)), Ok(100)); + + res = Err(5); + assert_eq!(res.and(Ok(100)), Err(5)); + + // `and_then()` and `or_else()` allow us to invoke functions + // only when the result is either an `Ok` or an `Err` respectively. + let sq = |x: i32| -> Result { Ok(x * x) }; + let err = |x: i32| -> Result { Err(x) }; + + assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16)); + assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4)); + assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2)); + assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3)); + + assert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2)); + assert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2)); + assert_eq!(Err(3).or_else(sq).or_else(err), Ok(9)); + assert_eq!(Err(3).or_else(err).or_else(err), Err(3)); +} \ No newline at end of file diff --git a/lessons/old/2021L/09-feedback2/index.html b/lessons/old/2021L/09-feedback2/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/09-feedback2/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/10-smart-pointers/box.rs b/lessons/old/2021L/10-smart-pointers/box.rs new file mode 100644 index 0000000..9a9f99e --- /dev/null +++ b/lessons/old/2021L/10-smart-pointers/box.rs @@ -0,0 +1,18 @@ +fn box_simple() { + let b = Box::new(5); + println!("b = {}", b); + + let _x = 10 + *b; +} + +// `Box` gives us the indirection required to define +// recursive types +#[allow(dead_code)] +enum List { + Cons(i32, Box), + Nil, +} + +fn main() { + box_simple(); +} diff --git a/lessons/old/2021L/10-smart-pointers/deref_coercion.rs b/lessons/old/2021L/10-smart-pointers/deref_coercion.rs new file mode 100644 index 0000000..c5b14c7 --- /dev/null +++ b/lessons/old/2021L/10-smart-pointers/deref_coercion.rs @@ -0,0 +1,39 @@ +use std::ops::Deref; + +struct MyBox(T); + +// We won't be allocating anything on the heap here as it is not important here. +// We're only focusing on the dereference mechanisms. +impl MyBox { + fn new(x: T) -> MyBox { + MyBox(x) + } +} + +impl Deref for MyBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn hello(name: &str) { + println!("Hello, {}!", name); +} + +fn main() { + let x = 5; + let int_box = MyBox::new(x); + + assert_eq!(5, *int_box); + + // String also implements the `Deref` trait. + // In fact, String actually is a smart pointer. + let s = String::from("I'm a smart pointer too"); + hello(&s); + + // Deref coercion can deal with multiple levels of indirection. + let str_box = MyBox::new(String::from("Rust")); + hello(&str_box); +} diff --git a/lessons/old/2021L/10-smart-pointers/index.html b/lessons/old/2021L/10-smart-pointers/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/10-smart-pointers/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/10-smart-pointers/ref_count.rs b/lessons/old/2021L/10-smart-pointers/ref_count.rs new file mode 100644 index 0000000..0f97932 --- /dev/null +++ b/lessons/old/2021L/10-smart-pointers/ref_count.rs @@ -0,0 +1,31 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + { + let outer_ref; + + { + let inner_ref = Rc::new(LoudInt(5)); + + // strong_count represents the number of owning references pointing + // to data + assert_eq!(Rc::strong_count(&inner_ref), 1); + + outer_ref = Rc::clone(&inner_ref); + + assert_eq!(Rc::strong_count(&inner_ref), Rc::strong_count(&outer_ref)); + assert_eq!(Rc::strong_count(&inner_ref), 2); + } + + println!("The {} still lives!", outer_ref.0); + assert_eq!(Rc::strong_count(&outer_ref), 1); + } +} diff --git a/lessons/old/2021L/10-smart-pointers/weak_ref.rs b/lessons/old/2021L/10-smart-pointers/weak_ref.rs new file mode 100644 index 0000000..b2406f6 --- /dev/null +++ b/lessons/old/2021L/10-smart-pointers/weak_ref.rs @@ -0,0 +1,36 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + let weak_ref; + + { + let shared_ref = Rc::new(LoudInt(5)); + + // weak_count keeps track of the non-owning reference to the data + assert_eq!(Rc::weak_count(&shared_ref), 0); + + // `downgrade()` obtains a weak pointer to Rc's data + weak_ref = Rc::downgrade(&shared_ref); + + assert_eq!(Rc::weak_count(&shared_ref), 1); + assert_eq!(Rc::strong_count(&shared_ref), 1); + + // In order to use the the data underneath the weak pointer + // we need to obtain a new shared pointer from it. + // The `upgrade()` method returns `Option>`. + let temp = weak_ref.upgrade(); + assert_eq!(Rc::strong_count(&shared_ref), 2); + println!("The value is {}", temp.unwrap().0); + } + + println!("The value should be deallocated by now."); + matches!(weak_ref.upgrade(), None); +} diff --git a/lessons/old/2021L/11-feedback3/index.html b/lessons/old/2021L/11-feedback3/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/11-feedback3/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/12-concurrency/index.html b/lessons/old/2021L/12-concurrency/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/12-concurrency/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/13-design-patterns/index.html b/lessons/old/2021L/13-design-patterns/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/13-design-patterns/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/14-async-1/index.html b/lessons/old/2021L/14-async-1/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/14-async-1/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/15-async-2/index.html b/lessons/old/2021L/15-async-2/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/15-async-2/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/16-macros/index.html b/lessons/old/2021L/16-macros/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/16-macros/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2021L/b1-rusty-graphs/index.html b/lessons/old/2021L/b1-rusty-graphs/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2021L/b1-rusty-graphs/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/00-organizational/index.html b/lessons/old/2022Z/00-organizational/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/00-organizational/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/01-introduction/clippy.jpg b/lessons/old/2022Z/01-introduction/clippy.jpg new file mode 100644 index 0000000..cf490ba Binary files /dev/null and b/lessons/old/2022Z/01-introduction/clippy.jpg differ diff --git a/lessons/old/2022Z/01-introduction/conditionals.rs b/lessons/old/2022Z/01-introduction/conditionals.rs new file mode 100644 index 0000000..441aba6 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/conditionals.rs @@ -0,0 +1,20 @@ +#![allow(unused_variables)] + +fn main() { + let x = 42; + + if x == 42 { + println!("x is 42"); + } else if x == 43 { + println!("x is 43"); + } else { + println!("x is not 42 or 43"); + } + + // we can also use ifs as expressions + let a_or_b = if x == 0 { + "a" // notice no semicolon at the end + } else { + "b" + }; +} diff --git a/lessons/old/2022Z/01-introduction/cpp_meme.jpg b/lessons/old/2022Z/01-introduction/cpp_meme.jpg new file mode 100644 index 0000000..4ec2e17 Binary files /dev/null and b/lessons/old/2022Z/01-introduction/cpp_meme.jpg differ diff --git a/lessons/old/2022Z/01-introduction/errors_demo.c b/lessons/old/2022Z/01-introduction/errors_demo.c new file mode 100644 index 0000000..936ac9c --- /dev/null +++ b/lessons/old/2022Z/01-introduction/errors_demo.c @@ -0,0 +1,35 @@ +#include + +#define LENGTH 3 + +int main() { + //# Syntax error + // printf("hello world"; + + //char* array[LENGTH] = {"hello", "ancient", "world!"}; + + //# Array out-of-bounds with a statically known index + // printf("%s\n", array[LENGTH]); + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= LENGTH; i++) { + // printf("%s\n", array[i]); + // } + + //# Doing God knows what + // char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = 0 ? 1 : 0; + // int joy_division = 1/joy; + + // printf("joy division equals %d", joy_division); + + return 0; +} diff --git a/lessons/old/2022Z/01-introduction/errors_demo.cpp b/lessons/old/2022Z/01-introduction/errors_demo.cpp new file mode 100644 index 0000000..1c7d88b --- /dev/null +++ b/lessons/old/2022Z/01-introduction/errors_demo.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +using std::cout; +using std::endl; + +int main() { + //# Syntax error + // cout < "hello world"; + + // constexpr int length = 3; + // std::array array = {"hello", "old", "world!"}; + + //# Array access with an out of bounds index and bounds checking during compile time + // cout << std::get(array) << endl; + + //# Array access with an out of bounds index and bounds checking during runtime + // cout << array.at(length) << endl; + + //# Most common access without any checks + // cout << array[length] << endl; + + //# Array out-of-bounds with a dynamically computed index + // for (int i = 0; i <= length; i++) { + // cout << array.at(i) << endl; + // } + + //# This will be there in Clang 14 ... + // std::string format = std::format("a very innocent hello {}"); + // cout << format << endl; + + //# ... but for now, this is doing God knows what + // const char* format = "a very innocent hello %s"; + // printf(format); + + //# Division by zero + // int joy_division = 1/0; + + // int joy = 0; + // int joy_division = 1/joy; + + // int joy = false ? 1 : 0; + // int joy_division = 1/joy; + + // cout << "joy division equals " << joy_division << endl; + + return 0; +} diff --git a/lessons/old/2022Z/01-introduction/errors_demo.rs b/lessons/old/2022Z/01-introduction/errors_demo.rs new file mode 100644 index 0000000..bf5a394 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/errors_demo.rs @@ -0,0 +1,29 @@ +fn main() { + //# Syntax error + // println("hello world"); + + // let array = ["hello", "new", "world!"]; + + //# Array out-of-bounds with a statically known index + // println!("{}", array[3]); + + //# Array out-of-bounds with a dynamically computed index + // for i in 0..=array.len() { + // println!("{}", array[i]); + // } + + //# An unsuccessful attempt at emulating C++'s ability to read the memory we're not supposed to access + // let format = "a very innocent hello {}"; + // println!(format); + + //# Division by zero + // let joy_division = 0/0; + + // let joy = 0; + // let joy_division = 0/joy; + + // let joy = if false {1} else {0}; + // let joy_division = 0/joy; + + // println!("{}", joy_division); +} diff --git a/lessons/old/2022Z/01-introduction/functions.rs b/lessons/old/2022Z/01-introduction/functions.rs new file mode 100644 index 0000000..2987a36 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/functions.rs @@ -0,0 +1,12 @@ +fn get_5() -> u32 { + 5 // we could also write "return 5;" +} + +fn print_sum(a: u32, b: u32) { + println!("a + b = {}", a + b); +} + +fn main() { + let a = 100; + print_sum(a, get_5()); +} diff --git a/lessons/old/2022Z/01-introduction/hello_world.rs b/lessons/old/2022Z/01-introduction/hello_world.rs new file mode 100644 index 0000000..fa9fb84 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/hello_world.rs @@ -0,0 +1,4 @@ +fn main() { + let name = "World"; + println!("Hello, {}!", name); // using the println! macro +} diff --git a/lessons/old/2022Z/01-introduction/index.html b/lessons/old/2022Z/01-introduction/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/01-introduction/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/01-introduction/loops.rs b/lessons/old/2022Z/01-introduction/loops.rs new file mode 100644 index 0000000..8a02d72 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/loops.rs @@ -0,0 +1,55 @@ +#![allow(unused_variables)] + +fn main() { + for i in 0..10 { + println!("i is {}", i); // i in [0, 10) + } + + let mut x = 0; + + while x < 50 { + x += 1; + } + + let mut y = 0; + let mut iterations = 0; + loop { + iterations += 1; + if iterations % 2 == 0 { + continue; + } + y += 1; + if y == 10 { + break; + } + } + + // we can use labels to refer to a specific loop + let mut count = 0; + 'counting_up: loop { + let mut remaining = 10; + + loop { + if remaining == 9 { + break; + } + if count == 2 { + break 'counting_up; // ends the outer loop + } + remaining -= 1; + } + + count += 1; + } + + // We can use break with a value. + // Because loops are expressions too, + // the value we break with will be returned from the functions + let mut counter = 0; + let value = loop { + counter += 1; + if counter == 10 { + break 32; + } + }; +} diff --git a/lessons/old/2022Z/01-introduction/students/gasinska.py b/lessons/old/2022Z/01-introduction/students/gasinska.py new file mode 100644 index 0000000..e4989f1 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/students/gasinska.py @@ -0,0 +1,107 @@ +# sample 1 - different ways of removing elements from the list while iterating +list1 = [1, 2, 3, 4] +for idx, item in enumerate(list1): + del item +list1 + +# [1, 2, 3, 4] + +list2 = [1, 2, 3, 4] +for idx, item in enumerate(list2): + list2.remove(item) +list2 + +# [2, 4] + +list3 = [1, 2, 3, 4] +for idx, item in enumerate(list3[:]): + list3.remove(item) +list3 + +# [] + +list4 = [1, 2, 3, 4] +for idx, item in enumerate(list4): + list4.pop(idx) +list4 + +# [2, 4] + +# sample 2 - string interning +a = "abc" +b = "abc" +a is b + +# True + +a = ''.join(['a', 'b', 'c']) +b = ''.join(['a', 'b', 'c']) +a is b + +# False + +a = "abc!" +b = "abc!" +a is b + +# False + +# sample 3 - chained operations +(False == False) in [False] + +# False + +False == (False in [False]) + +# False + +False == False in [False] # unexpected... + +# True + +# sample 4 - is operator +a = 256 +b = 256 +a is b + +# True + +a = 257 +b = 257 +a is b + +# False + +a, b = 257, 257 +a is b + +# True + +257 is 257 + +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# C:\Users\kgasinsk\AppData\Local\Temp\ipykernel_15776\331119389.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="? +# 257 is 257 + +# sample 5 - local variables +def f(trufel): + if trufel: + y = 1 + y += 1 + +f(True) # everything is fine + +f(False) # gives error: local variable 'y' referenced before assignment + +# --------------------------------------------------------------------------- +# UnboundLocalError Traceback (most recent call last) +# Input In [17], in () +# ----> 1 f(False) + +# Input In [15], in f(trufel) +# 3 if trufel: +# 4 y = 1 +# ----> 5 y += 1 + +# UnboundLocalError: local variable 'y' referenced before assignment \ No newline at end of file diff --git a/lessons/old/2022Z/01-introduction/students/grodzicki.py b/lessons/old/2022Z/01-introduction/students/grodzicki.py new file mode 100644 index 0000000..1454659 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/students/grodzicki.py @@ -0,0 +1,7 @@ +def add_contents(input_list, contents=[]): + for val in input_list: + contents.append(val) + return contents + +print(add_contents([1])) # [1] +print(add_contents([2])) # [1, 2] \ No newline at end of file diff --git a/lessons/old/2022Z/01-introduction/students/koszowski.go b/lessons/old/2022Z/01-introduction/students/koszowski.go new file mode 100644 index 0000000..2916f91 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/students/koszowski.go @@ -0,0 +1,88 @@ +// mutowalność jest wbudowana w język + +type S struct { + A string + B []string +} + +func main() { + x := S{"x-A", []string{"x-B"}} + y := x // copy the struct + y.A = "y-A" + y.B[0] = "y-B" + + fmt.Println(x, y) + // Outputs "{x-A [y-B]} {y-A [y-B]}" -- x was modified! +} + +// slices i kwestia append + +func doStuff(value []string) { + fmt.Printf("value=%v\n", value) + + value2 := value[:] + value2 = append(value2, "b") + fmt.Printf("value=%v, value2=%v\n", value, value2) + + value2[0] = "z" + fmt.Printf("value=%v, value2=%v\n", value, value2) +} + +func main() { + slice1 := []string{"a"} // length 1, capacity 1 + + doStuff(slice1) + // Output: + // value=[a] -- ok + // value=[a], value2=[a b] -- ok: value unchanged, value2 updated + // value=[a], value2=[z b] -- ok: value unchanged, value2 updated + + slice10 := make([]string, 1, 10) // length 1, capacity 10 + slice10[0] = "a" + + doStuff(slice10) + // Output: + // value=[a] -- ok + // value=[a], value2=[a b] -- ok: value unchanged, value2 updated + // value=[z], value2=[z b] -- WTF?!? value changed??? +} + +// error handling + +len, err := reader.Read(bytes) +if err != nil { + if err == io.EOF { + // All good, end of file + } else { + return err + } +} + + +// interfejs nil + +type Explodes interface { + Bang() + Boom() +} + +// Type Bomb implements Explodes +type Bomb struct {} +func (*Bomb) Bang() {} +func (Bomb) Boom() {} + +func main() { + var bomb *Bomb = nil + var explodes Explodes = bomb + println(bomb, explodes) // '0x0 (0x10a7060,0x0)' + if explodes != nil { + println("Not nil!") // 'Not nil!' What are we doing here?!?! + explodes.Bang() // works fine + explodes.Boom() // panic: value method main.Bomb.Boom called using nil *Bomb pointer + } else { + println("nil!") // why don't we end up here? + } +} + +// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{} +// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany. \ No newline at end of file diff --git a/lessons/old/2022Z/01-introduction/students/tudruj.cpp b/lessons/old/2022Z/01-introduction/students/tudruj.cpp new file mode 100644 index 0000000..a5e48fd --- /dev/null +++ b/lessons/old/2022Z/01-introduction/students/tudruj.cpp @@ -0,0 +1,28 @@ +#include +#include + +using name = std::string; +using age = int; +using person = std::pair; +using address = std::string; +using address_book = std::unordered_map; + +void print_address_book(const address_book &book) +{ + for (const auto &[person, address] : book) + { + std::cout << person.first << " is " << person.second << " years old and lives at " << address << std::endl; + } +} + +int main() +{ + + address_book people{}; + people.insert({{"John", 20}, "221B Baker Street, London"}); + people.insert({{"Mary", 30}, "Avenue des Champs-Élysées, Paris"}); + people.insert({{"Jack", 73}, "Wall Street, New York"}); + print_address_book(people); + + return 0; +} diff --git a/lessons/old/2022Z/01-introduction/variables.rs b/lessons/old/2022Z/01-introduction/variables.rs new file mode 100644 index 0000000..3350568 --- /dev/null +++ b/lessons/old/2022Z/01-introduction/variables.rs @@ -0,0 +1,18 @@ +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn main() { + let x = 40; // inferred type + let y: i32 = 100; // specified type + + { + let x = 40 + 2; // shadowing + println!("x is {}", x); // prints 42 + } + + // x = 0; // compilation error, variables are by default immutable + let mut x = 40; // declare as mutable + x = 0; // now we can reassign + + x += 1; // x = x + 1 +} diff --git a/lessons/old/2022Z/02-ownership/index.html b/lessons/old/2022Z/02-ownership/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/02-ownership/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/03-data-types/data_types.rs b/lessons/old/2022Z/03-data-types/data_types.rs new file mode 100644 index 0000000..78cd053 --- /dev/null +++ b/lessons/old/2022Z/03-data-types/data_types.rs @@ -0,0 +1,82 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct Position(i32, i32); // tuple struct + +// Could Hero derive the Copy trait? +#[derive(Clone, Debug, Eq, PartialEq)] +struct Hero { + name: String, + level: u32, + experience: u32, + position: Position, +} + +// we can add methods to structs using the 'impl' keyword +impl Hero { + // static method + fn new(name: String) -> Hero { + Hero { + name, + level: 1, + experience: 0, + position: Position(0, 0), + } + } +} + +// multiple impl blocks are possible for one struct +impl Hero { + // instance method, first argument (self) is the calling instance + fn distance(&self, pos: Position) -> u32 { + // fields of tuples and tuple structs can be accessed through 'tuple.[i]' + (pos.0 - self.position.0).unsigned_abs() + (pos.1 - self.position.1).unsigned_abs() + } + + // mutable borrow of self allows to change instance fields + fn level_up(&mut self) { + self.experience = 0; + self.level += 1; + } + + // 'self' is not borrowed here and will be moved into the method + fn die(self) { + println!( + "Here lies {}, a hero who reached level {}. RIP.", + self.name, self.level + ); + } +} + +fn main() { + let mut hero: Hero = Hero::new(String::from("Marty The Brave")); + hero.level_up(); // 'self' is always passed implicitly + + // fields other than 'name' will be the same as in 'hero' + let steve = Hero { + name: String::from("Steve The Normal Guy"), + ..hero + }; + + assert_eq!(hero.level, steve.level); + + let mut twin = hero.clone(); + + // we can compare Hero objects because it derives the PartialEq trait + assert_eq!(hero, twin); + twin.level_up(); + assert_ne!(hero, twin); + hero.level_up(); + assert_eq!(hero, twin); + + // we can print out a the struct's debug string with '{:?}' + println!("print to stdout: {:?}", hero); + + hero.die(); // 'hero' is not usable after this invocation, see the method's definiton + + // the dbg! macro prints debug strings to stderr along with file and line number + dbg!("print to stderr: {}", twin); + + let pos = Position(42, 0); + let dist = steve.distance(pos); // no clone here as Position derives the Copy trait + println!("{:?}", pos); + assert_eq!(dist, 42); +} diff --git a/lessons/old/2022Z/03-data-types/enums.c b/lessons/old/2022Z/03-data-types/enums.c new file mode 100644 index 0000000..9e60c40 --- /dev/null +++ b/lessons/old/2022Z/03-data-types/enums.c @@ -0,0 +1,29 @@ +#include + +enum shirt_size { + small, + medium, + large, + xlarge +}; + +void print_size(enum shirt_size size) { + printf("my size is "); + if (size == small) { + printf("small"); + } else if (size == medium) { + printf("medium"); + } else if (size == large) { + printf("large"); + } else if (size == xlarge) { + printf("xlarge"); + } else { + printf("unknown"); + } + printf("\n"); +} + +int main() { + enum shirt_size my_size = medium; + print_size(my_size); +} diff --git a/lessons/old/2022Z/03-data-types/enums.rs b/lessons/old/2022Z/03-data-types/enums.rs new file mode 100644 index 0000000..ceab783 --- /dev/null +++ b/lessons/old/2022Z/03-data-types/enums.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +#[derive(Debug)] +enum NamedSize { + Small, + Medium, + Large, + XL, +} + +#[derive(Debug)] +enum ShirtSize { + Named(NamedSize), + Numeric(u32), +} + +fn main() { + println!( + "Isn't it strange that some clothes' sizes are adjectives like {:?},", + ShirtSize::Named(NamedSize::Small) + ); + println!( + "but sometimes they are numbers like {:?}?", + ShirtSize::Numeric(42) + ); +} diff --git a/lessons/old/2022Z/03-data-types/index.html b/lessons/old/2022Z/03-data-types/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/03-data-types/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/03-data-types/option.rs b/lessons/old/2022Z/03-data-types/option.rs new file mode 100644 index 0000000..3c73877 --- /dev/null +++ b/lessons/old/2022Z/03-data-types/option.rs @@ -0,0 +1,112 @@ +#![allow(unused_assignments)] +#![allow(unused_variables)] +#![allow(dead_code)] + +fn main() { + let mut not_null: i32 = 42; + not_null = 43; + // not_null = None; // this won't compile because it's a different type! + + let mut nullable: Option = Some(42); + nullable = None; + nullable = Some(43); + + // such construction is rare, but possible + let mut double_nullable: Option> = Some(Some(42)); + // assert_ne!(double_nullable, Some(42)); // this won't even compile because it's a different type! + double_nullable = None; + double_nullable = Some(None); + + // None and Some(None) are different! + assert_ne!(double_nullable, None); + + // Now recall that division by 0 *panics* + // A panic is an unrecoverable error + // It is not an exception! + // And in Rust there are no exceptions, so there are no try/catch blocks + // Now let's imagine that we want to divide one number by another + fn divide(dividend: i32, divisor: i32) -> i32 { + dividend / divisor + } + + // We get the divisor from the user, so it can be 0 + // We want to handle this situation gracefully - we don't want to crash the program! + // We can do this by using the Option type + fn safe_divide(dividend: i32, divisor: i32) -> Option { + if divisor == 0 { + None + } else { + Some(dividend / divisor) + } + } + + // Fortunately, such a function is already included in the standard library + let number: i32 = 42; + // We need to specify the type explicitly + // because checked_div is implemented for all integer types + // and Rust won't know which type we want to use + assert_eq!(number.checked_div(2), Some(21)); + assert_eq!(number.checked_div(0), None); + + // Now let's imagine we search for a value in a vector + let numbers = vec![1, 2, 3, 4, 5]; + let three = numbers.iter().copied().find(|&x| x == 3); + assert_eq!(three, Some(3)); + let seven = numbers.iter().copied().find(|&x| x == 7); + assert_eq!(seven, None); + // We won't delve deeper into the details of how iterators work for now, + // but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust + + // Usually there are two kinds of methods: + // ones that will panic if the argument is incorrect, + // numbers[8]; // this will panic! + // and `checked` ones that return an Option + assert_eq!(numbers.get(8), None); + + // We can use `unwrap` to get the value out of an Option + // but we must be absolutely sure that the Option is Some, otherwise we'll get a panic + // numbers.get(8).unwrap(); // this will panic! + assert_eq!(numbers.get(8).copied().unwrap_or(0), 0); // or we can provide a default value + + // Usually instead of unwrapping we use pattern matching, we'll get to this in a minute + // but first let's see what else we can do with an option + let number: Option = Some(42); + // We can use `map` to transform the value inside an Option + let doubled = number.map(|x| x * 2); + assert_eq!(doubled, Some(84)); + // We can use flatten to reduce one level of nesting + let nested = Some(Some(42)); + assert_eq!(nested.flatten(), Some(42)); + // We can use `and_then` to chain multiple options + // This operation is called `flatmap` in some languages + let chained = number + .and_then(|x| x.checked_div(0)) + .and_then(|x| x.checked_div(2)); + assert_eq!(chained, None); + + // The last two things we'll cover here are `take` and `replace` + // They are important when dealing with non-Copy types + // `take` will return the value inside an Option and leave a None in its place + let mut option: Option = None; + // Again, we need to specify the type + // Even though we want to say that there is no value inside the Option, + // this absent value must have a concrete type! + assert_eq!(option.take(), None); + assert_eq!(option, None); + + let mut x = Some(2); + let y = x.take(); + assert_eq!(x, None); + assert_eq!(y, Some(2)); + + // `replace` can be used to swap the value inside an Option + let mut x = Some(2); + let old = x.replace(5); + assert_eq!(x, Some(5)); + assert_eq!(old, Some(2)); + + let mut x = None; + let old = x.replace(3); + assert_eq!(x, Some(3)); + assert_eq!(old, None); +} diff --git a/lessons/old/2022Z/03-data-types/pattern_matching.rs b/lessons/old/2022Z/03-data-types/pattern_matching.rs new file mode 100644 index 0000000..215a74a --- /dev/null +++ b/lessons/old/2022Z/03-data-types/pattern_matching.rs @@ -0,0 +1,128 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +fn main() { + // Pattern matching is basically a switch on steroids. + let number = rand::random::(); + match number % 7 { + 0 => println!("{number} is divisible by 7"), + 1 => println!("{number} is *almost* divisible by 7"), + _ => println!("{number} is not divisible by 7"), + } + + #[derive(Debug)] + enum Color { + Pink, + Brown, + Lime, + } + + let color = Color::Lime; + match color { + Color::Pink => println!("My favorite color!"), + _ => println!("Not my favorite color!"), // _ is a wildcard + // Rust will statically check that we covered all cases or included a default case. + } + + // We can also use pattern matching to match on multiple values. + match (color, number % 7) { + (Color::Pink, 0) => println!("My favorite color and number!"), + (Color::Pink, _) => println!("My favorite color!"), + (_, 0) => println!("My favorite number!"), + (_, _) => println!("Not my favorite color or number!"), + } + // (This is not special syntax, we're just pattern matching tuples.) + + // But we can also *destructure* the value + struct Human { + age: u8, + favorite_color: Color, + } + + let john = Human { + age: 42, + favorite_color: Color::Pink, + }; + + match &john { + Human { + age: 42, + favorite_color: Color::Pink, + } => println!("Okay, that's John!"), + Human { + favorite_color: Color::Pink, + .. + } => println!("Not John, but still his favorite color!"), + _ => println!("Somebody else?"), + } + + // Note two things: + // 1. Color is *not* Eq, so we can't use == to compare it, but pattern matching is fine. + // 2. We *borrowed* the value, so we can use it after the match. + + println!("John is {} years old and still kicking!", john.age); + + // To save some time, we can use `if let` to match against only one thing + // We could also use `while let ... {}` in the same way + if let Color::Pink = &john.favorite_color { + println!("He's also a man of great taste"); + } + + // We can match ranges... + match john.age { + 0..=12 => println!("John is a kid!"), + 13..=19 => println!("John is a teenager!"), + 20..=29 => println!("John is a young adult!"), + 30..=49 => println!("John is an adult!"), + 50..=69 => println!("John is mature!"), + _ => println!("John is old!"), + } + + // We can use match and capture the value at the same time. + match john.age { + age @ 0..=12 => println!("John is a kid, age {}", age), + age @ 13..=19 => println!("John is a teenager, age {}", age), + age @ 20..=29 => println!("John is a young adult, age {}", age), + age @ 30..=49 => println!("John is an adult, age {}", age), + age @ 50..=69 => println!("John is mature, age {}", age), + age => println!("John is old, age {}", age), + } + + // We can use guards to check for multiple conditions. + match john.age { + age @ 12..=19 if age % 2 == 1 => println!("John is an *odd* teenager, age {}", age), + age if age % 2 == 0 => println!("John is an *even* man, age {}", age), + _ => println!("John is normal"), + } + + // Finally, let's look at some references now + let reference: &i32 = &4; + + match reference { + &val => println!("Value under reference is: {}", val), + } + + // `ref` can be used to create a reference when destructuring + let Human { + age, + ref favorite_color, + } = john; + // `john` is still valid, because we borrowed using `ref` + if let Color::Pink = &john.favorite_color { + println!("John still has his color - {:?}!", favorite_color); + } + + let mut john = john; + + // `ref mut` borrows mutably + let Human { + age, + ref mut favorite_color, + } = john; + // We use `*` to dereference + *favorite_color = Color::Brown; + println!( + "Tastes do change with time and John likes {:?} now.", + john.favorite_color + ); +} diff --git a/lessons/old/2022Z/03-data-types/result.rs b/lessons/old/2022Z/03-data-types/result.rs new file mode 100644 index 0000000..ffd0797 --- /dev/null +++ b/lessons/old/2022Z/03-data-types/result.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::fs::File; +use std::io; +use std::io::Read; + +// Let's try reading from a file. +// Obviously this can fail. +fn first_try() -> io::Result { + let file = File::open("/dev/random"); + match file { + Ok(mut file) => { + // We got a file! + let mut buffer = vec![0; 128]; + // Matching each result quickly become tedious... + match file.read_exact(&mut buffer) { + Ok(_) => { + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) + } + Err(error) => Err(error), + } + } + Err(error) => { + Err(error) // This is needed in order to change the type from `io::Result` to `io::Result<()>` + } + } +} + +// The '?' operator allows us to return early in case of an error +// (it automatically converts the error type) +fn second_try(filename: &'static str) -> io::Result { + let mut file = File::open(filename)?; + let mut buffer = vec![0; 128]; + file.read_exact(&mut buffer)?; + let gibberish = String::from_utf8_lossy(&buffer); + Ok(gibberish.to_string()) +} + +fn main() { + let filenames = [ + "/dev/random", + "/dev/null", + "/dev/cpu", + "/dev/fuse", + "there_certainly_is_no_such_file", + ]; + for filename in filenames { + println!("Trying to read from '{}'", filename); + match second_try(filename) { + Ok(gibberish) => println!("{}", gibberish), + Err(error) => println!("Error: {}", error), + } + } +} diff --git a/lessons/old/2022Z/03-data-types/tagged_union.cpp b/lessons/old/2022Z/03-data-types/tagged_union.cpp new file mode 100644 index 0000000..ae07b6e --- /dev/null +++ b/lessons/old/2022Z/03-data-types/tagged_union.cpp @@ -0,0 +1,35 @@ +#include + +// Taken from: https://en.cppreference.com/w/cpp/language/union + +// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE), +// and three variant members (c, i, d) +struct S +{ + enum{CHAR, INT, DOUBLE} tag; + union + { + char c; + int i; + double d; + }; +}; + +void print_s(const S& s) +{ + switch(s.tag) + { + case S::CHAR: std::cout << s.c << '\n'; break; + case S::INT: std::cout << s.i << '\n'; break; + case S::DOUBLE: std::cout << s.d << '\n'; break; + } +} + +int main() +{ + S s = {S::CHAR, 'a'}; + print_s(s); + s.tag = S::INT; + s.i = 123; + print_s(s); +} diff --git a/lessons/old/2022Z/04-feedback-1/index.html b/lessons/old/2022Z/04-feedback-1/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/04-feedback-1/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/05-types-reasoning/basic_trait.rs b/lessons/old/2022Z/05-types-reasoning/basic_trait.rs new file mode 100644 index 0000000..1c76202 --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/basic_trait.rs @@ -0,0 +1,38 @@ +#![allow(dead_code)] + +trait Summary { + fn summarize(&self) -> String; +} + +struct NewsArticle { + headline: String, + location: String, + author: String, + content: String, +} + +impl Summary for NewsArticle { + fn summarize(&self) -> String { + format!("{}, by {} ({})", self.headline, self.author, self.location) + } +} + +struct Tweet { + username: String, + content: String, +} + +impl Summary for Tweet { + fn summarize(&self) -> String { + format!("{}: {}", self.username, self.content) + } +} + +fn main() { + let tweet = Tweet { + username: String::from("horse_ebooks"), + content: String::from("of course, as you probably already know, people"), + }; + + println!("1 new tweet: {}", tweet.summarize()); +} diff --git a/lessons/old/2022Z/05-types-reasoning/generic_largest.rs b/lessons/old/2022Z/05-types-reasoning/generic_largest.rs new file mode 100644 index 0000000..7aa6a3b --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/generic_largest.rs @@ -0,0 +1,23 @@ +fn largest(list: &[T]) -> T { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/old/2022Z/05-types-reasoning/generics.rs b/lessons/old/2022Z/05-types-reasoning/generics.rs new file mode 100644 index 0000000..3198df0 --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/generics.rs @@ -0,0 +1,70 @@ +#![allow(dead_code)] + +use std::fmt::Debug; + +// generic enums +enum OurOption { + Some(T), + None, +} + +// generic structs +struct Tuple2 { + x: T, + y: U, +} + +// generic implementation +impl Tuple2 { + fn new(x: T, y: U) -> Self { + Self { x, y } + } +} + +struct Pair { + x: T, + y: T, +} + +// conditional implementation +impl Pair { + fn largest(&self) -> T { + if self.x > self.y { + self.x + } else { + self.y + } + } +} + +// alternative syntax +impl Pair +where + T: PartialOrd + Copy, +{ + fn smallest(&self) -> T { + if self.x < self.y { + self.x + } else { + self.y + } + } +} + +// Here information about the concrete underlying type is erased +// We can only either format or clone the result +fn cloning_machine(item: &(impl Clone + Debug)) -> impl Clone + Debug { + item.clone() +} + +fn main() { + let _opt = OurOption::Some(10); + + let _p1 = Tuple2 { x: 5, y: 10 }; + let _p2 = Tuple2::new(1, 2.5); + + let arr = [1, 2, 3]; + let arr2 = cloning_machine(&arr); + // arr2[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug` + println!("{:?}", arr2) +} diff --git a/lessons/old/2022Z/05-types-reasoning/generics_fun.rs b/lessons/old/2022Z/05-types-reasoning/generics_fun.rs new file mode 100644 index 0000000..241c389 --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/generics_fun.rs @@ -0,0 +1,35 @@ +use std::fmt::{Display, Formatter}; + +trait DefaultishablyPrintable { + fn defaultish_print() + where + T: Display + Default, + { + println!("{}", T::default()) + } +} + +struct Foo; + +struct Bar; + +impl Display for Bar { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("this is a bar") + } +} + +impl Default for Bar { + fn default() -> Self { + Bar // well, we have no other choice + } +} + +impl DefaultishablyPrintable for Foo {} + +impl DefaultishablyPrintable for Foo {} + +fn main() { + >::defaultish_print(); + >::defaultish_print(); +} diff --git a/lessons/old/2022Z/05-types-reasoning/index.html b/lessons/old/2022Z/05-types-reasoning/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/05-types-reasoning/lifetimes_basic.rs b/lessons/old/2022Z/05-types-reasoning/lifetimes_basic.rs new file mode 100644 index 0000000..a4eaa2b --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/lifetimes_basic.rs @@ -0,0 +1,27 @@ +fn longest<'a>(first: &'a str, second: &'a str) -> &'a str { + if first.len() > second.len() { + first + } else { + second + } +} + +fn main() { + let string1 = String::from("long string is long"); + + { + let string2 = String::from("xyz"); + let result = longest(string1.as_str(), string2.as_str()); + println!("The longest string is {}", result); + } + + // This doesn't compile - incorrect lifetimes + // + // let string1 = String::from("long string is long"); + // let result; + // { + // let string2 = String::from("xyz"); + // result = longest(string1.as_str(), string2.as_str()); + // } + // println!("The longest string is {}", result); +} diff --git a/lessons/old/2022Z/05-types-reasoning/lifetimes_elision.rs b/lessons/old/2022Z/05-types-reasoning/lifetimes_elision.rs new file mode 100644 index 0000000..9633909 --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/lifetimes_elision.rs @@ -0,0 +1,16 @@ +fn first_two(seq: &[u32]) -> &[u32] { + if seq.len() < 2 { + seq + } else { + &seq[..2] + } +} + +fn main() { + let seq = [1, 2, 3, 4]; + + println!( + "First two elements of the sequence: {:?}", + first_two(&seq[..]) + ); +} diff --git a/lessons/old/2022Z/05-types-reasoning/non_generic.rs b/lessons/old/2022Z/05-types-reasoning/non_generic.rs new file mode 100644 index 0000000..c983e3b --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/non_generic.rs @@ -0,0 +1,35 @@ +fn largest_i32(list: &[i32]) -> i32 { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn largest_char(list: &[char]) -> char { + let mut largest = list[0]; + + for &item in list { + if item > largest { + largest = item; + } + } + + largest +} + +fn main() { + let number_list = vec![34, 50, 25, 100, 65]; + + let result = largest_i32(&number_list); + println!("The largest number is {}", result); + + let char_list = vec!['y', 'm', 'a', 'q']; + + let result = largest_char(&char_list); + println!("The largest char is {}", result); +} diff --git a/lessons/old/2022Z/05-types-reasoning/static_dynamic_dispatch.rs b/lessons/old/2022Z/05-types-reasoning/static_dynamic_dispatch.rs new file mode 100644 index 0000000..2d9eeda --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/static_dynamic_dispatch.rs @@ -0,0 +1,47 @@ +trait Speak { + fn speak(&self) -> &'static str; +} + +struct Dog; + +impl Speak for Dog { + fn speak(&self) -> &'static str { + "Hau hau" // it's a Polish dog! + } +} + +struct Human; + +impl Speak for Human { + fn speak(&self) -> &'static str { + "Hello world" + } +} + +// It works like templates in C++ +// A different function will be generated for each T during compilation +// This process is called "monomorphization" +fn static_dispatch(speaking: &T) { + println!("{}!", speaking.speak()); +} + +// Only one copy of that function will exist in the compiled binary +fn dynamic_dispatch(speaking: &dyn Speak) { + println!("{}!", speaking.speak()); +} + +fn main() { + let dog = Dog; + let human = Human; + + static_dispatch(&dog); + static_dispatch(&human); + + dynamic_dispatch(&dog); + dynamic_dispatch(&human); + + // The observable behavior is identical + // Static dispatch in general is a bit faster, + // because there is no need to perform a "vtable lookup". + // But it can also result in bigger binary sizes. +} diff --git a/lessons/old/2022Z/05-types-reasoning/trait_default.rs b/lessons/old/2022Z/05-types-reasoning/trait_default.rs new file mode 100644 index 0000000..84ba1b3 --- /dev/null +++ b/lessons/old/2022Z/05-types-reasoning/trait_default.rs @@ -0,0 +1,67 @@ +#![allow(dead_code)] + +struct Upload { + filename: String, +} + +#[allow(dead_code)] +struct Photo { + filename: String, + width: u32, + height: u32, +} + +trait Description { + fn describe(&self) -> String { + String::from("No description available.") + } +} + +// All default implementations +impl Description for Upload {} + +// Default implementations can be overwritten +impl Description for Photo { + fn describe(&self) -> String { + format!("{} ({} x {})", self.filename, self.width, self.height) + } +} + +// Default implementations can rely on methods with no defaults +trait Size { + fn width(&self) -> u32; + fn height(&self) -> u32; + + fn size(&self) -> u32 { + self.width() * self.height() + } +} + +impl Size for Photo { + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } + + // Using default impl of `size()` +} + +fn main() { + let upload = Upload { + filename: String::from("notes.txt"), + }; + + println!("Upload: {}", upload.describe()); + + let photo = Photo { + filename: String::from("stock_crustacean.png"), + width: 100, + height: 150, + }; + + println!("Photo: {}", photo.describe()); + println!("Size: {}", photo.size()); +} diff --git a/lessons/old/2022Z/06-closures-iterators/index.html b/lessons/old/2022Z/06-closures-iterators/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/06-closures-iterators/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/07-smart-pointers/box.rs b/lessons/old/2022Z/07-smart-pointers/box.rs new file mode 100644 index 0000000..9a9f99e --- /dev/null +++ b/lessons/old/2022Z/07-smart-pointers/box.rs @@ -0,0 +1,18 @@ +fn box_simple() { + let b = Box::new(5); + println!("b = {}", b); + + let _x = 10 + *b; +} + +// `Box` gives us the indirection required to define +// recursive types +#[allow(dead_code)] +enum List { + Cons(i32, Box), + Nil, +} + +fn main() { + box_simple(); +} diff --git a/lessons/old/2022Z/07-smart-pointers/deref_coercion.rs b/lessons/old/2022Z/07-smart-pointers/deref_coercion.rs new file mode 100644 index 0000000..c5b14c7 --- /dev/null +++ b/lessons/old/2022Z/07-smart-pointers/deref_coercion.rs @@ -0,0 +1,39 @@ +use std::ops::Deref; + +struct MyBox(T); + +// We won't be allocating anything on the heap here as it is not important here. +// We're only focusing on the dereference mechanisms. +impl MyBox { + fn new(x: T) -> MyBox { + MyBox(x) + } +} + +impl Deref for MyBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn hello(name: &str) { + println!("Hello, {}!", name); +} + +fn main() { + let x = 5; + let int_box = MyBox::new(x); + + assert_eq!(5, *int_box); + + // String also implements the `Deref` trait. + // In fact, String actually is a smart pointer. + let s = String::from("I'm a smart pointer too"); + hello(&s); + + // Deref coercion can deal with multiple levels of indirection. + let str_box = MyBox::new(String::from("Rust")); + hello(&str_box); +} diff --git a/lessons/old/2022Z/07-smart-pointers/index.html b/lessons/old/2022Z/07-smart-pointers/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/07-smart-pointers/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/07-smart-pointers/ref_count.rs b/lessons/old/2022Z/07-smart-pointers/ref_count.rs new file mode 100644 index 0000000..0f97932 --- /dev/null +++ b/lessons/old/2022Z/07-smart-pointers/ref_count.rs @@ -0,0 +1,31 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + { + let outer_ref; + + { + let inner_ref = Rc::new(LoudInt(5)); + + // strong_count represents the number of owning references pointing + // to data + assert_eq!(Rc::strong_count(&inner_ref), 1); + + outer_ref = Rc::clone(&inner_ref); + + assert_eq!(Rc::strong_count(&inner_ref), Rc::strong_count(&outer_ref)); + assert_eq!(Rc::strong_count(&inner_ref), 2); + } + + println!("The {} still lives!", outer_ref.0); + assert_eq!(Rc::strong_count(&outer_ref), 1); + } +} diff --git a/lessons/old/2022Z/07-smart-pointers/weak_ref.rs b/lessons/old/2022Z/07-smart-pointers/weak_ref.rs new file mode 100644 index 0000000..b2406f6 --- /dev/null +++ b/lessons/old/2022Z/07-smart-pointers/weak_ref.rs @@ -0,0 +1,36 @@ +use std::rc::Rc; + +struct LoudInt(i32); + +impl Drop for LoudInt { + fn drop(&mut self) { + println!("[{}] Farewell!", self.0); + } +} + +fn main() { + let weak_ref; + + { + let shared_ref = Rc::new(LoudInt(5)); + + // weak_count keeps track of the non-owning reference to the data + assert_eq!(Rc::weak_count(&shared_ref), 0); + + // `downgrade()` obtains a weak pointer to Rc's data + weak_ref = Rc::downgrade(&shared_ref); + + assert_eq!(Rc::weak_count(&shared_ref), 1); + assert_eq!(Rc::strong_count(&shared_ref), 1); + + // In order to use the the data underneath the weak pointer + // we need to obtain a new shared pointer from it. + // The `upgrade()` method returns `Option>`. + let temp = weak_ref.upgrade(); + assert_eq!(Rc::strong_count(&shared_ref), 2); + println!("The value is {}", temp.unwrap().0); + } + + println!("The value should be deallocated by now."); + matches!(weak_ref.upgrade(), None); +} diff --git a/lessons/old/2022Z/08-feedback-2/index.html b/lessons/old/2022Z/08-feedback-2/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/08-feedback-2/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/09-concurrency/index.html b/lessons/old/2022Z/09-concurrency/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/09-concurrency/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/10-design-patterns/index.html b/lessons/old/2022Z/10-design-patterns/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/10-design-patterns/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/11-async-1/index.html b/lessons/old/2022Z/11-async-1/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/11-async-1/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/old/2022Z/12-project-feedback/index.html b/lessons/old/2022Z/12-project-feedback/index.html new file mode 100644 index 0000000..2bbc7ae --- /dev/null +++ b/lessons/old/2022Z/12-project-feedback/index.html @@ -0,0 +1,43 @@ + + + Zola + + +
                                  +

                                  Welcome to Zola!

                                  +

                                  + You're seeing this page because we couldn't find a template to render. +

                                  +

                                  + To modify this page, create a page.html file in the templates directory or + install a theme. +
                                  + You can find what variables are available in this template in the documentation. +

                                  +
                                  + + + + diff --git a/lessons/project-showcase/fail-menu.png b/lessons/project-showcase/fail-menu.png new file mode 100644 index 0000000..ecee149 Binary files /dev/null and b/lessons/project-showcase/fail-menu.png differ diff --git a/lessons/project-showcase/game.png b/lessons/project-showcase/game.png new file mode 100644 index 0000000..65acfa6 Binary files /dev/null and b/lessons/project-showcase/game.png differ diff --git a/lessons/project-showcase/graph_vis_demo.mkv b/lessons/project-showcase/graph_vis_demo.mkv new file mode 100644 index 0000000..a2f08eb Binary files /dev/null and b/lessons/project-showcase/graph_vis_demo.mkv differ diff --git a/lessons/project-showcase/index.html b/lessons/project-showcase/index.html new file mode 100644 index 0000000..420bd0d --- /dev/null +++ b/lessons/project-showcase/index.html @@ -0,0 +1,277 @@ + + + + + + + + Rust course + + + + + + + + + + + + + + + +
                                  + + +
                                  + +
                                  + +
                                  +
                                  +
                                    +
                                    +
                                    + +
                                    + +

                                    2021L Project showcase

                                    +

                                    + 2022-10-17 (last edit: 2022-10-17) +

                                    +

                                    (Some) projects from the previous semester

                                    +

                                    Chat App

                                    +

                                    GitHub

                                    +

                                    Bartłomiej Sadlej (GitHub: @barteksad email: sadlejbartek@gmail.com)

                                    +

                                    "Chat app written in Rust with Postgress db. Allows users to create new accounts and channels and communicate within channel. All unseed messages from last login are delivered once logged in again.

                                    +

                                    By default server stats with one user named ADMIN with password ADMIN"

                                    +

                                    tokio, serde, anyhow, thiserror, dashmap

                                    +

                                    Algorithm Visualizer

                                    +

                                    GitHub

                                    +

                                    Mikołaj Piróg (mikolajpirog@gmail.com, GitHub: @aetn23), Mikołaj Wasiak (wasiak.mikolaj1@gmail.com, GitHub: @RudyMis)

                                    +

                                    Graph editor with algorithms visualization. Create and modify the graph using GUI, move it around using WSAD. To see an algorithm being run, simply click on a node, and select the desired algorithm. See video and/or GitHub page for further details.

                                    +

                                    Petgraph, egui-tetra, dyn_partial_eq

                                    + +

                                    Rustal Combat

                                    +

                                    GitHub

                                    +

                                    Kamil Bugała (GiHub: @Emilo77)

                                    +

                                    Simple game in the style of Mortal Combat. Two players fight each other by using dashes. It is the 1 v 1 version, so far it is possible to play on a one computer.

                                    +

                                    Bevy

                                    +

                                    Rusty dungeon

                                    +

                                    GitHub

                                    +

                                    Barbara Rosiak (GitHub: @barosiak, email basiarosiak.7@gmail.com), Tomasz Kubica (GitHub: @Tomasz-Kubica, email: tomaszkubica4@gmail.com), Dawid Mędrek (GitHub: @dawidmd)

                                    +

                                    A 2D game written using Rust and Bevy Game Engine, in which the player has to shoot and defeat enemies on their way to the final boss, inspired by the Binding of Isaac.

                                    +

                                    Bevy

                                    +

                                    +

                                    MarioMIM

                                    +

                                    GitHub

                                    +

                                    Katarzyna Kloc (GitHub: @KatKlo, email: kk429317@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/katarzyna-kloc-7a7503209/), +Patryk Bundyra (GitHub: PBundyra, email: pb429159@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/pbundyra/)

                                    +

                                    Since the moment CLion has shown us the first segfaults, we wanted to create a computer game inspired by the student’s adventure of pursuing a Computer Science degree. MarioMIM is a platform game whose main goal is to... get a degree. We’ve implemented a game inspired by Super Mario Bros, but in the special University of Warsaw edition. In order to overcome bugs, the student can strengthen himself by drinking coffee or learning the best programming language in the world - Rust.

                                    +

                                    Bevy, Rapier, Kira

                                    +

                                    +

                                    +

                                    Sendino

                                    +

                                    GitHub

                                    +

                                    Grzegorz Nowakowski (Github: @grzenow4, email: g.nowakowski@student.uw.edu.pl) with @izmael7 on Github

                                    +

                                    One room chat application with client-server model of communication. Many users can communicate at one time.

                                    +

                                    Tokio, serde, crossterm

                                    +

                                    Chatter

                                    +

                                    GitHub

                                    +

                                    Kacper Kramarz-Fernandez (GitHub: @kfernandez31, email: kacper.fernandez@gmail.com), +Jan Zembowicz (GitHub: @JWZ1996, email: janzembowicz@gmail.com)

                                    +

                                    Chatter is a simple multi-room command-line chat application that uses a two-protocol (HTTP + WS) communication style for high-efficiency.

                                    +

                                    Tokio, Warp, Serde, Hyper, among others

                                    + + +
                                    +
                                    + + + + +
                                    + + + + + + + + + + + \ No newline at end of file diff --git a/lessons/project-showcase/rusty-dungeon.png b/lessons/project-showcase/rusty-dungeon.png new file mode 100644 index 0000000..01f149d Binary files /dev/null and b/lessons/project-showcase/rusty-dungeon.png differ diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..c41df62 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: +Allow: / +Sitemap: https://mimuw-jnp2-rust.github.io/sitemap.xml diff --git a/search_index.en.js b/search_index.en.js new file mode 100644 index 0000000..0a5842f --- /dev/null +++ b/search_index.en.js @@ -0,0 +1 @@ +window.searchIndex = {"fields":["title","body"],"pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5","index":{"body":{"root":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":6,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}},"=":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1},"4":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":1}}}}}}}}},"6":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}},"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1},"x":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"a":{"docs":{},"df":0,"7":{"docs":{},"df":0,"0":{"docs":{},"df":0,"6":{"docs":{},"df":0,"0":{"docs":{},"df":0,",":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":5.0990195135927845},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":12,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"/":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":2}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":2}},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1,":":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":4,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"=":{"docs":{},"df":0,"1":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"=":{"docs":{},"df":0,"1":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":4},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2},"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}},"9":{"docs":{},"df":0,".":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":1}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":12,".":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"/":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"=":{"docs":{},"df":0,"2":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}},"2":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1},"1":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.7320508075688772}},"df":1},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}},"2":{"docs":{},"df":0,"1":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"3":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},":":{"docs":{},"df":0,"5":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7}}}},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.8284271247461903}},"df":1}},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":3.3166247903554}},"df":1}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":10,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"=":{"docs":{},"df":0,"4":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":6,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979}},"df":3},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":7,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"=":{"docs":{},"df":0,"6":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}},"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":5,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1},"6":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"a":{"docs":{},"df":0,"7":{"docs":{},"df":0,"5":{"docs":{},"df":0,"0":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}},"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":3,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}},"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":5},"_":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979}},"df":1}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}},"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"b":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0}},"df":1},"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":7}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":5}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}}}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1}}}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":3}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"n":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":5.5677643628300215}},"df":2,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}}},"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"z":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1,"[":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.0}},"df":2}}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.449489742783178}},"df":3},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":7,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":2}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":4}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":4}}}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6,";":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979}},"df":1,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"'":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":3}}}}},"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"[":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1},"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}}},"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{},"df":0,"[":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}},"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1}}}},"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"h":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":8,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"[":{"docs":{},"df":0,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"1":{"docs":{},"df":0,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}},"q":{"docs":{},"df":0,"[":{"docs":{},"df":0,".":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":4}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2}},"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}},"d":{"docs":{},"df":0,"/":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}},"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178}},"df":1}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"y":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":6}}},"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"y":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178}},"df":2,"(":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"_":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":2}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":2,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":3},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":10}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}}},"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":2,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0}},"df":1}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":2}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":2}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":2}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"k":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":5.291502622129181},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":5,"\"":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"ł":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5},"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,".":{"docs":{},"df":0,"7":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}},"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.8284271247461903}},"df":2}}}}},"v":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.0}},"df":1}}},"i":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":3}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1},"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":3}},"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.8284271247461903}},"df":1}},"n":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1}},"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":5.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.4142135623730951}},"df":11,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.47213595499958},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":7,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":5}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}},"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"ł":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":2},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":7,"\"":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}},"+":{"docs":{},"df":0,"+":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1},"1":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},":":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"k":{"docs":{},"df":0,"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"y":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"1":{"docs":{},"df":0,"5":{"docs":{},"df":0,"7":{"docs":{},"df":0,"7":{"docs":{},"df":0,"6":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"9":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"y":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.0}},"df":8,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.7416573867739413}},"df":2}}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":2}},"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":5},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":2,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":4},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":2}}}},"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":9}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":3,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.0}},"df":1,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":9,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":4}}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979}},"df":4},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":6.164414002968976},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2,"e":{"docs":{},"df":0,"@":{"docs":{},"df":0,"s":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"9":{"docs":{},"df":0,":":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}},"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"x":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":9}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.23606797749979}},"df":1}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903}},"df":1}}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"b":{"docs":{},"df":0,"(":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}},"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.0}},"df":2}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.47213595499958},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979}},"df":5,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.0}},"df":2},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":2}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":4}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}},"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":6,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}},"x":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":2}}}},"o":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":2},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}},"p":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":5},"y":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"'":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":3.0}},"df":3,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":5,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":2}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":2}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":9}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,"\"":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":3}},"y":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":6,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}},"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":8}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":2,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":4,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":3.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"=":{"docs":{},"df":0,"u":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"=":{"docs":{},"df":0,"u":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903}},"df":2,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":2}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}}}},"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":8}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.47213595499958}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178}},"df":1}}}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"(":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":4,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"m":{"docs":{},"df":0,"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"ę":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":7}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}}}}},"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0}},"df":5}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0}},"df":3,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}}},"y":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":2,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}},"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.7320508075688772}},"df":1,"\"":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},".":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":8}},"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}}},"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":2}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.8284271247461903}},"df":1}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"7":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":4},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":3}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.123105625617661},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"{":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178}},"df":2}}}}}},"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}},"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":3}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"1":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979}},"df":1}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":7,"[":{"docs":{},"df":0,"e":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"2":{"docs":{},"df":0,"7":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"3":{"docs":{},"df":0,"6":{"docs":{},"df":0,"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"8":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"9":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":3}}}}},"t":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":2}},"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":3}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.795831523312719},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":9,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"/":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":1}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":2}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178}},"df":1,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":2,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}}}},".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"6":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":2.23606797749979}},"df":1}}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}},"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903}},"df":1}}}}}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":3}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":3}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":3}}},"f":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907}},"df":2,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":2}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":3},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":4.795831523312719}},"df":2}}}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":9,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}},"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"w":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.4641016151377544}},"df":1}}}}}}}}}},".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"=":{"docs":{},"df":0,"%":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"\\":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"l":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":8.660254037844387},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":4.69041575982343},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.3166247903554}},"df":11,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979}},"df":1,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":4}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1},"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}},"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":2,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178}},"df":1,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0}},"df":9,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":3}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":5.916079783099616}},"df":3,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":1}}}}}}}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.6457513110645907}},"df":1,".":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"@":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.449489742783178}},"df":1}},"r":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"ń":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":7,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"y":{"docs":{},"df":0,"k":{"docs":{},"df":0,"ó":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4,"_":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1},"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}}}},"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":4.47213595499958}},"df":3}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":5,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":3},"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":4}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":9}},"p":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"z":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}},"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":6.324555320336759},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":8.717797887081348},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.4641016151377544}},"df":10},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}}}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"ł":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"z":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":7,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1}}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.449489742783178}},"df":3},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}},"v":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":1},"l":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":4}},"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":11},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.872983346207417}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1}}}}}}},"g":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":3,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":1}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"/":{"docs":{},"df":0,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"w":{"docs":{},"df":0,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"/":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"w":{"docs":{},"df":0,"w":{"docs":{},"df":0,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"/":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"z":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}},"p":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":2}}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0}},"df":1}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":2}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":4}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":5.830951894845301},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":2}}}}}}}},"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},"`":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":10}}}}},"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":3}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}},"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":3}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1,"r":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":2,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3},"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":9}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903}},"df":3,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":2}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}},"e":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":3,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2},"l":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2}}},"s":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"t":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":8},"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":4,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}},"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}}}},"z":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"j":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"z":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"h":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.0}},"df":1,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"(":{"docs":{},"df":0,"[":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"z":{"docs":{},"df":0,"1":{"docs":{},"df":0,"9":{"docs":{},"df":0,"9":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"ę":{"docs":{},"df":0,"z":{"docs":{},"df":0,"y":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"z":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"z":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"k":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"z":{"docs":{},"df":0,"3":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"k":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{},"df":0,"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"1":{"docs":{},"df":0,"7":{"docs":{},"df":0,"@":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":5,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"z":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"#":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5}}}}},"r":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":5.385164807134504},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":6}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}}},"z":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}}}}}}},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1},"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":6}}}},"t":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":6}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":5,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":6.48074069840786},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"k":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":6},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":4.69041575982343},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":6,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"(":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}},"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1},"_":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":4}},"t":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":3}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.6457513110645907}},"df":2}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}}}}}}}}}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}},"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":6,"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":3,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}},"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178}},"df":2}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}}}}}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,"2":{"docs":{},"df":0,"7":{"docs":{},"df":0,":":{"docs":{},"df":0,"3":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},"u":{"docs":{},"df":0,"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"é":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}}},"r":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.7320508075688772}},"df":5}}},"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.7320508075688772}},"df":1},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.872983346207417},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":10,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},"k":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":9,"(":{"docs":{},"df":0,"[":{"docs":{},"df":0,"]":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"_":{"docs":{},"df":0,"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":3,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.898979485566356},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}},"y":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":6}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":2}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1}}}},"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.23606797749979}},"df":3}}}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":5}}}}},"r":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":10,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"z":{"docs":{},"df":0,"k":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"k":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"j":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"ł":{"docs":{},"df":0,"a":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4}},"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":7},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.872983346207417},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":6},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":6}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.872983346207417},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":9,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":6}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}},"e":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772}},"df":2,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}},"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}},"(":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"ę":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.7320508075688772}},"df":2,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":8,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}},"e":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":8}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":8,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.242640687119285}},"df":1,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}},"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}},"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"x":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":4,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}},"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6},"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4},"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3},"f":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":8,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}},"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"8":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"[":{"docs":{},"df":0,"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"(":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":7}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2}}}},"d":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979}},"df":1,"(":{"docs":{},"df":0,"_":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979}},"df":3}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.3166247903554},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.0}},"df":10,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":6}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}},"b":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}},".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":6,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":3}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":4,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":2}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":3,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":5}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":3}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.449489742783178}},"df":2,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":5,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"b":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{},"df":0,"9":{"docs":{},"df":0,"1":{"docs":{},"df":0,"5":{"docs":{},"df":0,"9":{"docs":{},"df":0,"@":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":2,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"{":{"docs":{},"df":0,"{":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"j":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"h":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":4}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.4142135623730951}},"df":1,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}}}}}},"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"ó":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.3166247903554}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":1}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.23606797749979}},"df":1}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.23606797749979}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0}},"df":1},"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"y":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.4142135623730951}},"df":2}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979}},"df":1,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":6}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":6}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"[":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"(":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}}},"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"x":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"l":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":7,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1},"[":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}}}},"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"f":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.6457513110645907}},"df":1}}}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"h":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.123105625617661}},"df":1}}}},"m":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}},"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":2,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":2}}}}},"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2},"{":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}}}}},"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":2}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":4}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2}}}},"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":7,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":3}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":2}}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":6}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":6}}}},"z":{"docs":{},"df":0,"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"ł":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":2},"r":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}},"w":{"docs":{},"df":0,"3":{"docs":{},"df":0,"9":{"docs":{},"df":0,"4":{"docs":{},"df":0,"9":{"docs":{},"df":0,"8":{"docs":{},"df":0,"0":{"docs":{},"df":0,"[":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}}}}},"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}}}}},"u":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":2},"z":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795}},"df":3,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2}},"i":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":3}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":3},"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":11,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"b":{"docs":{},"df":0,"y":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":1},"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":6}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":3}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}},"e":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979}},"df":1,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0}},"df":1,"l":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"i":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772}},"df":1}}}}}},"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":8,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979}},"df":6}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":2}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0}},"df":5,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.23606797749979}},"df":9}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"z":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"ą":{"docs":{},"df":0,"z":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"y":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.47213595499958},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":14,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4},"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"f":{"docs":{},"df":0,"m":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":1}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":6.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":7,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2}}}}}},"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":1}}}}},"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.7416573867739413}},"df":1},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}},"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"k":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.6457513110645907}},"df":5,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4}}}},"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":7},"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":6}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":5,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":7,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}},"g":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.8284271247461903}},"df":5,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}},".":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":2},"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}}}}},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"o":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"1":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"(":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}},"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":3.7416573867739413}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}},"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1},"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":2},"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5,"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":3.3166247903554}},"df":3,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"(":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}}},"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":2}}}},"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0}},"df":2}}}},"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":5,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.8284271247461903}},"df":1,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":3}}}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":3}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"z":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":4,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"#":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.0}},"df":1,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":3}},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1},"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"8":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}},"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3}},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":3},"i":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2}}},"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0}},"df":6}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0}},"df":2,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":4}},"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":3,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907}},"df":4}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"2":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}},"3":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0}},"df":1,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979}},"df":1,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"4":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"2":{"docs":{},"df":0,":":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"4":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"5":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"6":{"docs":{},"df":0,":":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"7":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"7":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"9":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"3":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"9":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":3}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":3}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":4.69041575982343},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":5}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":3,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":7,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}},"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":2}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"m":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.3166247903554}},"df":1}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.4641016151377544}},"df":1}}}}}},"{":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}},"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"(":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"{":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":2},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":7}}},"o":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989}},"df":2,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":5.916079783099616},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":7.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":8,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2,"b":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"'":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0}},"df":1}}}}},"n":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979}},"df":1}},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"x":{"docs":{},"df":0,"y":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"8":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":2}}}}},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"_":{"docs":{},"df":0,"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"{":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":5.196152422706632},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772}},"df":5,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"/":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}},"k":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"’":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}}},"y":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":1}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}}}}}},"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0}},"df":1,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.0}},"df":1,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907}},"df":1}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.449489742783178}},"df":1}}}}}}}}}}}}},"y":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1},"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":3.4641016151377544}},"df":1,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":2}}}}},"{":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.795831523312719},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.449489742783178}},"df":4,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"k":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}}}},"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772}},"df":2}},"m":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":4},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}}},"y":{"docs":{},"df":0,"'":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}}}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":4}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":5.5677643628300215},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":6.324555320336759},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":4,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1},".":{"docs":{},"df":0,"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.23606797749979}},"df":1}}}}}}}}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":5}}}}},"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":8}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.0}},"df":1}}},"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951}},"df":1}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":3}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1,"k":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"4":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.4142135623730951}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":6.48074069840786},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":8,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}},"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":5,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}},"y":{"docs":{},"df":0,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"j":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":2,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":6.082762530298219}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"_":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}},"a":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,";":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"'":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}},"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.0}},"df":1,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}},"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":10}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.358898943540674},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":6.708203932499369},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178}},"df":10,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}}},"1":{"docs":{},"df":0,"6":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.23606797749979}},"df":1}},"3":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":2.0}},"df":7,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}}}}}}}}}},"6":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}},"8":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":2},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":4}}}}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":2}}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\"":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772}},"df":1}},"q":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}},"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":4}}}}},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":7,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.7320508075688772}},"df":1}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.872983346207417},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":5.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":3.872983346207417},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":2.23606797749979}},"df":13,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.7320508075688772}},"df":4,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795}},"df":1}}}}},"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0}},"df":4}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}}},"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}}},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1},"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}},"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":3.4641016151377544},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.605551275463989},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":4.242640687119285},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.1622776601683795},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":9,"e":{"docs":{},"df":0,"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.6457513110645907}},"df":1,"=":{"docs":{},"df":0,"%":{"docs":{},"df":0,"v":{"docs":{},"df":0,"\\":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}}},"[":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1},"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1}}},"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"=":{"docs":{},"df":0,"[":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979}},"df":1},"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":6,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"u":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":2}}}}},"e":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1,"!":{"docs":{},"df":0,"[":{"docs":{},"df":0,"'":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}},"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1},"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2},"3":{"docs":{},"df":0,"4":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"8":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2}}}},"r":{"docs":{},"df":0,"i":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":3.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":5},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":5}}}}}},"i":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":1}}}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":2}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.7320508075688772}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}}}}}},"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"n":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":5,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951}},"df":1,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"k":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":3.1622776601683795}},"df":1,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":8}},"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":2},"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"k":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"j":{"docs":{},"df":0,"1":{"docs":{},"df":0,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0}},"df":1}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":9}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"'":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":5}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":3},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3}},"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"b":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":2,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":4}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1},"’":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"'":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2},"e":{"docs":{},"df":0,"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}},"o":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,";":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772}},"df":4}}}}}},"o":{"docs":{},"df":0,"j":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"z":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":5}}},"r":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.7320508075688772},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":2.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.4142135623730951}},"df":8,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":1}}}}},"l":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":3}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"h":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":4}}}},"p":{"docs":{},"df":0,"4":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,"[":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"w":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"[":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"]":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"z":{"docs":{},"df":0,"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951}},"df":2,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.7320508075688772}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":2.23606797749979},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.6457513110645907},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.4142135623730951},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.4142135623730951}},"df":8,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":2.0}},"df":1}}},"l":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.7320508075688772}},"df":1}},"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.4142135623730951}},"df":3}}}}}},"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"d":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"x":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":4.47213595499958},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":2.449489742783178},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.7416573867739413},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":2.8284271247461903},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":2.23606797749979}},"df":6,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"3":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1},"5":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":1}}}}}}},"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}}}},"y":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}},"y":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":4.123105625617661},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":3.605551275463989}},"df":3,".":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1},"b":{"docs":{},"df":0,"[":{"docs":{},"df":0,"0":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"'":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1},"v":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"g":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":2}}}}}}}},"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":2}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"z":{"docs":{},"df":0,"n":{"docs":{},"df":0,"y":{"docs":{},"df":0,"m":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}},"title":{"root":{"docs":{},"df":0,"1":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0}},"df":2},"2":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":3,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"1":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}},"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}}}}},"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0}},"df":3}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"tf":1.0}},"df":1}}}},"j":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"tf":1.0}},"df":1}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":2}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"tf":1.0}},"df":2}}}},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"tf":1.0},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"tf":1.0}},"df":2}}}}}}},"documentStore":{"save":true,"docs":{"https://mimuw-jnp2-rust.github.io/":{"body":"This is a website for the JNP2: Rust course at MIM UW.\nThe course's instructor is Wojciech Przytuła (GitHub: wprzytula, e-mail: wp418383[at]students[dot]mimuw[dot]edu[dot]pl),\nInstructori emeriti:\n\nPiotr Wojtczak (GitHub: StarostaGit, e-mail: pw394980[at]students[dot]mimuw[dot]edu[dot]pl),\nAndrzej Głuszak (GitHub: agluszak, e-mail: agluszak[at]mimuw[dot]edu[dot]pl).\n\nYou can find lesson scenarios here.\nContributions are welcome! ;)\n","id":"https://mimuw-jnp2-rust.github.io/","title":"JNP 2: Rust"},"https://mimuw-jnp2-rust.github.io/lessons/":{"body":"","id":"https://mimuw-jnp2-rust.github.io/lessons/","title":"List of lessons"},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"body":"Rust course\nWe will be using Github Classroom for task submission and Discord for discussions.\nOur main learning/teaching resource will be \"The Book\".\nAlso worth mentioning: \"Rust by Example\".\nGrading\n\n1/3 of the grade is based on small tasks. There will be approximately 1 task every two weeks and each task will be graded on a scale of 0 to 3.\n2/3 of the grade is based on a big project. You can choose a topic yourself, but it must be accepted by me. The project has to be split into two parts. It can be done in groups of two (or bigger, if ambitious enough).\nThe grade may be increased by a bonus. You can get a bonus for:\n\nMaking a presentation about some advanced topic (const generics, futures, macros, etc.) or about architecture of a selected Rust open-source library\nContributing to a selected Rust open-source library\nContributing to this course's materials\nQuizzes, homeworks, general activity etc.\n\n\n\nProject Deadlines\n\n2024-11-7: Project ideas should be presented to me for further refining. If you wish to pair up with someone, now is the time to tell me.\n2024-11-14: Final project ideas should be accepted by now.\n2024-12-12: Deadline for submitting the first part of the project.\n2025-01-09: Deadline for submitting the second and final part of the project.\n\n","id":"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/","title":"Organizational lesson"},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"body":"\nA language empowering everyone to build reliable and efficient software.\n(unofficial logo)\nWhy use Rust?\n\nIt is safe (compared to C++ for example, as we will see in a minute)\nIt is fast (because it is compiled to machine code)\nIt is ergonomic and pleasant to use (static typing, expressive type system, helpful compiler\nwarnings)\nIt\nis loved by programmers\nIt provides excellent tooling\n\nWhy learn Rust?\nEven if you don't end up using Rust, learning it expands your horizons\n\nit helps especially with the awareness of what you can and can't do in concurrent applications\nit helps you understand memory management and learn its good practices\n\nWhy not to learn Rust?\n\nSome people say Rust is too hard to learn because of the borrow checker\nOnce you get to know Cargo you won't ever want to use a language without a built-in package\nmanager ;)\nYou will start hating C++\n\nDemos\n\nLet's compare the same code written in C, C++\nand Rust.\nCode you sent in previous editions!\nAleksander Tudruj\n#include <iostream>\n#include <unordered_map>\n\nusing name = std::string;\nusing age = int;\nusing person = std::pair<name, age>;\nusing address = std::string;\nusing address_book = std::unordered_map<person, address>;\n\nvoid print_address_book(const address_book &book)\n{\n for (const auto &[person, address] : book)\n {\n std::cout << person.first << \" is \" << person.second << \" years old and lives at \" << address << std::endl;\n }\n}\n\nint main()\n{\n\n address_book people{};\n people.insert({{\"John\", 20}, \"221B Baker Street, London\"});\n people.insert({{\"Mary\", 30}, \"Avenue des Champs-Élysées, Paris\"});\n people.insert({{\"Jack\", 73}, \"Wall Street, New York\"});\n print_address_book(people);\n\n return 0;\n}\n\n\n(Download the source code for this example: tudruj.cpp)\nKrystyna Gasińska\n# sample 1 - different ways of removing elements from the list while iterating\nlist1 = [1, 2, 3, 4]\nfor idx, item in enumerate(list1):\n del item\nlist1\n\n# [1, 2, 3, 4]\n\nlist2 = [1, 2, 3, 4]\nfor idx, item in enumerate(list2):\n list2.remove(item)\nlist2\n\n# [2, 4]\n\nlist3 = [1, 2, 3, 4]\nfor idx, item in enumerate(list3[:]):\n list3.remove(item)\nlist3\n\n# []\n\nlist4 = [1, 2, 3, 4]\nfor idx, item in enumerate(list4):\n list4.pop(idx)\nlist4\n\n# [2, 4]\n\n# sample 2 - string interning\na = \"abc\"\nb = \"abc\"\na is b\n\n# True\n\na = ''.join(['a', 'b', 'c'])\nb = ''.join(['a', 'b', 'c'])\na is b\n\n# False\n\na = \"abc!\"\nb = \"abc!\"\na is b\n\n# False\n\n# sample 3 - chained operations\n(False == False) in [False]\n\n# False\n\nFalse == (False in [False])\n\n# False\n\nFalse == False in [False] # unexpected...\n\n# True\n\n# sample 4 - is operator\na = 256\nb = 256\na is b\n\n# True\n\na = 257\nb = 257\na is b\n\n# False\n\na, b = 257, 257\na is b\n\n# True\n\n257 is 257\n\n# <>:1: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n# <>:1: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n# C:\\Users\\kgasinsk\\AppData\\Local\\Temp\\ipykernel_15776\\331119389.py:1: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n# 257 is 257\n\n# sample 5 - local variables\ndef f(trufel):\n if trufel:\n y = 1\n y += 1\n\nf(True) # everything is fine\n\nf(False) # gives error: local variable 'y' referenced before assignment\n\n# ---------------------------------------------------------------------------\n# UnboundLocalError Traceback (most recent call last)\n# Input In [17], in <cell line: 1>()\n# ----> 1 f(False)\n\n# Input In [15], in f(trufel)\n# 3 if trufel:\n# 4 y = 1\n# ----> 5 y += 1\n\n# UnboundLocalError: local variable 'y' referenced before assignment\n\n(Download the source code for this example: gasinska.py)\nAntoni Koszowski\n// mutowalność jest wbudowana w język\n\ntype S struct {\n A string\n B []string\n}\n \nfunc main() {\n x := S{\"x-A\", []string{\"x-B\"}}\n y := x // copy the struct\n y.A = \"y-A\"\n y.B[0] = \"y-B\"\n \n fmt.Println(x, y)\n // Outputs \"{x-A [y-B]} {y-A [y-B]}\" -- x was modified!\n}\n\n// slices i kwestia append\n\nfunc doStuff(value []string) {\n fmt.Printf(\"value=%v\\n\", value)\n \n value2 := value[:]\n value2 = append(value2, \"b\")\n fmt.Printf(\"value=%v, value2=%v\\n\", value, value2)\n \n value2[0] = \"z\"\n fmt.Printf(\"value=%v, value2=%v\\n\", value, value2)\n}\n \nfunc main() {\n slice1 := []string{\"a\"} // length 1, capacity 1\n \n doStuff(slice1)\n // Output:\n // value=[a] -- ok\n // value=[a], value2=[a b] -- ok: value unchanged, value2 updated\n // value=[a], value2=[z b] -- ok: value unchanged, value2 updated\n \n slice10 := make([]string, 1, 10) // length 1, capacity 10\n slice10[0] = \"a\"\n \n doStuff(slice10)\n // Output:\n // value=[a] -- ok\n // value=[a], value2=[a b] -- ok: value unchanged, value2 updated\n // value=[z], value2=[z b] -- WTF?!? value changed???\n}\n\n// error handling\n\nlen, err := reader.Read(bytes)\nif err != nil {\n if err == io.EOF {\n // All good, end of file\n } else {\n return err\n }\n}\n\n\n// interfejs nil\n\ntype Explodes interface {\n Bang()\n Boom()\n}\n \n// Type Bomb implements Explodes\ntype Bomb struct {}\nfunc (*Bomb) Bang() {}\nfunc (Bomb) Boom() {}\n \nfunc main() {\n var bomb *Bomb = nil\n var explodes Explodes = bomb\n println(bomb, explodes) // '0x0 (0x10a7060,0x0)'\n if explodes != nil {\n println(\"Not nil!\") // 'Not nil!' What are we doing here?!?!\n explodes.Bang() // works fine\n explodes.Boom() // panic: value method main.Bomb.Boom called using nil *Bomb pointer\n } else {\n println(\"nil!\") // why don't we end up here?\n }\n}\n\n// ubogie struktury danych, takie customowe tracą type safety m.in poprzez castowanie do interface{}\n// kiedyś brak generyków, choć teraz w znacznym stopniu problem został rozwiązany.\n\n(Download the source code for this example: koszowski.go)\nMieszko Grodzicki\ndef add_contents(input_list, contents=[]):\n for val in input_list:\n contents.append(val)\n return contents\n\nprint(add_contents([1])) # [1]\nprint(add_contents([2])) # [1, 2]\n\n(Download the source code for this example: grodzicki.py)\nInstalling Rust\n\nRustup\nSetup an IDE\n\nCLion (you can get\nit for free)\nand RustRover\nVSCode\nand rust-analyzer\nrust-analyzer also works\nwith other IDEs\n\n\n\nUseful tools\n\n\ncargo clippy (for static analysis)\nthere's also cargo check, but it's less powerful than clippy\ncargo fmt (for code formatting)\n\nRust Playground\n\nonline Rust compiler\n\nHello world\nfn main() {\n let name = \"World\";\n println!(\"Hello, {}!\", name); // using the println! macro\n}\n\n\n(Download the source code for this example: hello_world.rs)\nVariables\n#![allow(unused_variables)]\n#![allow(unused_assignments)]\n\nfn main() {\n let x = 40; // inferred type\n let y: i32 = 100; // specified type\n\n {\n let x = 40 + 2; // shadowing\n println!(\"x is {}\", x); // prints 42\n }\n\n // x = 0; // compilation error, variables are by default immutable\n let mut x = 40; // declare as mutable\n x = 0; // now we can reassign\n\n x += 1; // x = x + 1\n}\n\n\n(Download the source code for this example: variables.rs)\nConditionals\n#![allow(unused_variables)]\n\nfn main() {\n let x = 42;\n\n if x == 42 {\n println!(\"x is 42\");\n } else if x == 43 {\n println!(\"x is 43\");\n } else {\n println!(\"x is not 42 or 43\");\n }\n\n // we can also use ifs as expressions\n let a_or_b = if x == 0 {\n \"a\" // notice no semicolon at the end\n } else {\n \"b\"\n };\n}\n\n\n(Download the source code for this example: conditionals.rs)\nLoops\n#![allow(unused_variables)]\n\nfn main() {\n for i in 0..10 {\n println!(\"i is {}\", i); // i in [0, 10)\n }\n\n let mut x = 0;\n\n while x < 50 {\n x += 1;\n }\n\n let mut y = 0;\n let mut iterations = 0;\n loop {\n iterations += 1;\n if iterations % 2 == 0 {\n continue;\n }\n y += 1;\n if y == 10 {\n break;\n }\n }\n\n // we can use labels to refer to a specific loop\n let mut count = 0;\n 'counting_up: loop {\n let mut remaining = 10;\n\n loop {\n if remaining == 9 {\n break;\n }\n if count == 2 {\n break 'counting_up; // ends the outer loop\n }\n remaining -= 1;\n }\n\n count += 1;\n }\n\n // We can use break with a value.\n // Because loops are expressions too,\n // the value we break with will be returned from the functions\n let mut counter = 0;\n let value = loop {\n counter += 1;\n if counter == 10 {\n break 32;\n }\n };\n}\n\n\n(Download the source code for this example: loops.rs)\nFunctions\nfn get_5() -> u32 {\n 5 // we could also write \"return 5;\"\n}\n\nfn print_sum(a: u32, b: u32) {\n println!(\"a + b = {}\", a + b);\n}\n\nfn main() {\n let a = 100;\n print_sum(a, get_5());\n}\n\n\n(Download the source code for this example: functions.rs)\nTest assignment (not graded)\nClick here\nObligatory reading\n\nThe Book, chapters 1-3\n\nAdditional reading\n\nRust By Example\n\n","id":"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/","title":"Introduction to Rust"},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"body":"Why all the fuss?\nEven if you've never seen Rust code before, chances are you still heard the term borrow checker or something about Rust's ownership. Indeed, Rust's ownership model lies at the very core of its uniqueness. But to fully understand it and appreciate it, let's first take a look at how memory management is handled in most popular languages.\n\n\nGarbage Collection - in many high-level programming languages, like Java, Haskell or Python, memory management is done fully by the language, relieving the programmer from this burden. This prevents memory leaks and memory related errors (like use after free), but does come at a cost - there is a runtime overhead, both memory and performance wise, caused by the constantly running garbage collection algorithms and the programmer usually has very little control over when the garbage collection takes place. Also, garbage collection does not prevent concurrency-related errors, such as data races, in any way.\n\n\nMind your own memory - in low-level languages and specific ones like C++, performance comes first so we cannot really afford to run expansive bookkeeping and cleaning algorithms. Most of these languages compile directly to machine code and have no language-specific runtime environment. That means that the only place where memory management can happen is in the produced code. While compilers insert these construction and destruction calls for stack allocated memory, it generally requires a lot of discipline from the programmer to adhere to good practices and patterns to avoid as many memory related issues as possible and one such bug can be quite deadly to the program and a nightmare to find and fix. These languages basically live by the \"your memory, your problem\" mantra.\n\n\nAnd then we have Rust. Rust is a systems programming language and in many ways it's akin to C++ - it's basically low-level with many high-level additions. But unlike C++, it doesn't exactly fall into either of the categories described above, though it's way closer to the second one. It performs no additional management at runtime, but instead imposes a set of rules on the code, making it easier to reason about and thus check for its safety and correctness at compile time - these rules make up Rust's ownership model.\nIn a way, programming in Rust is like pair-programming with a patient and very experienced partner. Rust's compiler will make sure you follow all the good patterns and practices (by having them ingrained in the language itself) and very often even tell you how to fix the issues it finds.\nDisclaimer: when delving deeper into Rust below we will make heavy use of concepts like scopes, moving data, stack and heap, which should have been introduced as part of the C++ course. If you need a refresher of any of these, it's best to do so now, before reading further.\nStart with the basics - ownership\nIn the paragraph above we mentioned a set of rules that comprise Rust's ownership model. The book starts off with the following three as its very foundation:\n\n\nEach value in Rust is tied to a specific variable - we call that variable its owner.\n\n\nThere can only be one owner at a time.\n\n\nWhen the owner goes out of scope, the value will be destroyed (or in Rust terms - dropped).\n\n\nThe third point might make you think about C++ and its automatic storage duration. We will later see that, while very similar at first, Rust expands on these mechanics quite a bit. The following code illustrates the basic version of this:\n{\n let a: i32 = 5; // allocation on the stack, 'a' becomes an owner\n\n // do some stuff with 'a'\n\n} // 'a', the owner, goes out of scope and the value is dropped\n\nSo far, so good. Variables are pushed onto the stack when they enter the scope and destroyed during stack unwinding that happens upon leaving their scope. However, allocating and deallocating simple integers doesn't impress anybody. Let's try something more complex:\n{\n let s = String::from(\"a string\"); // 's' is allocated on the stack, while its contents (\"a string\")\n // are allocated on the heap. 's' is the owner of this String object.\n\n // do some stuff with 's'\n\n} // 's', the owner, goes out of scope and the String is dropped, its heap allocated memory freed\n\nIf you recall the RAII (Resource Acquisition Is Initialization) pattern from C++, the above is basically the same thing. We go two for two now in the similarity department, so... is Rust really any different then? There is a part of these examples that we skipped over - actually doing something with the values.\nMoving around is fun\nLet's expand on the last example. The scoping is not really important for that one, so we don't include it here.\nlet s = String::from(\"a string\"); // same thing, 's' is now an owner\n\nlet s2 = s; // easy, 's2' becomes another owner... right?\n\nprintln!(\"And the contents are: {}\", s); // this doesn't work, can you guess why?\n\nAt first glance everything looks great. If we write this code (well, an equivalent of it) in basically any other popular language, it will compile no issue - but it does not here and there's a good reason why.\nTo understand what's happening, we have to consult the rules again, rule 2 in particular. It says that there can only be one owner of any value at a given time. So, s and s2 cannot own the same object. Okay, makes sense, but what is happening in this line then - let s2 = s;? Experience probably tells you that s just gets copied into s2, creating a new String object. That would result in each variable owning its very own instance of the string and each instance having exactly one owner. Sounds like everyone should be happy now, but wait - in that case the last line should work no issue, right? But it doesn't, so can't be a copy. Let's see now what the compiler actually has to say:\nerror[E0382]: borrow of moved value: `s`\n --> src/main.rs:6:42\n |\n2 | let s = String::from(\"a string\");\n | - move occurs because `s` has type `String`, which does not implement the `Copy` trait\n3 |\n4 | let s2 = s;\n | - value moved here\n5 |\n6 | println!(\"And the contents are: {}\", s);\n | ^ value borrowed here after move\n\n\"value moved here\" - gotcha! So s is being moved to s2, which also means that s2 now becomes the new owner of the string being moved and s cannot be used anymore. In Rust, the default method of passing values around is by move, not by copy. While it may sound a bit odd at first, it actually has some very interesting implications. But before we get to them, let's fix our code, so it compiles now. To do so, we have to explicitly tell Rust to make a copy by invoking the clone method:\nlet s = String::from(\"a string\"); // 's' is an owner\n\nlet s2 = s.clone(); // 's2' now contains its own copy\n\nprintln!(\"And the contents are: {}\", s); // success!\n\nThe compiler is happy now and so are we. The implicit move takes some getting used to, but the compiler is here to help us. Now, let's put the good, old C++ on the table again and compare the two lines:\n\nlet s2 = s; is equivalent to auto s2 = std::move(s);\nlet s2 = s.clone() is equivalent to auto s2 = s\n\nThere are a few important things to note here:\n\n\nMaking a copy is oftentimes not cheap. Memory needs to be allocated and copied, and a call to the system has to be made. We should prefer to move things as much as possible to avoid this cost - in C++ we have a myriad of language features like std::move and r-references to achieve this. Every programmer worth their salt needs to be well versed in all of them to write efficient C++ code and simply forgetting one move can lead to significant performance loss (and this happens to even the most senior devs ever existing, let's not pretend). On the contrary, in Rust you need to make an effort to make a copy and that makes you very aware of the cost you're paying - something that we'll see quite a lot of in the language. Also, if you forget a clone there's no harm done - it just won't compile!\n\n\nHidden in all of this is another nice thing Rust gives us. In C++, nothing prevents you from using variables after they've been moved from, leading to unexpected errors in a more complex code. In Rust, that variable (in our case s) simply becomes invalid and the compiler gives us a nice error about it.\n\n\nBut what about ints?\nA good question to ask. Copying primitives is cheap. And it's not convenient for the programmer to have to always write .clone() after every primitive. If we take a look at the error from the previous example:\nmove occurs because `s` has type `String`, which does not implement the `Copy` trait`\n\nIt says that s was moved because the String type doesn't have the Copy trait. We will talk about traits more in depth in the future lessons, but what this basically means is that String is not specified to be copied by default. All primitive types (i32, bool, f64, char, etc.) and tuples consisting only of primitive types implement the Copy trait.\nExercise\nHow to fix that code?\nfn count_animals(num: u32, animal: String) {\n println!(\"{} {} ...\", num, animal);\n}\n\nfn main() {\n let s = String::from(\"sheep\");\n\n count_animals(1, s);\n count_animals(2, s);\n count_animals(3, s);\n}\n\nLet's borrow some books\nWe now know how to move things around and how to clone them if moving is not possible. But what if making a copy is unnecessary - maybe we just want to let someone look at our resource and keep on holding onto it once they're done. Consider the following example:\nfn read_book(book: String) {\n println!(\"[Reading] {}\", book);\n}\n\nfn main() {\n let book = String::from(\"Merry lived in a big old house. The end.\");\n\n read_book(book.clone());\n\n println!(\"Book is still there: {}\", book);\n}\n\nCloning is pretty excessive here. Imagine recommending a book to your friend and instead of lending it to them for the weekend, you scan it and print an exact copy. Not the best way to go about it, is it? Thankfully, Rust allows us to access a resource without becoming an owner through the use of references and the & operator. This is called a borrow.\nThe adjusted code should look like this:\nfn read_book(book: &String) {\n println!(\"[Reading] {}\", book);\n}\n\nfn main() {\n let book = String::from(\"Merry lived in a big old house. The end.\");\n\n read_book(&book);\n\n println!(\"Book is still there: {}\", book);\n}\n\nAs with everything, references are too, by default, immutable, which means that the read_book function is not able to modify that book passed into it. We can also borrow something mutably by specifying it both in the receiving function signature and the place it gets called. Maybe you want to have your book signed by its author?\nfn sign_book(book: &mut String) {\n book.push_str(\" ~ Arthur Author\");\n}\n\nfn main() {\n // note that the book has to be marked as mutable in the first place\n let mut book = String::from(\"Merry lived in a big old house. The end.\");\n\n sign_book(&mut book); // it's always clear when a parameter might get modified\n\n println!(\"{}\", book); // book is now signed\n}\n\nPretty neat, but doesn't seem that safe right now. Let's try to surprise our friend:\nfn erase_book(book: &mut String) {\n book.clear();\n}\n\nfn read_book(book: &String) {\n println!(\"[Reading] {}\", book);\n}\n\nfn main() {\n let mut book = String::from(\"Merry lived in a big old house. The end.\");\n\n let r = &book; // an immutable borrow\n\n erase_book(&mut book); // a mutable borrow\n\n read_book(r); // would be pretty sad to open a blank book when it was not\n // what we borrowed initially\n\n println!(\"{}\", book);\n}\n\nFortunately for us (and our poor friend just wanting to read), the compiler steps in and doesn't let us do that, printing the following message:\nerror[E0502]: cannot borrow `book` as mutable because it is also borrowed as immutable\n --> src/main.rs:14:14\n |\n12 | let r = &book; // an immutable borrow\n | ----- immutable borrow occurs here\n13 |\n14 | erase_book(&mut book); // a mutable borrow\n | ^^^^^^^^^ mutable borrow occurs here\n15 |\n16 | read_book(r); // would be pretty sad to open a blank book when it was not\n | - immutable borrow later used here\n\nThis is where the famous borrow checker comes in. To keep things super safe, Rust clearly states what can and cannot be done with references and tracks their lifetimes. Exactly one of the following is always true for references to a given resource:\n\n\nThere exists only one mutable reference and no immutable references, or\n\n\nThere is any number of immutable references and no mutable ones.\n\n\nYou may notice a parallel to the readers - writers problem from concurrent programming. In fact, the way Rust's borrow checker is designed lends itself incredibly well to preventing data race related issues.\nDangling references\nRust also checks for dangling references. If we try to compile the following code:\nfn main() {\n let reference_to_nothing = dangle();\n}\n\nfn dangle() -> &String {\n let s = String::from(\"hello\");\n\n &s\n}\n\nwe will get an adequate error:\nerror[E0106]: missing lifetime specifier\n --> src/main.rs:5:16\n |\n5 | fn dangle() -> &String {\n | ^ expected named lifetime parameter\n |\n = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from\nhelp: consider using the `'static` lifetime\n |\n5 | fn dangle() -> &'static String {\n | ^^^^^^^^\n\nThe message above suggests specifing a lifetime for the returned string. In Rust, the lifetime of each variable is also a part of its type, but we will talk more about it later.\nExercise\nOur previous solution using clone() was pretty inefficient. How should this code look now?\nfn count_animals(num: u32, animal: String) {\n println!(\"{} {} ...\", num, animal);\n}\n\nfn main() {\n let s = String::from(\"sheep\");\n\n count_animals(1, s.clone());\n count_animals(2, s.clone());\n count_animals(3, s); // we could've ommitted the clone() here. Why?\n}\n\nEveryone gets a slice\nThe last part of working with references that we will cover in this lesson are slices. A slice in Rust is a view over continuous data. Let us start with a string slice - the &str type.\nNote: for the purposes of these examples we assume we are working with ASCII strings. More comprehensive articles on handling strings are linked at the end of this lesson.\nTo create a string slice from the String object s, we can simply write:\nlet slice = &s[1..3]; // creates a slice of length 2, starting with the character at index 1\n\nThis makes use of the & operator and Rust's range notation to specify the beginning and end of the slice. Thus, we can also write:\nlet slice = &s[2..]; // everything from index 2 till the end\nlet slice = &s[..1]; // only the first byte\nlet slice = &s[..]; // the whole string as a slice\nlet slice = s.as_str(); // also the whole string\n\nYou might have noticed that we always built String values using the from() method and never actually used the string literals directly. What type is a string literal then? Turns out it's the new string slice we just learned about!\nlet slice: &str = \"string literal\";\n\nIn fact, it makes a lot sense - string literals, after all, are not allocated on the heap, but rather placed in a special section of the resulting binary. It's only natural we just reference that place with a slice.\nSlices can also be taken from arrays:\nlet array: [i32; 4] = [42, 10, 5, 2]; // creates an array of four 32 bit integers\nlet slice: &[i32] = &array[1..3]; // results in a slice [10, 5]\n\nExercise\nCan this code still be improved from the previous version utilizing references? Think about the signature of count_animals.\nfn count_animals(num: u32, animal: &String) {\n println!(\"{} {} ...\", num, animal);\n}\n\nfn main() {\n let s = String::from(\"sheep\");\n\n count_animals(1, &s);\n count_animals(2, &s);\n count_animals(3, &s);\n}\n\nFurther reading\n\n\nChar documentation\n\n\nWorking with strings in Rust\n\n\nThe Book, chapter 4\n\n\nAssignment 1 (graded)\nordering in Van Binh\nDeadline: 16.10.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/","title":"Ownership Model"},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"body":"Aggregating data\nBelow is a compact overview of Rust's structs\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nstruct Position(i32, i32); // tuple struct\n\n// Could Hero derive the Copy trait?\n#[derive(Clone, Debug, Eq, PartialEq)]\nstruct Hero {\n name: String,\n level: u32,\n experience: u32,\n position: Position,\n}\n\n// we can add methods to structs using the 'impl' keyword\nimpl Hero {\n // static method (in Rust nomenclature: \"associated function\")\n fn new(name: String) -> Hero {\n Hero {\n name,\n level: 1,\n experience: 0,\n position: Position(0, 0),\n }\n }\n}\n\n// multiple impl blocks are possible for one struct\nimpl Hero {\n // instance method, first argument (self) is the calling instance\n fn distance(&self, pos: Position) -> u32 {\n // shorthand to: `self: &Self`\n // field `i` of a tuple or a tuple struct can be accessed through 'tuple.i'\n (pos.0 - self.position.0).unsigned_abs() + (pos.1 - self.position.1).unsigned_abs()\n }\n\n // mutable borrow of self allows to change instance fields\n fn level_up(&mut self) {\n // shorthand to: `self: &mut Self`\n self.experience = 0;\n self.level += 1;\n }\n\n // 'self' is not borrowed here and will be moved into the method\n fn die(self) {\n // shorthand to: `self: Self`\n println!(\n \"Here lies {}, a hero who reached level {}. RIP.\",\n self.name, self.level\n );\n }\n}\n\nfn main() {\n // Calling associated functions requires scope (`::`) operator.\n let mut hero: Hero = Hero::new(String::from(\"Ferris\"));\n hero.level_up(); // 'self' is always passed implicitly\n\n // fields other than 'name' will be the same as in 'hero'\n let steve = Hero {\n name: String::from(\"Steve The Normal Guy\"),\n ..hero\n };\n\n assert_eq!(hero.level, steve.level);\n\n let mut twin = hero.clone();\n\n // we can compare Hero objects because it derives the PartialEq trait\n assert_eq!(hero, twin);\n twin.level_up();\n assert_ne!(hero, twin);\n hero.level_up();\n assert_eq!(hero, twin);\n\n // we can print out a the struct's debug string with '{:?}'\n println!(\"print to stdout: {:?}\", hero);\n\n hero.die(); // 'hero' is not usable after this invocation, see the method's definiton\n\n // the dbg! macro prints debug strings to stderr along with file and line number\n // dbg! takes its arguments by value, so better borrow them not to have them\n // moved into dbg! and consumed.\n dbg!(\"print to stderr: {}\", &twin);\n\n let pos = Position(42, 0);\n let dist = steve.distance(pos); // no clone here as Position derives the Copy trait\n println!(\"{:?}\", pos);\n assert_eq!(dist, 42);\n}\n\n\n(Download the source code for this example: data_types.rs)\nEnums\nIt is often the case that we want to define a variable that can only take\na certain set of values and the values are known up front. In C you can use an enum for this.\n#include <stdio.h>\n\nenum shirt_size {\n small,\n medium,\n large,\n xlarge\n};\n\nvoid print_size(enum shirt_size size) {\n printf(\"my size is \");\n switch (size) {\n case small:\n printf(\"small\");\n break;\n case medium:\n printf(\"medium\");\n break;\n case large:\n printf(\"large\");\n break;\n case xlarge:\n printf(\"xlarge\");\n break;\n default:\n printf(\"unknown\");\n break;\n }\n printf(\"\\n\");\n}\n\nint main() {\n enum shirt_size my_size = medium;\n print_size(my_size);\n}\n\n\n(Download the source code for this example: enums.c)\nHowever, in C enums are just integers. Nothing prevents us from writing\nint main() {\n enum shirt_size my_size = 666;\n print_size(my_size);\n}\n\nC++ introduces enum classes which are type-safe. Legacy enums are also somewhat safer than in C (same code as above):\n<source>:27:31: error: invalid conversion from 'int' to 'shirt_size' [-fpermissive]\n 27 | enum shirt_size my_size = 666;\n | ^~~\n | |\n | int\n\nSome programming languages (especially functional ones) allow programmers to define\nenums which carry additional information. Such types are usually called tagged unions\nor algebraic data types.\nIn C++ we can use union with an enum tag to define it:\n#include <iostream>\n\n// Taken from: https://en.cppreference.com/w/cpp/language/union\n\n// S has one non-static data member (tag), three enumerator members (CHAR, INT, DOUBLE),\n// and three variant members (c, i, d)\nstruct S\n{\n enum{CHAR, INT, DOUBLE} tag;\n union\n {\n char c;\n int i;\n double d;\n };\n};\n\nvoid print_s(const S& s)\n{\n switch(s.tag)\n {\n case S::CHAR: std::cout << s.c << '\\n'; break;\n case S::INT: std::cout << s.i << '\\n'; break;\n case S::DOUBLE: std::cout << s.d << '\\n'; break;\n }\n}\n\nint main()\n{\n S s = {S::CHAR, 'a'};\n print_s(s);\n s.tag = S::INT;\n s.i = 123;\n print_s(s);\n}\n\n\n(Download the source code for this example: tagged_union.cpp)\nC++17 introduced a new feature called variant which generalizes this concept.\nYou can read more about it here.\nJava has a more or less analogous feature called sealed classes\nsince version 17.\nEnums in Rust\nLet's see how they are defined in Rust.\n#![allow(unused_assignments)]\n#![allow(unused_variables)]\n#![allow(dead_code)]\n\n#[derive(Debug)]\nenum NamedSize {\n Small,\n Medium,\n Large,\n XL,\n}\n\n#[derive(Debug)]\nenum ShirtSize {\n Named(NamedSize),\n Numeric(u32),\n}\n\nfn main() {\n println!(\n \"Isn't it strange that some clothes' sizes are adjectives like {:?},\",\n ShirtSize::Named(NamedSize::Small)\n );\n println!(\n \"but sometimes they are numbers like {:?}?\",\n ShirtSize::Numeric(42)\n );\n}\n\n\n(Download the source code for this example: enums.rs)\nIn Rust, enums are a core feature of the language.\nYou may have heard that one of Rust's defining characteristics is\nthe absence of \"the billion dollar mistake\".\nSo what can we do to say that a value is missing if there is no null?\nIn Rust, we can use the Option type to represent the absence of a value.\nOption is defined as:\nenum Option<T> {\n Some(T),\n None,\n}\n\nThe <T> part is called the \"type parameter\" and it causes Option to be generic.\nWe won't go deeper into this for now.\nThe fact that variables which could be null in other languages have a different type in Rust is\nthe solution to the billion dollar mistake!\n#![allow(unused_assignments)]\n#![allow(unused_variables)]\n#![allow(dead_code)]\n\nfn main() {\n let mut not_null: i32 = 42;\n not_null = 43;\n // not_null = None; // this won't compile because it's a different type!\n\n let mut nullable: Option<i32> = Some(42);\n nullable = None;\n nullable = Some(43);\n\n // such construction is rare, but possible\n let mut double_nullable: Option<Option<i32>> = Some(Some(42));\n // assert_ne!(double_nullable, Some(42)); // this won't even compile because it's a different type!\n double_nullable = None;\n double_nullable = Some(None);\n\n // None and Some(None) are different!\n assert_ne!(double_nullable, None);\n\n // Now recall that division by 0 *panics*\n // A panic is an unrecoverable error\n // It is not an exception!\n // And in Rust there are no exceptions, so there are no try/catch blocks\n // Now let's imagine that we want to divide one number by another\n fn divide(dividend: i32, divisor: i32) -> i32 {\n dividend / divisor\n }\n\n // We get the divisor from the user, so it can be 0\n // We want to handle this situation gracefully - we don't want to crash the program!\n // We can do this by using the Option<T> type\n fn safe_divide(dividend: i32, divisor: i32) -> Option<i32> {\n if divisor == 0 {\n None\n } else {\n Some(dividend / divisor)\n }\n }\n\n // Fortunately, such a function is already included in the standard library\n let number: i32 = 42;\n // We need to specify the type explicitly\n // because checked_div is implemented for all integer types\n // and Rust won't know which type we want to use\n assert_eq!(number.checked_div(2), Some(21));\n assert_eq!(number.checked_div(0), None);\n\n // Now let's imagine we search for a value in an array.\n let numbers = [1, 2, 3, 4, 5];\n let three = numbers.iter().copied().find(|&x| x == 3);\n assert_eq!(three, Some(3));\n let seven = numbers.iter().copied().find(|&x| x == 7);\n assert_eq!(seven, None);\n // We won't delve deeper into the details of how iterators work for now,\n // but the key takeaway is that there are no sentinel or special values like `nullptr` in Rust\n\n // Usually there are two kinds of methods:\n // ones that will panic if the argument is incorrect,\n // numbers[8]; // this will panic!\n // and `checked` ones that return an Option\n assert_eq!(numbers.get(8), None);\n\n // We can use `unwrap` to get the value out of an Option\n // but we must be absolutely sure that the Option is Some, otherwise we'll get a panic\n // numbers.get(8).unwrap(); // this will panic!\n assert_eq!(numbers.get(8).copied().unwrap_or(0), 0); // or we can provide a default value\n\n // Usually instead of unwrapping we use pattern matching, we'll get to this in a minute\n // but first let's see what else we can do with an option\n let number: Option<i32> = Some(42);\n // We can use `map` to transform the value inside an Option\n let doubled = number.map(|x| x * 2);\n assert_eq!(doubled, Some(84));\n // We can use flatten to reduce one level of nesting\n let nested = Some(Some(42));\n assert_eq!(nested.flatten(), Some(42));\n // We can use `and_then` to chain multiple options\n // This operation is called `flatmap` in some languages\n let chained = number\n .and_then(|x| x.checked_div(0))\n .and_then(|x| x.checked_div(2));\n assert_eq!(chained, None);\n\n // The last two things we'll cover here are `take` and `replace`\n // They are important when dealing with non-Copy types\n // `take` will return the value inside an Option and leave a None in its place\n let mut option: Option<i32> = None;\n // Again, we need to specify the type\n // Even though we want to say that there is no value inside the Option,\n // this absent value must have a concrete type!\n assert_eq!(option.take(), None);\n assert_eq!(option, None);\n\n let mut x = Some(2);\n let y = x.take();\n assert_eq!(x, None);\n assert_eq!(y, Some(2));\n\n // `replace` can be used to swap the value inside an Option\n let mut x = Some(2);\n let old = x.replace(5);\n assert_eq!(x, Some(5));\n assert_eq!(old, Some(2));\n\n let mut x = None;\n let old = x.replace(3);\n assert_eq!(x, Some(3));\n assert_eq!(old, None);\n}\n\n\n(Download the source code for this example: option.rs)\nPattern matching\nPattern matching is a powerful feature of Rust and many functional languages, but it's slowly making\nits way into imperative languages like Java and Python as well.\n#![allow(dead_code)]\n#![allow(unused_variables)]\n\nfn main() {\n // Pattern matching is basically a switch on steroids.\n let number = rand::random::<i32>();\n match number % 7 {\n 0 => println!(\"{number} is divisible by 7\"),\n 1 => println!(\"{number} is *almost* divisible by 7\"),\n _ => println!(\"{number} is not divisible by 7\"),\n }\n\n #[derive(Debug)]\n enum Color {\n Pink,\n Brown,\n Lime,\n }\n\n let color = Color::Lime;\n match color {\n Color::Pink => println!(\"My favorite color!\"),\n _ => println!(\"Not my favorite color!\"), // _ is a wildcard\n // Rust will statically check that we covered all cases or included a default case.\n }\n\n // We can also use pattern matching to match on multiple values.\n match (color, number % 7) {\n (Color::Pink, 0) => println!(\"My favorite color and number!\"),\n (Color::Pink, _) => println!(\"My favorite color!\"),\n (_, 0) => println!(\"My favorite number!\"),\n (_, _) => println!(\"Not my favorite color or number!\"),\n }\n // (This is not special syntax, we're just pattern matching tuples.)\n\n // But we can also *destructure* the value\n struct Human {\n age: u8,\n favorite_color: Color,\n }\n\n let john = Human {\n age: 42,\n favorite_color: Color::Pink,\n };\n\n match &john {\n Human {\n age: 42,\n favorite_color: Color::Pink,\n } => println!(\"Okay, that's John!\"),\n Human {\n favorite_color: Color::Pink,\n ..\n } => println!(\"Not John, but still his favorite color!\"),\n _ => println!(\"Somebody else?\"),\n }\n\n // Note two things:\n // 1. Color is *not* Eq, so we can't use == to compare it, but pattern matching is fine.\n // 2. We *borrowed* the value, so we can use it after the match.\n\n println!(\"John is {} years old and still kicking!\", john.age);\n\n // To save some time, we can use `if let` to match against only one thing\n // We could also use `while let ... {}` in the same way\n if let Color::Pink = &john.favorite_color {\n println!(\"He's also a man of great taste\");\n }\n\n // We can match ranges...\n match john.age {\n 0..=12 => println!(\"John is a kid!\"),\n 13..=19 => println!(\"John is a teenager!\"),\n 20..=29 => println!(\"John is a young adult!\"),\n 30..=49 => println!(\"John is an adult!\"),\n 50..=69 => println!(\"John is mature!\"),\n _ => println!(\"John is old!\"),\n }\n\n // We can use match and capture the value at the same time.\n match john.age {\n age @ 0..=12 => println!(\"John is a kid, age {}\", age),\n age @ 13..=19 => println!(\"John is a teenager, age {}\", age),\n age @ 20..=29 => println!(\"John is a young adult, age {}\", age),\n age @ 30..=49 => println!(\"John is an adult, age {}\", age),\n age @ 50..=69 => println!(\"John is mature, age {}\", age),\n age => println!(\"John is old, age {}\", age),\n }\n\n // We can use guards to check for multiple conditions.\n match john.age {\n age @ 12..=19 if age % 2 == 1 => println!(\"John is an *odd* teenager, age {}\", age),\n age if age % 2 == 0 => println!(\"John is an *even* man, age {}\", age),\n _ => println!(\"John is normal\"),\n }\n\n // Finally, let's look at some references now\n let reference: &i32 = &4;\n\n match reference {\n &val => println!(\"Value under reference is: {}\", val),\n }\n\n // `ref` can be used to create a reference when destructuring\n let Human {\n age,\n ref favorite_color,\n } = john;\n // `john` is still valid, because we borrowed using `ref`\n if let Color::Pink = &john.favorite_color {\n println!(\"John still has his color - {:?}!\", favorite_color);\n }\n\n let mut john = john;\n\n // `ref mut` borrows mutably\n let Human {\n age,\n ref mut favorite_color,\n } = john;\n // We use `*` to dereference\n *favorite_color = Color::Brown;\n println!(\n \"Tastes do change with time and John likes {:?} now.\",\n john.favorite_color\n );\n}\n\n\n(Download the source code for this example: pattern_matching.rs)\nResult\nWe said there are no exceptions in Rust and panics mean errors which cannot be caught.\nSo how do we handle situations which can fail? That's where the Result type comes in.\n#![allow(dead_code)]\n#![allow(unused_variables)]\n\nuse std::fs::File;\nuse std::io;\nuse std::io::Read;\n\n// Let's try reading from a file.\n// Obviously this can fail.\nfn first_try() -> io::Result<String> {\n let file = File::open(\"/dev/random\");\n match file {\n Ok(mut file) => {\n // We got a file!\n let mut buffer = vec![0; 128];\n // Matching each result quickly become tedious...\n match file.read_exact(&mut buffer) {\n Ok(_) => {\n let gibberish = String::from_utf8_lossy(&buffer);\n Ok(gibberish.to_string())\n }\n Err(error) => Err(error),\n }\n }\n Err(error) => {\n Err(error) // This is needed in order to change the type from `io::Result<File>` to `io::Result<()>`\n }\n }\n}\n\n// The '?' operator allows us to return early in case of an error\n// (it automatically converts the error type)\nfn second_try(filename: &'static str) -> io::Result<String> {\n let mut file = File::open(filename)?;\n let mut buffer = vec![0; 128];\n file.read_exact(&mut buffer)?;\n let gibberish = String::from_utf8_lossy(&buffer);\n Ok(gibberish.to_string())\n}\n\nfn main() {\n let filenames = [\n \"/dev/random\",\n \"/dev/null\",\n \"/dev/cpu\",\n \"/dev/fuse\",\n \"there_certainly_is_no_such_file\",\n ];\n for filename in filenames {\n println!(\"Trying to read from '{}'\", filename);\n match second_try(filename) {\n Ok(gibberish) => println!(\"{}\", gibberish),\n Err(error) => println!(\"Error: {}\", error),\n }\n }\n}\n\n\n(Download the source code for this example: result.rs)\nObligatory reading\n\nThe Book, chapters 5,\n6,\n8\nand 9\nOption docs\nResult docs\n\nAssignment 2 (graded)\nCommunications\nDeadline: 23.10.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/","title":"Data Types"},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"body":"Feedback\nUnwrapping\nInstead of this:\nif self.favorite_color.is_some() {\n self.favorite_color.as_mut().unwrap().lighten();\n}\n\ndo this:\nif let Some(ref mut color) = self.favorite_color {\n color.lighten();\n}\n\nor\nif let Some(color) = &mut self.favorite_color {\n color.lighten();\n}\n\n(unwrapping is a code smell)\nSpot the overflow\nColor::Rgb(r, g, b) => *b > (*r + *g) / 2,\n\n1/3\nColor::Rgb(r, g, b) => (*b as u16) * 3 > (*r as u16) + (*g as u16) + (*b as u16),\n\nNo need to cast to u16. If b accounts for 1/3 of the sum, it's enough to check that it's bigger than both r and g.\nFormat\nColor::Named(ref mut name) => *name = \"light \".to_string() + name,\n\nThere's a format! macro for this.\nColor::Named(ref mut name) => *name = format!(\"light {name}\"),\n\nFrom vs Into vs as\nlet tmp1: u32 = <u8 as Into<u32>>::into(*c) * 2;\n\nThis could be written as\nlet tmp1: u32 = (*c).into() * 2;\n\nor even simpler (note the omission of the type annotation):\nlet tmp1 = u32::from(*c) * 2;\n\nHowever in most cases of numeric conversion you can just use as:\nlet tmp1 = *c as u32 * 2;\n\nInto trait docs\nSaturating addition\nThere's a saturating_add method on u8 which does exactly what we wanted.\nBut it was fun watching you struggle with it :)\nfn lighten(&mut self) {\n match self {\n Color::Named(name) => *name = \"light \".to_string() + name,\n Color::Rgb(r, g, b) => {\n *r = r.saturating_add(10);\n *g = g.saturating_add(10);\n *b = b.saturating_add(10);\n }\n }\n}\n\nExchange\nfn exchange_items(robot1: &mut Robot, robot2: &mut Robot) {\n mem::swap(&mut robot1.held_item, &mut robot2.held_item);\n}\n\nSwap is the preferred way to exchange the contents of two variables.\nRegex? Nope\nThere's no need to use a regex here. String has a contains method.\nIf you really want to use a regex,\nyou can use the lazy_static crate to avoid recompiling the regex every time you call the function.\n","id":"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/","title":"Feedback #1"},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"body":"Type traits\nTraits are a way to defined common behavior between different types. They can be compared to interfaces from many other mainstream languages or to type classes from Haskell, however, Rust is not an object-oriented language and there are some notable differences between type traits and Java interfaces.\nThe way we describe behavior in Rust is through methods. Traits consist of a set of these methods which then should be implemented by a type. We've already encountered examples of these, like the Clone trait which specified that the clone() method can be called on some given type. Now, let's take a deeper look and try defining our own trait.\n#![allow(dead_code)]\n\ntrait Summary {\n fn summarize(&self) -> String;\n}\n\nstruct NewsArticle {\n headline: String,\n location: String,\n author: String,\n content: String,\n}\n\nimpl Summary for NewsArticle {\n fn summarize(&self) -> String {\n format!(\"{}, by {} ({})\", self.headline, self.author, self.location)\n }\n}\n\nstruct Tweet {\n username: String,\n content: String,\n}\n\nimpl Summary for Tweet {\n fn summarize(&self) -> String {\n format!(\"{}: {}\", self.username, self.content)\n }\n}\n\nfn main() {\n let tweet = Tweet {\n username: String::from(\"horse_ebooks\"),\n content: String::from(\"of course, as you probably already know, people\"),\n };\n\n println!(\"1 new tweet: {}\", tweet.summarize());\n}\n\n\n(Download the source code for this example: basic_trait.rs)\nDefault implementations\nTrait definitions can also be provided with default implementations of behaviors.\n#![allow(dead_code)]\n\nstruct Upload {\n filename: String,\n}\n\n#[allow(dead_code)]\nstruct Photo {\n filename: String,\n width: u32,\n height: u32,\n}\n\ntrait Description {\n fn describe(&self) -> String {\n String::from(\"No description available.\")\n }\n}\n\n// All default implementations\nimpl Description for Upload {}\n\n// Default implementations can be overwritten\nimpl Description for Photo {\n fn describe(&self) -> String {\n format!(\"{} ({} x {})\", self.filename, self.width, self.height)\n }\n}\n\n// Default implementations can rely on methods with no defaults\ntrait Size {\n fn width(&self) -> u32;\n fn height(&self) -> u32;\n\n fn size(&self) -> u32 {\n self.width() * self.height()\n }\n}\n\nimpl Size for Photo {\n fn width(&self) -> u32 {\n self.width\n }\n\n fn height(&self) -> u32 {\n self.height\n }\n\n // Using default impl of `size()`\n}\n\nfn main() {\n let upload = Upload {\n filename: String::from(\"notes.txt\"),\n };\n\n println!(\"Upload: {}\", upload.describe());\n\n let photo = Photo {\n filename: String::from(\"stock_crustacean.png\"),\n width: 100,\n height: 150,\n };\n\n println!(\"Photo: {}\", photo.describe());\n println!(\"Size: {}\", photo.size());\n}\n\n\n(Download the source code for this example: trait_default.rs)\nWhat about derive?\nThere is a trait-related thing we have used quite extensively and not explained yet, namely the #[derive] attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.\nDerivable traits:\n\n\nEquality traits: Eq, PartialEq and comparison traits: Ord and PartialOrd. The Partial- versions exist because there are types which don't fulfill the reflexivity requirement of equality (NaN != NaN) or do not form a total order ( NaN < 0.0 == false and NaN >= 0.0 == false).\n\n\nData duplication traits: Clone and Copy\n\n\nHash - allows using values of that type as keys in a hashmap\n\n\nDefault - provides a zero-arg constructor function\n\n\nDebug - provides a formatting of the value which can be used in debugging context. It should NOT be implemented manually. In general, if it's possible to derive the Debug, there are no reasons against doing it.\n\n\nWhen is it possible to derive a trait?\nWhen all fields of a struct/variants of an enum implement that trait.\nShould all traits always be derived if it is possible?\nNo. Although it may be tempting to just slap #[derive(Clone, Copy)] everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.\nGenerics\nSuppose we want to find the largest element in a sequence and return it. Very much on purpose, we didn't specify what type these elements would be - ideally, we would love it to work on all types that have a defined notion of a largest element. However, to make things simpler for now, let's focus only on two primitive types: i32 and char. Let's try to write the code:\nfn largest_i32(list: &[i32]) -> i32 {\n let mut largest = list[0];\n\n for &item in list {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nfn largest_char(list: &[char]) -> char {\n let mut largest = list[0];\n\n for &item in list {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nfn main() {\n let number_list = vec![34, 50, 25, 100, 65];\n\n let result = largest_i32(&number_list);\n println!(\"The largest number is {}\", result);\n\n let char_list = vec!['y', 'm', 'a', 'q'];\n\n let result = largest_char(&char_list);\n println!(\"The largest char is {}\", result);\n}\n\n\n(Download the source code for this example: non_generic.rs)\nPerfect, it works! Now only twenty more types to go...\nFortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.\nfn largest<T>(list: &[T]) -> T {\n let mut largest = list[0];\n\n for &item in list {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nCleaner already - we merged possibly very many implementations into one. But, when we try to compile this:\nerror[E0369]: binary operation `>` cannot be applied to type `T`\n --> src/main.rs:5:17\n |\n5 | if item > largest {\n | ---- ^ ------- T\n | |\n | T\n |\nhelp: consider restricting type parameter `T`\n |\n1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {\n | ++++++++++++++++++++++\n\nSince T can be of absolutely any type now, the compiler cannot be sure that operator > is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:\nfn largest<T: PartialOrd>(list: &[T]) -> T {\n let mut largest = list[0];\n\n for &item in list {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nWe call this a trait bound, a way to provide constraints on what kind of types we are talking about in a given context. This implementation almost works now. Let's look at the new error.\nerror[E0508]: cannot move out of type `[T]`, a non-copy slice\n --> src/main.rs:2:23\n |\n2 | let mut largest = list[0];\n | ^^^^^^^\n | |\n | cannot move out of here\n | move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait\n | help: consider borrowing here: `&list[0]`\n\nerror[E0507]: cannot move out of a shared reference\n --> src/main.rs:4:18\n |\n4 | for &item in list {\n | ----- ^^^^\n | ||\n | |data moved here\n | |move occurs because `item` has type `T`, which does not implement the `Copy` trait\n | help: consider removing the `&`: `item`\n\nOur function attempts to take ownership, but, again, the compiler doesn't know whether T can just be trivially copied. Rust allows us to combine multiple trait bounds together:\nfn largest<T: PartialOrd + Copy>(list: &[T]) -> T {\n let mut largest = list[0];\n\n for &item in list {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nfn main() {\n let number_list = vec![34, 50, 25, 100, 65];\n\n let result = largest(&number_list);\n println!(\"The largest number is {}\", result);\n\n let char_list = vec!['y', 'm', 'a', 'q'];\n\n let result = largest(&char_list);\n println!(\"The largest char is {}\", result);\n}\n\n\n(Download the source code for this example: generic_largest.rs)\nA powerful tool\nThere's a lot more that we can do with generics:\n#![allow(dead_code)]\n\nuse std::fmt::Debug;\n\n// generic enums\nenum OurOption<T> {\n Some(T),\n None,\n}\n\n// generic structs\nstruct Tuple2<T, U> {\n x: T,\n y: U,\n}\n\n// generic implementation\nimpl<T, U> Tuple2<T, U> {\n fn new(x: T, y: U) -> Self {\n Self { x, y }\n }\n}\n\nstruct Pair<T> {\n x: T,\n y: T,\n}\n\n// conditional implementation\nimpl<T: PartialOrd + Copy> Pair<T> {\n fn largest(&self) -> T {\n if self.x > self.y {\n self.x\n } else {\n self.y\n }\n }\n}\n\n// alternative syntax\nimpl<T> Pair<T>\nwhere\n T: PartialOrd + Copy,\n{\n fn smallest(&self) -> T {\n if self.x < self.y {\n self.x\n } else {\n self.y\n }\n }\n}\n\n// Here information about the concrete underlying type is preserved.\nfn cloning_machine<T: Clone + Debug>(item: &T) -> T {\n item.clone()\n}\n\n// Here information about the concrete underlying type is erased.\n// We can only either format or clone the result.\nfn erasing_cloning_machine1(item: &(impl Clone + Debug)) -> impl Clone + Debug {\n item.clone()\n}\n\n// Ditto.\nfn erasing_cloning_machine2<T: Clone + Debug>(item: &T) -> impl Clone + Debug {\n item.clone()\n}\n\nfn main() {\n let _opt = OurOption::Some(10);\n\n let _p1 = Tuple2 { x: 5, y: 10 };\n let _p2 = Tuple2::new(1, 2.5);\n\n let arr = [1, 2, 3];\n\n let arr2 = cloning_machine(&arr);\n let _x = arr2[0]; // This compiles, because `cloning_machine` preserves the type.\n println!(\"{:?}\", arr2);\n\n let arr3 = erasing_cloning_machine1(&arr);\n // arr3[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug`\n println!(\"{:?}\", arr3);\n\n let arr4 = erasing_cloning_machine2(&arr);\n // arr4[0]; // won't compile: cannot index into a value of type `impl std::clone::Clone + std::fmt::Debug`\n println!(\"{:?}\", arr4);\n}\n\n\n(Download the source code for this example: generics.rs)\nA bit more involved example:\nuse std::fmt::{Display, Formatter};\n\ntrait DefaultishablyPrintable<T> {\n fn defaultish_print()\n where\n T: Display + Default,\n {\n println!(\"{}\", T::default())\n }\n}\n\nstruct Foo;\n\nstruct Bar;\n\nimpl Display for Bar {\n fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n f.write_str(\"this is a bar\")\n }\n}\n\nimpl Default for Bar {\n fn default() -> Self {\n Bar // well, we have no other choice\n }\n}\n\nimpl DefaultishablyPrintable<i32> for Foo {}\n\nimpl DefaultishablyPrintable<Bar> for Foo {}\n\nfn main() {\n <Foo as DefaultishablyPrintable<i32>>::defaultish_print();\n <Foo as DefaultishablyPrintable<Bar>>::defaultish_print();\n}\n\n\n(Download the source code for this example: generics_fun.rs)\nStatic vs dynamic dispatch\ntrait Speak {\n fn speak(&self) -> &'static str;\n}\n\nstruct Dog;\n\nimpl Speak for Dog {\n fn speak(&self) -> &'static str {\n \"Hau hau\" // it's a Polish dog!\n }\n}\n\nstruct Human;\n\nimpl Speak for Human {\n fn speak(&self) -> &'static str {\n \"Hello world\"\n }\n}\n\n// It works like templates in C++\n// A different function will be generated for each T during compilation\n// This process is called \"monomorphization\"\nfn static_dispatch<T: Speak>(speaking: &T) {\n println!(\"{}!\", speaking.speak());\n}\n\n// Only one copy of that function will exist in the compiled binary\nfn dynamic_dispatch(speaking: &dyn Speak) {\n println!(\"{}!\", speaking.speak());\n}\n\nfn main() {\n let dog = Dog;\n let human = Human;\n\n static_dispatch(&dog);\n static_dispatch(&human);\n\n dynamic_dispatch(&dog);\n dynamic_dispatch(&human);\n\n // The observable behavior is identical\n // Static dispatch in general is a bit faster,\n // because there is no need to perform a \"vtable lookup\".\n // But it can also result in bigger binary sizes.\n}\n\n\n(Download the source code for this example: static_dynamic_dispatch.rs)\nLifetimes\nGoing back to the lesson about ownership, if we try to compile the following code:\n{\n let r;\n\n {\n let x = 5;\n r = &x;\n }\n\n println!(\"r: {}\", r);\n}\n\nwe should expect to get an error:\nerror[E0597]: `x` does not live long enough\n --> src/main.rs:7:17\n |\n7 | r = &x;\n | ^^ borrowed value does not live long enough\n8 | }\n | - `x` dropped here while still borrowed\n9 |\n10 | println!(\"r: {}\", r);\n | - borrow later used here\n\nCourtesy of the borrow checker, we didn't end up with a dangling reference. But what exactly is happening behind the scenes? Rust introduces a concept of annotated lifetimes, where the lifetime of each value is being marked and tracked by the checker. Let's look at some examples:\n{\n let r; // ---------+-- 'a\n // |\n { // |\n let x = 5; // -+-- 'b |\n r = &x; // | |\n } // -+ |\n // |\n println!(\"r: {}\", r); // |\n} // ---------+\n\n{\n let x = 5; // ----------+-- 'b\n // |\n let r = &x; // --+-- 'a |\n // | |\n println!(\"r: {}\", r); // | |\n // --+ |\n} // ----------+\n\nAnnotations\nLet's consider the following code finding the longer out of two strings:\nfn longest(x: &str, y: &str) -> &str {\n if x.len() > y.len() {\n x\n } else {\n y\n }\n}\n\nfn main() {\n let string1 = String::from(\"abcd\");\n let string2 = \"xyz\";\n\n let result = longest(string1.as_str(), string2);\n println!(\"The longest string is {}\", result);\n}\n\nIf we try to compile this, we will get an error:\nerror[E0106]: missing lifetime specifier\n --> src/main.rs:9:33\n |\n9 | fn longest(x: &str, y: &str) -> &str {\n | ---- ---- ^ expected named lifetime parameter\n |\n = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`\nhelp: consider introducing a named lifetime parameter\n |\n9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n | ++++ ++ ++ ++\n\nThis is because Rust doesn't know which of the two provided strings (x or y) will be returned from the function. And because they potentially have different lifetimes, the lifetime of what we are returning remains unclear to the compiler - it needs our help.\nRust provides syntax for specifying lifetimes. The lifetime parameter name from the example (a) doesn't have any concrete meaning - it's just an arbitrary name for this one lifetime.\n&i32 // a reference\n&'a i32 // a reference with an explicit lifetime\n&'a mut i32 // a mutable reference with an explicit lifetime\n\nSo, knowing this, let's address the compiler's demands.\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n if x.len() > y.len() {\n x\n } else {\n y\n }\n}\n\nWhen working with lifetimes, our work will usually revolve around specifying relationships between lifetimes of different values so that the compiler can successfully reason about the program's safety. In the context of the example above, this signature means that both of the function's arguments and its output will live at least as long as lifetime 'a. In practice, this means that the output's lifetime will be equal to the smaller of the two inputs' lifetimes.\nfn longest<'a>(first: &'a str, second: &'a str) -> &'a str {\n if first.len() > second.len() {\n first\n } else {\n second\n }\n}\n\nfn main() {\n let string1 = String::from(\"long string is long\");\n\n {\n let string2 = String::from(\"xyz\");\n let result = longest(string1.as_str(), string2.as_str());\n println!(\"The longest string is {}\", result);\n }\n\n // This doesn't compile - incorrect lifetimes\n //\n // let string1 = String::from(\"long string is long\");\n // let result;\n // {\n // let string2 = String::from(\"xyz\");\n // result = longest(string1.as_str(), string2.as_str());\n // }\n // println!(\"The longest string is {}\", result);\n}\n\n\n(Download the source code for this example: lifetimes_basic.rs)\nTrying to compile the second variant displeases the compiler (just like we hoped).\nerror[E0597]: `string2` does not live long enough\n --> src/main.rs:6:44\n |\n6 | result = longest(string1.as_str(), string2.as_str());\n | ^^^^^^^^^^^^^^^^ borrowed value does not live long enough\n7 | }\n | - `string2` dropped here while still borrowed\n8 | println!(\"The longest string is {}\", result);\n | ------ borrow later used here\n\nLifetime elision\nWe now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this lifetime elision.\nfn first_two(seq: &[u32]) -> &[u32] {\n if seq.len() < 2 {\n seq\n } else {\n &seq[..2]\n }\n}\n\nfn main() {\n let seq = [1, 2, 3, 4];\n\n println!(\n \"First two elements of the sequence: {:?}\",\n first_two(&seq[..])\n );\n}\n\n\n(Download the source code for this example: lifetimes_elision.rs)\nThe above works, even though we didn't specify any lifetime parameters at all. The reason lies in the rules we mentioned, which are as follows (where input lifetimes are lifetimes on parameters and output lifetimes are lifetimes on return values):\n\n\nEach parameter that is a reference gets its own lifetime parameter.\n\n\nIf there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters.\n\n\nIf there are multiple input lifetime parameters, but one of them is &self or &mut self, the lifetime of self is assigned to all output lifetime parameters.\n\n\nLet's try to understand how the compiler inferred the lifetimes of our first_two functions. We start with the following signature:\nfn first_two(seq: &[u32]) -> &[u32] {\n\nThen, we apply the first rule:\nfn first_two<'a>(seq: &'a [u32]) -> &[u32] {\n\nNext, we check the second rule. It applies here as well.\nfn first_two<'a>(seq: &'a [u32]) -> &'a [u32] {\n\nWith that, we arrive at a state where all lifetimes are specified.\nStatic lifetime\nThere exists one special lifetime called 'static, which means that a reference can live for the entire duration of the program. All string literals are annotated with this lifetime as they are stored directly in the program's binary. Full type annotation of a string literal in Rust is therefore as follows:\nlet s: &'static str = \"I have a static lifetime.\";\n\nTrait + lifetimes - a challenging tandem\nLet's go back to our basic_trait.rs example. The Summary trait was really wasteful: it always allocated the Strings on heap, even though we only needed to display the formatted string, and we could do that without allocations. How? By using Display trait, of course.\nThe simplest possible optimisation would be like this:\n#![allow(dead_code)]\n\nuse std::fmt::Display;\n\nstruct NewsArticle {\n headline: String,\n location: String,\n author: String,\n content: String,\n}\n\nimpl Display for NewsArticle {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n write!(\n f,\n \"{}, by {} ({})\",\n self.headline, self.author, self.location\n )\n }\n}\n\nstruct Tweet {\n username: String,\n content: String,\n}\n\nimpl Display for Tweet {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n write!(f, \"{}: {}\", self.username, self.content)\n }\n}\n\nfn main() {\n let tweet = Tweet {\n username: String::from(\"horse_ebooks\"),\n content: String::from(\"of course, as you probably already know, people\"),\n };\n\n println!(\"1 new tweet: {}\", tweet);\n}\n\n\n(Download the source code for this example: basic_trait_display.rs)\nThis eliminates the heap allocations, but there's another catch. What if NewsArticle already had another (non-summarizing) Display implementation? We would end up in a double-trait-implementation conflict, which is a compile-time error.\nWe can solve the one-type-one-trait-impl problem by introducing another type just for summarizing. The first attempt could be to use generics in traits:\n#![allow(dead_code)]\n\nuse std::fmt::Display;\n\ntrait Summary<'a, Summarizer: Display> {\n fn summarize(&'a self) -> Summarizer;\n}\n\nstruct NewsArticle {\n headline: String,\n location: String,\n author: String,\n content: String,\n}\n\nimpl Display for NewsArticle {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n writeln!(\n f,\n \"{}, by {} ({})\",\n self.headline, self.author, self.location\n )?;\n f.write_str(&self.content)\n }\n}\n\nstruct NewsArticleSummarizer<'a>(&'a NewsArticle);\n\nimpl Display for NewsArticleSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let article = self.0;\n write!(\n f,\n \"{}, by {} ({})\",\n article.headline, article.author, article.location\n )\n }\n}\n\nimpl<'a> Summary<'a, NewsArticleSummarizer<'a>> for NewsArticle {\n fn summarize(&'a self) -> NewsArticleSummarizer<'a> {\n NewsArticleSummarizer(self)\n }\n}\n\nstruct Tweet {\n username: String,\n content: String,\n}\n\nstruct TweetSummarizer<'a>(&'a Tweet);\n\nimpl Display for TweetSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let tweet = self.0;\n write!(f, \"{}: {}\", tweet.username, tweet.content)\n }\n}\n\nimpl<'a> Summary<'a, TweetSummarizer<'a>> for Tweet {\n fn summarize(&'a self) -> TweetSummarizer<'a> {\n TweetSummarizer(self)\n }\n}\n\nimpl<'a> Summary<'a, NewsArticleSummarizer<'a>> for Tweet {\n fn summarize(&'a self) -> NewsArticleSummarizer<'a> {\n unimplemented!(\"This is only to make code type-check and compile.\");\n }\n}\n\nfn main() {\n let empty_article = NewsArticle {\n headline: \"\".into(),\n location: String::new(),\n author: String::default(),\n content: Default::default(),\n };\n println!(\"1 new article: {}\", empty_article.summarize());\n\n let tweet = Tweet {\n username: String::from(\"horse_ebooks\"),\n content: String::from(\"of course, as you probably already know, people\"),\n };\n\n // Compile error: `type annotations needed; multiple `impl`s satisfying `Tweet: Summary<'_, _>` found`\n // println!(\"1 new tweet: {}\", tweet.summarize());\n println!(\n \"1 new tweet: {}\",\n <Tweet as Summary<'_, TweetSummarizer>>::summarize(&tweet)\n );\n println!(\n \"1 new tweet: {}\",\n <Tweet as Summary<'_, NewsArticleSummarizer>>::summarize(&tweet)\n );\n}\n\n\n(Download the source code for this example: trait_generic_type.rs)\nThe problem here is that nothing hinders us from implement the trait (with various type parameters) for the same type, which leads to awkward ambiguity when calling the trait's methods (see main fn).\nThe use of generic types in Summary trait makes it semantics like this:\n\nA type can be summarized with any type supporting it.\n\nWhen we want the trait to require exactly one possible generic implementation for a given type, we can leverage associated types. Example here:\n#![allow(dead_code)]\n\nuse std::fmt::Display;\n\ntrait Summary<'a> {\n type Summarizer: Display;\n\n fn summarize(&'a self) -> Self::Summarizer;\n}\n\nstruct NewsArticle {\n headline: String,\n location: String,\n author: String,\n content: String,\n}\n\nimpl Display for NewsArticle {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n writeln!(\n f,\n \"{}, by {} ({})\",\n self.headline, self.author, self.location\n )?;\n f.write_str(&self.content)\n }\n}\n\nstruct NewsArticleSummarizer<'a>(&'a NewsArticle);\n\nimpl Display for NewsArticleSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let article = self.0;\n write!(\n f,\n \"{}, by {} ({})\",\n article.headline, article.author, article.location\n )\n }\n}\n\nimpl<'a> Summary<'a> for NewsArticle {\n type Summarizer = NewsArticleSummarizer<'a>;\n fn summarize(&'a self) -> Self::Summarizer {\n NewsArticleSummarizer(self)\n }\n}\n\nstruct Tweet {\n username: String,\n content: String,\n}\n\nstruct TweetSummarizer<'a>(&'a Tweet);\n\nimpl Display for TweetSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let tweet = self.0;\n write!(f, \"{}: {}\", tweet.username, tweet.content)\n }\n}\n\nimpl<'a> Summary<'a> for Tweet {\n type Summarizer = TweetSummarizer<'a>;\n fn summarize(&'a self) -> Self::Summarizer {\n TweetSummarizer(self)\n }\n}\n\nfn main() {\n let tweet = Tweet {\n username: String::from(\"horse_ebooks\"),\n content: String::from(\"of course, as you probably already know, people\"),\n };\n\n println!(\"1 new tweet: {}\", tweet.summarize());\n}\n\n\n(Download the source code for this example: trait_associated_type.rs)\nThe use of associated types in Summary trait makes it semantics like this:\n\nA type can be summarized with at most one specific type.\n\nYet another approach (arguably, the cleanest one) would be to use the impl trait syntax in a trait (quite recently stabilized!).\nExample:\n#![allow(dead_code)]\n\nuse std::fmt::Display;\n\ntrait Summary {\n fn summarize(&self) -> impl Display;\n}\n\nstruct NewsArticle {\n headline: String,\n location: String,\n author: String,\n content: String,\n}\n\nimpl Display for NewsArticle {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n writeln!(\n f,\n \"{}, by {} ({})\",\n self.headline, self.author, self.location\n )?;\n f.write_str(&self.content)\n }\n}\n\nstruct NewsArticleSummarizer<'a>(&'a NewsArticle);\n\nimpl Display for NewsArticleSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let article = self.0;\n write!(\n f,\n \"{}, by {} ({})\",\n article.headline, article.author, article.location\n )\n }\n}\n\nimpl Summary for NewsArticle {\n fn summarize(&self) -> impl Display {\n NewsArticleSummarizer(self)\n }\n}\n\nstruct Tweet {\n username: String,\n content: String,\n}\n\nimpl Summary for Tweet {\n fn summarize(&self) -> impl Display {\n struct TweetSummarizer<'a>(&'a Tweet);\n\n impl Display for TweetSummarizer<'_> {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n let tweet = self.0;\n write!(f, \"{}: {}\", tweet.username, tweet.content)\n }\n }\n\n TweetSummarizer(self)\n }\n}\n\nfn main() {\n let tweet = Tweet {\n username: String::from(\"horse_ebooks\"),\n content: String::from(\"of course, as you probably already know, people\"),\n };\n\n println!(\"1 new tweet: {}\", tweet.summarize());\n}\n\n\n(Download the source code for this example: impl_trait.rs)\nObligatory reading\n\n\nThe Book, chapter 10\n\n\nPolymorphism in Rust\n\n\nAssignment 3 (graded)\nPassage Pathing\nDeadline: 30.10.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/","title":"Reasoning About Types"},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"body":"Closures\nClosures (Polish: \"domknięcia\") are anonymous functions that can access variables from the scope in which they were defined.\nClosure syntax\nfn main() {\n #[rustfmt::skip]\n {\n // This is formatted so that without rust-analyzer it renders as well-aligned.\n\n fn add_one_v1 (x: u32) -> u32 { x + 1 } // This is an ordinary function.\n let add_one_v2 = |x: u32| -> u32 { x + 1 }; // Closures use pipes instead of parentheses.\n let add_one_v3 = |x| { x + 1 }; // Both parameters and return value can have their types inferred.\n let add_one_v4 = |x| x + 1 ; // If the body is a single expression, braces can be omitted.\n\n let _res = add_one_v1(0_u32);\n let _res = add_one_v2(0_u32);\n let _res = add_one_v3(0_u32);\n let _res = add_one_v4(0_u32);\n \n // This does not compile, because closures are not generic.\n // Their type is inferred once and stays the same.\n // let _res = add_one_v4(0_i32);\n };\n}\n\n\n(Download the source code for this example: closures_syntax.rs)\nClosures' types\nClosures are unnameable types. That is, each closure gets its own unique type from the compiler,\nbut we cannot name it. Therefore, closures' types must be inferred.\nWe will often use impl keyword with closure traits (e.g., impl Fn) - those traits are described below.\nClosures capture environment\nClosures can capture variables from the environment where they are defined. They can do that in two ways:\n\nCapturing References (borrowing), or\nMoving Ownership.\n\nHOW closures capture variables is one thing.\nBut even more important is WHAT closures do with their captures.\nFunctions & closures hierarchy\nBased on WHAT a closure does with its captures, it implements closure traits:\n\nFnOnce - closures that may move out of their captures environment (and thus called once).\nFnMut - closures that may mutate their captures, but don't move out of their captures environment (so can be called multiple times, but require a mutable reference);\nFn - closures that do not mutate their captures (so can be called multiple times through an immutable reference).\n\nFor completeness, there is a (concrete) type of function pointers:\n\nfn - functions, closures with no captures.\n\nThose traits and the fn type form a hierarchy: fn ⊆ Fn ⊆ FnMut ⊆ FnOnce\n $$ fn \\subseteq Fn \\subseteq FnMut \\subseteq FnOnce $$ -->\nThe following code sample demonstrates various ways to capture environment (borrowing or moving) and various kinds of closures, based on what they do with their captures:\nfn main() {\n borrowing_immutably_closure();\n borrowing_mutably_closure();\n moving_in_nonmutating_closure();\n moving_in_mutating_closure();\n moving_in_moving_out_closure();\n}\n\nfn borrowing_immutably_closure() {\n let list = vec![1, 2, 3];\n println!(\"Before defining closure: {:?}\", list);\n\n let only_borrows = || println!(\"From closure: {:?}\", list);\n\n // This would not really only borrow... (it needs Vec by value).\n // let only_borrows = || std::mem::drop::<Vec<_>>(list);\n\n println!(\"Before calling closure: {:?}\", list);\n only_borrows();\n println!(\"After calling closure: {:?}\", list);\n}\n\nfn borrowing_mutably_closure() {\n let mut list = vec![1, 2, 3];\n println!(\"Before defining closure: {:?}\", list);\n\n let mut borrows_mutably = || list.push(7);\n\n // println!(\"Before calling closure: {:?}\", list);\n borrows_mutably();\n println!(\"After calling closure: {:?}\", list);\n}\n\nfn moving_in_nonmutating_closure() {\n let list = vec![1, 2, 3];\n println!(\"Before defining closure: {:?}\", list);\n\n // This closure would just borrow the list, because it only prints it.\n // However, as spawning threads require passing `impl FnOnce + 'static`,\n // we need to use `move` keyword to force the closure to move `list`\n // into its captured environment.\n std::thread::spawn(move || println!(\"From thread: {:?}\", list))\n .join()\n .unwrap();\n}\n\nfn moving_in_mutating_closure() {\n fn append_42(mut appender: impl FnMut(i32)) {\n appender(42);\n }\n\n let mut appender = {\n let mut list = vec![1, 2, 3];\n println!(\"Before defining closure: {:?}\", list);\n\n // The `move` keyword is necessary to prevent dangling reference to `list`.\n // Of course, the borrow checker protects us from compiling code without `move`.\n move |num| list.push(num)\n };\n\n append_42(&mut appender);\n append_42(&mut appender);\n}\n\nfn moving_in_moving_out_closure() {\n fn append_multiple_times(appender: impl FnOnce(&mut Vec<String>) + Clone) {\n let mut list = Vec::new();\n\n // We can clone this `FnOnce`, because we additionally require `Clone`.\n // If we didn't clone it, we couldn't call it more than *once*.\n appender.clone()(&mut list);\n appender(&mut list);\n }\n\n let appender = {\n let string = String::from(\"Ala\");\n println!(\"Before defining closure: {:?}\", string);\n\n // The `move` keyword is necessary to prevent dangling reference to `list`.\n // Of course, the borrow checker protects us from compiling code without `move`.\n move |list: &mut Vec<String>| list.push(string)\n };\n\n // As `appender` is only `FnOnce`, we need to clone before we consume it by calling it.\n append_multiple_times(appender.clone());\n append_multiple_times(appender);\n}\n\n\n(Download the source code for this example: closures_capturing.rs)\nClosures as trait objects (in dynamic dispatch)\nThe following code sample shows how one can use closures as dyn Trait objects, bypassing the problem of them having anonymous types:\nfn main() {\n fn some_function() -> String {\n String::new()\n }\n\n let v1 = String::from(\"v1\");\n let mut borrowing_immutably_closure = || v1.clone();\n\n let mut v2 = String::from(\"v2\");\n let mut borrowing_mutably_closure = || {\n v2.push('.');\n v2.clone()\n };\n\n let v3 = String::from(\"v3\");\n let mut moving_in_nonmutating_closure = move || v3.clone();\n\n let mut v4 = String::from(\"v4\");\n let mut moving_in_mutating_closure = move || {\n v4.push('.');\n v4.clone()\n };\n let v5 = String::from(\"v5\");\n let moving_in_moving_out_closure = || v5;\n\n let fn_once_callables: [&dyn FnOnce() -> String; 5] = [\n &some_function,\n &borrowing_immutably_closure,\n &borrowing_mutably_closure,\n &moving_in_nonmutating_closure,\n &moving_in_moving_out_closure,\n ];\n\n #[allow(unused_variables)]\n for fn_once_callable in fn_once_callables {\n // Cannot move a value of type `dyn FnOnce() -> String`.\n // The size of `dyn FnOnce() -> String` cannot be statically determined.\n // println!(\"{}\", fn_once_callable());\n\n // So, for FnOnce, we need to be their owners to be able to call them,\n // and we can't have a `dyn` object owned on stack.\n // We will solve this problem soon with smart pointers (e.g., Box).\n // This will give us `std::function` -like experience.\n }\n\n // Mutable reference to FnMut is required to be able to call it.\n let fn_mut_callables: [&mut dyn FnMut() -> String; 4] = [\n &mut borrowing_immutably_closure,\n &mut borrowing_mutably_closure,\n &mut moving_in_nonmutating_closure,\n &mut moving_in_mutating_closure,\n ];\n\n for fn_mut_callable in fn_mut_callables {\n println!(\"{}\", fn_mut_callable());\n }\n\n let fn_callables: &[&dyn Fn() -> String] =\n &[&borrowing_immutably_closure, &moving_in_nonmutating_closure];\n\n for fn_callable in fn_callables {\n println!(\"{}\", fn_callable());\n }\n}\n\n\n(Download the source code for this example: closures_fun.rs)\nExamples\nWe'll go through the examples from Rust by Example.\nMore examples will be seen when working with iterators.\nIterators\nIn Rust, there is no hierarchy of types for collections (because there is no inheritance in general).\nInstead, what makes a collection is that it can be iterated over.\nA usual way in Rust to perform an iteration over something, be it a range of values or items in a collection, is creating a (lazy) iterator over it and transforming it using iterator adaptors. For example, if T: Iterator, then T::map() creates a Map<T> adaptor. Once a final iterator is created, it has to be actually activated (iterated over), which is most commonly done by:\n\nexhausting it with the for loop,\nmanually iterating over it using next() calls,\ncollecting its contents into inferred collection (collect()),\nconsuming it with a consuming adaptor (e.g., sum(), count),\n\nuse std::collections::HashSet;\n\nfn main() {\n // Various ways to create a String.\n let mut strings = [\n String::new(),\n String::from(\"a\"),\n \"b\".into(),\n \"c\".to_owned(),\n \"d\".to_string(),\n \"e\".chars().collect(),\n ];\n\n // `iter()` is a usual method that creates an iterator over immutable references to the collection's items.\n let _all_len_0_or_1 = strings\n .iter()\n .filter(|s| !s.is_empty())\n .all(|s| s.len() == 1);\n\n // `iter_mut()` is a usual method that creates an iterator over mutable references to the collection's items.\n for s in strings.iter_mut().map_while(|s| match s.as_str() {\n \"c\" => None,\n _ => Some(s),\n }) {\n *s = s.replace(\"b\", \"aba\");\n }\n\n // This code is equivalent to the `for` above.\n // `for` is usually more idiomatic, but `for_each` is sometimes cleaner and sometimes faster.\n strings\n .iter_mut()\n .map_while(|s| match s.as_str() {\n \"c\" => None,\n _ => Some(s),\n })\n .for_each(|s| *s = s.replace(\"b\", \"aba\"));\n\n // `into_iter()` is a method from `IntoIterator` trait that converts a collection to an iterator\n let mut empty_strings_iter = strings.into_iter().map(|mut s| {\n s.clear();\n s\n });\n\n // This is a set of empty Strings...\n let empty_strings_set = empty_strings_iter.clone().collect::<HashSet<_>>();\n\n // And this is a Vec of immutable references to empty Strings.\n let empty_string_refs_vec = empty_strings_set.iter().collect::<Vec<_>>();\n\n // equivalent to `empty_string_refs_vec.into_iter()`\n for s in empty_string_refs_vec {\n println!(\"{}\", s)\n }\n\n while let Some(s) = empty_strings_iter.next_back() {\n assert!(s.is_empty());\n }\n}\n\n\n(Download the source code for this example: iterator_exhaustion.rs)\nIterators are highly optimised, so they are high-level code that compiles down to simple and optimised machine code (intended as zero-cost abstractions).\nWe'll go through the official docs.\n\nMost methods are defined in the Iterator trait.\nIntoIterator is also worth noting, because it makes types work with the for loop.\nFor completeness, there is FromIterator, which is required for collect() to work.\n\nReading\n\nThe Book, chapter 12 (that's a project!)\nThe Book, chapter 13\nThe Book, chapter 14\nThe Book, Advanced Functions and Closures\nThe Book, Advanced Traits\n\nAssignment 4 (graded)\nLazy\nDeadline: 06.11.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/","title":"Closures and Iterators"},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"body":"Working with the heap\nSo far we've only used heap allocated memory indirectly by working with containers such as vectors, maps or the String type, otherwise allocating our variables on the stack. We didn't really have to be aware of the fact that these collections used the heap, as all that memory management details were hidden away from us. In this lesson we'll take a closer look at what is really happening there and how we can do that ourselves.\nTo work with heap-allocated memory, Rust features smart pointers. You should have already heard this term as it is a very important feature in C++ and the concept is virtually the same here - they are wrappers around raw allocated memory that provide additional, safety-ensuring mechanism. What defines a smart pointer in Rust is generally the implementation of two traits: Drop and Deref.\nThe Drop trait is pretty straightforward as it consists of one method - fn drop(&mut self) - that is, basically, the destructor, invoked during stack unwinding.\nThe Deref trait allows us to overload the dereference (*) operator.\nDeref coercion\nApart from enabling access to the underlying value, implementing the Deref trait enables Rust to perform deref coercion on the pointer - trying to remove as many levels of indirection as it can. What it means in practice is that we will be able to use it with any code working on plain references.\nuse std::ops::Deref;\n\nstruct MyBox<T>(T);\n\n// We won't be allocating anything on the heap here as it is not important here.\n// We're only focusing on the dereference mechanisms.\nimpl<T> MyBox<T> {\n fn new(x: T) -> MyBox<T> {\n MyBox(x)\n }\n}\n\nimpl<T> Deref for MyBox<T> {\n type Target = T;\n\n fn deref(&self) -> &Self::Target {\n &self.0\n }\n}\n\nfn hello(name: &str) {\n println!(\"Hello, {}!\", name);\n}\n\nfn main() {\n let x = 5;\n let int_box = MyBox::new(x);\n\n assert_eq!(5, *int_box);\n\n // String also implements the `Deref` trait.\n // In fact, String actually is a smart pointer.\n let s = String::from(\"I'm a smart pointer too\");\n hello(&s);\n\n // Deref coercion can deal with multiple levels of indirection.\n let str_box = MyBox::new(String::from(\"Rust\"));\n hello(&str_box);\n}\n\n\n(Download the source code for this example: deref_coercion.rs)\nIn general, there are three possible coercions that Rust can perform:\n\n\nFrom &T to &U when T: Deref<Target=U>\n\n\nFrom &mut T to &mut U when T: DerefMut<Target=U>\n\n\nFrom &mut T to &U when T: Deref<Target=U>\n\n\nWhile the first two coercions are straightforward, the third one is possible because treating a mutable reference as an immutable one does not break the rules of ownership.\nBox - simple wrapper\nThe Box<T> type is the most basic out of Rust's smart pointers, equivalent to C++'s std::unique_ptr<T>. It's a simple wrapper that makes sure the underlying memory gets allocated and freed properly.\nfn box_simple() {\n let b = Box::new(5);\n println!(\"b = {}\", b);\n\n let _x = 10 + *b;\n}\n\n// `Box` gives us the indirection required to define\n// recursive types\n#[allow(dead_code)]\nenum List {\n Cons(i32, Box<List>),\n Nil,\n}\n\nfn main() {\n box_simple();\n}\n\n\n(Download the source code for this example: box.rs)\nReference counting\nThe Rc<T> type is the equivalent of std::shared_ptr<T> from C++. There is one caveat to this though - because we're creating multiple references to the same object, those references have to be immutable in accordance with the ownership rules.\nuse std::rc::Rc;\n\nstruct LoudInt(i32);\n\nimpl Drop for LoudInt {\n fn drop(&mut self) {\n println!(\"[{}] Farewell!\", self.0);\n }\n}\n\nfn main() {\n {\n let outer_ref;\n\n {\n let inner_ref = Rc::new(LoudInt(5));\n\n // strong_count represents the number of owning references pointing\n // to data\n assert_eq!(Rc::strong_count(&inner_ref), 1);\n\n outer_ref = Rc::clone(&inner_ref);\n\n assert_eq!(Rc::strong_count(&inner_ref), Rc::strong_count(&outer_ref));\n assert_eq!(Rc::strong_count(&inner_ref), 2);\n }\n\n println!(\"The {} still lives!\", outer_ref.0);\n assert_eq!(Rc::strong_count(&outer_ref), 1);\n }\n}\n\n\n(Download the source code for this example: ref_count.rs)\nRust also provides a non-owning pointer in the form of Weak<T> (equivalent to std::weak_ptr<T>) that can be obtained from an instance of Rc<T>.\nuse std::rc::Rc;\n\nstruct LoudInt(i32);\n\nimpl Drop for LoudInt {\n fn drop(&mut self) {\n println!(\"[{}] Farewell!\", self.0);\n }\n}\n\nfn main() {\n let weak_ref;\n\n {\n let shared_ref = Rc::new(LoudInt(5));\n\n // weak_count keeps track of the non-owning reference to the data\n assert_eq!(Rc::weak_count(&shared_ref), 0);\n\n // `downgrade()` obtains a weak pointer to Rc's data\n weak_ref = Rc::downgrade(&shared_ref);\n\n assert_eq!(Rc::weak_count(&shared_ref), 1);\n assert_eq!(Rc::strong_count(&shared_ref), 1);\n\n // In order to use the the data underneath the weak pointer\n // we need to obtain a new shared pointer from it.\n // The `upgrade()` method returns `Option<Rc<T>>`.\n let temp = weak_ref.upgrade();\n assert_eq!(Rc::strong_count(&shared_ref), 2);\n println!(\"The value is {}\", temp.unwrap().0);\n }\n\n println!(\"The value should be deallocated by now.\");\n assert!(weak_ref.upgrade().is_none());\n}\n\n\n(Download the source code for this example: weak_ref.rs)\nMutating the immutable\nGood examples and explanation of the interior mutability pattern and runtime borrow checking can be found in the book.\nAlongside the RefCell<T> type described above, there is an analogous Cell<T> type that operates on values instead of references.\nConvenient handling of dyn objects\nIn previous labs you learned about dynamic dispatch and its strengths. The largest drawback you noticed is most likely that they are unsized (!Sized, where ! being syntax signifying lack of trait implementation).\nWhen storing an object on a heap, however, we can use it as a dyn object seamlessly.\nObligatory reading\n\n\nThe Book, chapter 15\n\n\nstd::borrow::Cow, a versatile copy-on-write smart pointer\n\n\nAdditional reading\n\nOn wrapped references\nDeref vs AsRef vs Borrow\n\nAssignment 5 (graded)\nCorporations\nDeadline: 13.11.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/","title":"Smart Pointers"},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"body":"Feedback\nConditional implementation\nimpl<const N: usize> Shape for SphereN<N> {\n type Volume = VolumeN<N>;\n fn volume(&self) -> Self::Volume {\n let mut volume: u32 = (f64::from(self.radius) * f64::from(self.radius) * PI) as u32;\n if N == 3 {\n volume = (f64::from(self.radius)\n * f64::from(self.radius)\n * f64::from(self.radius)\n * PI\n * 4.0_f64\n / 3.0_f64) as u32;\n }\n Self::Volume::new(volume)\n }\n}\n\nInstead of checking N == 3, you can provide different impls for SphereN<2> and\nSphereN<3> (as they are different types).\nu32 and u64\nThey are different types, but because you can easily cast one to another,\nit was not sufficient to make the implementation type-safe.\n","id":"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/","title":"Feedback #2"},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"body":"Parallelism vs Concurrency\nConcurrency is when tasks can make progress independently of each other.\nParallelism is when multiple tasks make progress at the same time.\nConcurrency models in Rust\nThreads\nNothing unusual here.\nThreads can be created with the thread::spawn function docs - please read them!.\nThis method returns a JoinHandle<T> which can be used to wait for the thread to finish. T is the type of the thread's return value.\nAnother way to spawn threads is using scope. Threads created in such way are mandatorily joined at the end of the scope, which guarantees that they will borrow items for no longer that the lifetime of the scope. Hence, they can borrow non-'static items!\nPropagating panics\nIn Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).\nClosures\nClosures which are used to create threads must take ownership of any values they use. It can be forced with the move keyword.\nuse std::thread;\n\nfn main() {\n let v = vec![1, 2, 3];\n\n let handle = thread::spawn(move || {\n println!(\"Here's a vector: {:?}\", v);\n });\n\n handle.join().unwrap();\n}\n\nNormal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!\nBut what if we need to share some state?\nSend and Sync\nThey are marker traits used to indicate that a type or a reference to it can be used across threads. See the nomicon for more information.\n\n\nA type is Send if it is safe to move it (send it) to another thread.\nA type is Sync if it is safe to share (sync) between threads (T is Sync if and only if &T is Send).\n\n\nThis makes sense, because Sync is about sharing object between threads, and & is the shared reference.\nThere is also a great answer on Rust forum, listing + explaining example types that are !Send or !Sync.\nFor more convenient analysis, examples are listed here:\nSend + !Sync:\n\nUnsafeCell (=> Cell, RefCell);\n\n!Send + !Sync:\n\nRc\n*const T, *mut T (raw pointers)\n\n!Send + Sync:\n\nMutexGuard (hint: !Send for POSIX reasons)\n\nExercise for the reader: explain reasons for all limitations of the above types.\nSharing state between threads\nMessage passing\nOne possible way is to use message passing. We can use a blocking queue (called mpsc - \"multi producer single consumer FIFO queue\") to do it.\nWe talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.\nMutexes\nIn Rust, a mutex wraps a value and makes it thread-safe.\nBecause it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the RefCell type.\nArc is a smart pointer like Rc but it can be shared between threads.\nPlease read more about them in the book.\nThe docs also mention poisoning.\nRwLocks\nRwLocks are similar to mutexes, but they distinguish between read and write locks.\nAtomic types\nAtomic types are described in the docs.\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicUsize, Ordering};\nuse std::{hint, thread};\n\nfn main() {\n let spinlock = Arc::new(AtomicUsize::new(1));\n\n let spinlock_clone = Arc::clone(&spinlock);\n let thread = thread::spawn(move|| {\n spinlock_clone.store(0, Ordering::SeqCst);\n });\n\n // Wait for the other thread to release the lock\n while spinlock.load(Ordering::SeqCst) != 0 {\n hint::spin_loop();\n }\n\n if let Err(panic) = thread.join() {\n println!(\"Thread had an error: {:?}\", panic);\n }\n}\n\nNote that atomic values don't have to be wrapped in a mutex when shared across threads.\nWait...\nIf most types are Sync + Send, then what stops us from using a standard, non-atomic integer in the example above?\nlet spinlock = Arc::new(1);\n\nlet spinlock_clone = Arc::clone(&spinlock);\nlet thread = thread::spawn(move|| {\n *spinlock_clone += 1;\n});\n\nwhile *spinlock != 0 {\n hint::spin_loop();\n}\n\nerror[E0594]: cannot assign to data in an `Arc`\n --> src/main.rs:9:9\n |\n9 | *spinlock_clone += 1;\n | ^^^^^^^^^^^^^^^^^^^^ cannot assign\n |\n = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<i32>`\n\n...so we would have to use a RefCell to be able to modify the value through a shared reference...\nlet spinlock = Arc::new(RefCell::new(1));\n\nlet spinlock_clone = Arc::clone(&spinlock);\nlet thread = thread::spawn(move|| {\n *spinlock_clone.borrow_mut() += 1;\n});\n\n// Wait for the other thread to release the lock\nwhile *spinlock.borrow() != 0 {\n hint::spin_loop();\n}\n\n...but RefCell isn't Sync:\nerror[E0277]: `RefCell<i32>` cannot be shared between threads safely\n --> src/main.rs:9:18\n |\n9 | let thread = thread::spawn(move|| {\n | ^^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely\n |\n = help: the trait `Sync` is not implemented for `RefCell<i32>`\n = note: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>`\n = note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]`\nnote: required by a bound in `spawn`\n\nAnd that bound mentioned in the last line looks like this:\npub fn spawn<F, T>(f: F) -> JoinHandle<T> where\n F: FnOnce() -> T,\n F: Send + 'static,\n T: Send + 'static,\n\nExercise for the reader\nWhy is it impossible to share a reference to a Mutex between threads spawned with std::thread::spawn?\nData parallelism with Rayon\nRayon is a library for parallelization of data processing.\nIt can be used to parallelize the execution of functions over a collection of data by switching the standard Iterator to a ParallelIterator.\nIt works very similar to Java's parallel streams.\nWhy do that? Because thread synchronization is hard! Rust prevents data races, but logical races and deadlocks are impossible to prevent!!\nRayon's FAQ is worth reading.\nReading\n\nThe Book\nSafely writing code that isn't thread-safe\n\nNo assignment this week\nPlease work on the first iteration of the big project instead.\n","id":"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/","title":"Fearless concurrency"},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"body":"Object-oriented programming and Rust\nThe book has a chapter dedicated to it.\nEspecially the \"typestate\" pattern is very interesting.\nYou can read more about it here.\nHow to build a good library\nThese guidelines have been created by the Rust library team.\nHow to handle errors\nThis post is from 2020, but the libraries it mentions (anyhow and thiserror) are still the most popular.\nSerde\nSerde is the most popular serialization library for Rust.\nAssignment\nThis week's assignment is to write a \"distributed\" calculator.\nYou should base your solution on the final project from the book.\n","id":"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/","title":"Design patterns"},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"body":"Tokio\nWe'll use the Tokio tutorial (chapters Overview-Channels).\nCommon Rust Lifetime Misconceptions\nPlease read this blogpost.\nAssignment 6 (graded)\nCalculator\nDeadline: 04.12.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/","title":"Async: Part 1"},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"body":"Project feedback\nUnwrapping options/results\nAlways ask yourself twice if you really need to unwrap. In most cases, you don't have to. Use pattern matching instead,\nas it provides a static guarantee that the value is present.\nPattern matching prevents you from writing code like this:\nfn main() {\n let x: Option<i32> = some_function();\n\n if x.is_some() {\n println!(\"x is {}\", x.unwrap());\n }\n\n // Let's say this line was added later and/or you forgot to put it in the if statement.\n do_something(x.unwrap()); // this will blow up if x == None!\n}\n\nInstead, you can write:\nfn main() {\n let x: Option<i32> = some_function();\n\n if let Some(x) = x {\n println!(\"x is {}\", x);\n do_something(x);\n }\n}\n\nQuestion mark operator\nIn methods that return Result or Option, you can use the question mark operator to return early if the value is None or Err.\nSee: https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html\nLogging\nYou can use the log crate to log messages. It's better than println! because it\ncan be easily turned off. It also allows you to use different severity levels (e.g. info, warn, error) and only\nlog messages above a certain level.\n&String vs &str\nSee https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters\nIn general, if you want to pass a reference to a string, use &str instead of &String.\nUse current versions of dependencies\nYou can use cargo upgrades to check for outdated dependencies.\nIf your project has separate binaries, use multiple binaries or a workspace\nYou can have multiple binaries in a single cargo project. Simply place them in the src/bin directory.\nYou can run them with cargo run --bin <name>. Alternatively, you can setup a\nworkspace.\nRun clippy & cargo fmt\nThis should have become a habit by now. You can disable clippy warnings for a single item with #[allow(clippy::...)],\nbut in most cases you shouldn't do that.\nIf you need to escape characters in a string, use raw strings\nSee https://doc.rust-lang.org/reference/tokens.html#raw-string-literals\nHow to handle errors?\nShort: https://kerkour.com/rust-error-handling\nLong: https://www.lpalmieri.com/posts/error-handling-rust/\nDon't pass around locked mutex's contents\nIf you have a mutex, you can use lock() to get a guard that will unlock the mutex when it goes out of scope.\nBut don't pass the contents of the guard to functions that can block (unless the mutex must be locked for\nthe entire duration of the function).\nInstead of:\nuse std::sync::Mutex;\nuse std::thread;\nuse std::time::Duration;\nuse std::time::Instant;\n\nfn handle_function(counter: &mut i32) {\n thread::sleep(Duration::from_secs(1));\n *counter += 1;\n thread::sleep(Duration::from_secs(1));\n}\n\nfn main() {\n let counter = Mutex::new(1);\n\n thread::scope(|s| {\n for i in 0..10 {\n let counter = &counter;\n s.spawn(move || {\n println!(\"Thread {i} started\");\n let now = Instant::now();\n let mut counter = counter.lock().unwrap();\n handle_function(&mut counter); // lock is held for 2 seconds\n println!(\"Thread {i} finished after {}s\", now.elapsed().as_secs());\n });\n }\n })\n}\n\nYou should do this:\nuse std::sync::Mutex;\nuse std::thread;\nuse std::time::Duration;\nuse std::time::Instant;\n\nfn handle_function(counter: &Mutex<i32>) { // <-- changed\n thread::sleep(Duration::from_secs(1));\n {\n let mut counter = counter.lock().unwrap(); // <-- changed\n *counter += 1;\n // lock is held only for the duration of the block\n // it is important to create a new scope here, otherwise the lock would be held for another second\n }\n thread::sleep(Duration::from_secs(1));\n}\n\nfn main() {\n let counter = Mutex::new(1);\n\n thread::scope(|s| {\n for i in 0..10 {\n let counter = &counter;\n s.spawn(move || {\n println!(\"Thread {i} started\");\n let now = Instant::now();\n handle_function(counter); // <-- changed! we don't lock here\n println!(\"Thread {i} finished after {}s\", now.elapsed().as_secs());\n });\n }\n })\n}\n\n\nCompare the output of the two programs. The first one will take 20 seconds to finish, while the second one will take 2 seconds.\nFirst one:\nThread 1 started\nThread 0 started\nThread 2 started\nThread 3 started\nThread 4 started\nThread 5 started\nThread 6 started\nThread 7 started\nThread 8 started\nThread 9 started\nThread 1 finished after 2s\nThread 0 finished after 4s\nThread 2 finished after 6s\nThread 3 finished after 8s\nThread 4 finished after 10s\nThread 5 finished after 12s\nThread 6 finished after 14s\nThread 7 finished after 16s\nThread 8 finished after 18s\nThread 9 finished after 20s\n\n\nSecond one:\nThread 0 started\nThread 2 started\nThread 1 started\nThread 3 started\nThread 4 started\nThread 5 started\nThread 6 started\nThread 7 started\nThread 8 started\nThread 9 started\nThread 1 finished after 2s\nThread 2 finished after 2s\nThread 0 finished after 2s\nThread 3 finished after 2s\nThread 4 finished after 2s\nThread 5 finished after 2s\nThread 6 finished after 2s\nThread 7 finished after 2s\nThread 8 finished after 2s\nThread 9 finished after 2s\n\n","id":"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/","title":"Project feedback"},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"body":"Reinventing futures\nWe recently got our feet wet with the async/await functionality of Rust by using the Tokio library. With this basic understanding of what we expect out of futures, let's try to come up with their details ourselves.\nWe know that, when asked, a future can either give us a ready value or still be waiting for it. Asking about the future's result is called polling. Our future could look something like this:\ntrait SimpleFuture {\n type Output;\n fn poll(&mut self) -> Poll<Self::Output>;\n}\n\nenum Poll<T> {\n Ready(T),\n Pending,\n}\n\nThe poll method can be called to check for the result of the future. There is a flaw in this however - whatever is coordinating our future-based computations will have to constantly poll each of them in hope they are ready to do some work.\ntrait SimpleFuture {\n type Output;\n fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;\n}\n\n\nWe can solve this by attaching a callback to our polling. The wake function passed to poll can be used to notify whoever issued the poll that the future is ready to make some progress and should be polled.\nLet's picture a quick example of how our SimpleFuture could be used.\npub struct SocketRead<'a> {\n socket: &'a Socket,\n}\n\nimpl SimpleFuture for SocketRead<'_> {\n type Output = Vec<u8>;\n\n fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {\n if self.socket.has_data_to_read() {\n // The socket has data -- read it into a buffer and return it.\n Poll::Ready(self.socket.read_buf())\n } else {\n // The socket does not yet have data.\n //\n // Arrange for `wake` to be called once data is available.\n // When data becomes available, `wake` will be called, and the\n // user of this `Future` will know to call `poll` again and\n // receive data.\n self.socket.set_readable_callback(wake);\n Poll::Pending\n }\n }\n}\n\nCombining futures\nWith the SimpleFuture at our disposal we can easily model more advanced concurrent computations.\n/// Concurrency is achieved via the fact that calls to `poll` each future\n/// may be interleaved, allowing each future to advance itself at its own pace.\npub struct Join<FutureA, FutureB> {\n // Each field may contain a future that should be run to completion.\n // If the future has already completed, the field is set to `None`.\n // This prevents us from polling a future after it has completed, which\n // would violate the contract of the `Future` trait.\n a: Option<FutureA>,\n b: Option<FutureB>,\n}\n\nimpl<FutureA, FutureB> SimpleFuture for Join<FutureA, FutureB>\nwhere\n FutureA: SimpleFuture<Output = ()>,\n FutureB: SimpleFuture<Output = ()>,\n{\n type Output = ();\n fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {\n // Attempt to complete future `a`.\n if let Some(a) = &mut self.a {\n if let Poll::Ready(()) = a.poll(wake) {\n self.a.take();\n }\n }\n\n // Attempt to complete future `b`.\n if let Some(b) = &mut self.b {\n if let Poll::Ready(()) = b.poll(wake) {\n self.b.take();\n }\n }\n\n if self.a.is_none() && self.b.is_none() {\n // Both futures have completed -- we can return successfully\n Poll::Ready(())\n } else {\n // One or both futures returned `Poll::Pending` and still have\n // work to do. They will call `wake()` when progress can be made.\n Poll::Pending\n }\n }\n}\n\nWe can also queue futures like this:\npub struct AndThenFut<FutureA, FutureB> {\n first: Option<FutureA>,\n second: FutureB,\n}\n\nimpl<FutureA, FutureB> SimpleFuture for AndThenFut<FutureA, FutureB>\nwhere\n FutureA: SimpleFuture<Output = ()>,\n FutureB: SimpleFuture<Output = ()>,\n{\n type Output = ();\n fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {\n if let Some(first) = &mut self.first {\n match first.poll(wake) {\n // We've completed the first future -- remove it and start on\n // the second!\n Poll::Ready(()) => self.first.take(),\n // We couldn't yet complete the first future.\n Poll::Pending => return Poll::Pending,\n };\n }\n // Now that the first future is done, attempt to complete the second.\n self.second.poll(wake)\n }\n}\n\nExercise\nThe last example assumes that both futures are already constructed. In practice, however, we often want to chain futures that use the results of their predecessors, like this - get_breakfast().and_then(|food| eat(food));. Try implementing this behavior by adding a new method to the SimpleFuture trait called and_then and something that models this sequential computation (like the previous AndThenFut future).\nThe real deal\nWe weren't far from the actual way Rust's futures are structured. The Future trait looks as follows:\ntrait Future {\n type Output;\n fn poll(\n // Note the change from `&mut self` to `Pin<&mut Self>`:\n self: Pin<&mut Self>,\n // and the change from `wake: fn()` to `cx: &mut Context<'_>`:\n cx: &mut Context<'_>,\n ) -> Poll<Self::Output>;\n}\n\nThere are two differences here. Firstly, we use a context instead of a standalone wake method. Since this callback was just a simple function pointer, there was no way for it to hold any data pertaining to which future called it.\nSecondly, we take self as a Pin<>. This enables us to create immovable futures - we will go into it later.\nCoordinating futures - waker & executor\nUsing wakers and context\nWe will follow the steps in the book to make a future that runs a separate thread that sleeps for a given duration and only then returns a result.\nExecutor\nWe will follow the steps in the book to create our own executor to run our futures on.\nObligatory reading\n\nPinning in detail\nPinning in even more detail\nAsync in depth\n\nAdditional reading\n\nWhat color is your function\nPin and suffering\nUnderstanding Rust futures by going way too deep\nDesugaring and assembly of Rust async/await\n\nAssignment 7 (graded)\nCalculator\nDeadline: 18.12.2024 23:59\n","id":"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/","title":"Async: Part 2"},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"body":"Reading\n\n\nThe Book, Chapter 19.5\n\n\nLittle Book of Macros\n\n\nMacro Workshop\n\n\n","id":"https://mimuw-jnp2-rust.github.io/lessons/14-macros/","title":"Macros"},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"body":"(Some) projects from the previous semester\nChat App\nGitHub\nBartłomiej Sadlej (GitHub: @barteksad email: sadlejbartek@gmail.com)\n\"Chat app written in Rust with Postgress db. Allows users to create new accounts and channels and communicate within channel. All unseed messages from last login are delivered once logged in again.\nBy default server stats with one user named ADMIN with password ADMIN\"\ntokio, serde, anyhow, thiserror, dashmap\nAlgorithm Visualizer\nGitHub\nMikołaj Piróg (mikolajpirog@gmail.com, GitHub: @aetn23), Mikołaj Wasiak (wasiak.mikolaj1@gmail.com, GitHub: @RudyMis)\nGraph editor with algorithms visualization. Create and modify the graph using GUI, move it around using WSAD. To see an algorithm being run, simply click on a node, and select the desired algorithm. See video and/or GitHub page for further details.\nPetgraph, egui-tetra, dyn_partial_eq\n\n\nRustal Combat\nGitHub\nKamil Bugała (GiHub: @Emilo77)\nSimple game in the style of Mortal Combat. Two players fight each other by using dashes. It is the 1 v 1 version, so far it is possible to play on a one computer.\nBevy\nRusty dungeon\nGitHub\nBarbara Rosiak (GitHub: @barosiak, email basiarosiak.7@gmail.com), Tomasz Kubica (GitHub: @Tomasz-Kubica, email: tomaszkubica4@gmail.com), Dawid Mędrek (GitHub: @dawidmd)\nA 2D game written using Rust and Bevy Game Engine, in which the player has to shoot and defeat enemies on their way to the final boss, inspired by the Binding of Isaac.\nBevy\n\nMarioMIM\nGitHub\nKatarzyna Kloc (GitHub: @KatKlo, email: kk429317@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/katarzyna-kloc-7a7503209/),\nPatryk Bundyra (GitHub: PBundyra, email: pb429159@students.mimuw.edu.pl, linkedin: https://www.linkedin.com/in/pbundyra/)\nSince the moment CLion has shown us the first segfaults, we wanted to create a computer game inspired by the student’s adventure of pursuing a Computer Science degree. MarioMIM is a platform game whose main goal is to... get a degree. We’ve implemented a game inspired by Super Mario Bros, but in the special University of Warsaw edition. In order to overcome bugs, the student can strengthen himself by drinking coffee or learning the best programming language in the world - Rust.\nBevy, Rapier, Kira\n\n\nSendino\nGitHub\nGrzegorz Nowakowski (Github: @grzenow4, email: g.nowakowski@student.uw.edu.pl) with @izmael7 on Github\nOne room chat application with client-server model of communication. Many users can communicate at one time.\nTokio, serde, crossterm\nChatter\nGitHub\nKacper Kramarz-Fernandez (GitHub: @kfernandez31, email: kacper.fernandez@gmail.com),\nJan Zembowicz (GitHub: @JWZ1996, email: janzembowicz@gmail.com)\nChatter is a simple multi-room command-line chat application that uses a two-protocol (HTTP + WS) communication style for high-efficiency.\nTokio, Warp, Serde, Hyper, among others\n","id":"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/","title":"2021L Project showcase"}},"docInfo":{"https://mimuw-jnp2-rust.github.io/":{"body":37,"title":3},"https://mimuw-jnp2-rust.github.io/lessons/":{"body":0,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/00-organizational/":{"body":128,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/01-introduction/":{"body":866,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/02-ownership/":{"body":1511,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/03-data-types/":{"body":1424,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/":{"body":182,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/":{"body":2292,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/":{"body":840,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/":{"body":523,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/":{"body":57,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/":{"body":530,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/":{"body":50,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/11-async-1/":{"body":22,"title":3},"https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/":{"body":484,"title":2},"https://mimuw-jnp2-rust.github.io/lessons/13-async-2/":{"body":491,"title":3},"https://mimuw-jnp2-rust.github.io/lessons/14-macros/":{"body":9,"title":1},"https://mimuw-jnp2-rust.github.io/lessons/project-showcase/":{"body":297,"title":3}},"length":18},"lang":"English"}; \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..5bf2e47 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,209 @@ + + + + https://mimuw-jnp2-rust.github.io/ + + + https://mimuw-jnp2-rust.github.io/lessons/ + + + https://mimuw-jnp2-rust.github.io/lessons/00-organizational/ + 2024-10-06 + + + https://mimuw-jnp2-rust.github.io/lessons/01-introduction/ + 2024-09-20 + + + https://mimuw-jnp2-rust.github.io/lessons/01_introduction/a_taste_of_rust-introductory_slides/a-taste-of-rust/ + + + https://mimuw-jnp2-rust.github.io/lessons/02-ownership/ + 2024-10-08 + + + https://mimuw-jnp2-rust.github.io/lessons/02_ownership/dont_panic/dont-panic/ + + + https://mimuw-jnp2-rust.github.io/lessons/02_ownership/string_formatting/string-formatting/ + + + https://mimuw-jnp2-rust.github.io/lessons/03-data-types/ + 2024-10-17 + + + https://mimuw-jnp2-rust.github.io/lessons/03_data_types/module_system/module-system/ + + + https://mimuw-jnp2-rust.github.io/lessons/04-feedback-1/ + 2022-10-31 + + + https://mimuw-jnp2-rust.github.io/lessons/05-types-reasoning/ + 2024-10-24 + + + https://mimuw-jnp2-rust.github.io/lessons/06-closures-iterators/ + 2024-10-30 + + + https://mimuw-jnp2-rust.github.io/lessons/07-smart-pointers/ + 2022-11-21 + + + https://mimuw-jnp2-rust.github.io/lessons/08-feedback-2/ + 2022-11-21 + + + https://mimuw-jnp2-rust.github.io/lessons/09-concurrency/ + 2024-11-14 + + + https://mimuw-jnp2-rust.github.io/lessons/10-design-patterns/ + 2022-12-05 + + + https://mimuw-jnp2-rust.github.io/lessons/11-async-1/ + 2024-11-27 + + + https://mimuw-jnp2-rust.github.io/lessons/12-project-feedback/ + 2022-12-29 + + + https://mimuw-jnp2-rust.github.io/lessons/13-async-2/ + 2022-05-23 + + + https://mimuw-jnp2-rust.github.io/lessons/14-macros/ + 2022-06-07 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/00-organizational/ + 2022-02-16 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/01-introduction/ + 2022-03-09 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/02-ownership/ + 2022-03-06 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/03-data-types/ + 2022-03-09 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/04-enums/ + 2022-03-13 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/05-tests/ + 2022-03-13 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/06-types-reasoning/ + 2022-03-21 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/07-feedback/ + 2022-03-29 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/08-closures-iterators/ + 2022-03-27 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/09-feedback2/ + 2022-04-04 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/10-smart-pointers/ + 2022-04-10 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/11-feedback3/ + 2022-04-11 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/12-concurrency/ + 2022-04-25 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/13-design-patterns/ + 2022-05-09 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/14-async-1/ + 2022-05-16 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/15-async-2/ + 2022-05-23 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/16-macros/ + 2022-06-07 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2021L/b1-rusty-graphs/ + 2022-06-07 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/00-organizational/ + 2022-10-10 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/01-introduction/ + 2022-10-10 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/02-ownership/ + 2022-10-17 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/03-data-types/ + 2022-10-24 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/04-feedback-1/ + 2022-10-31 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/05-types-reasoning/ + 2022-11-07 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/06-closures-iterators/ + 2022-11-14 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/07-smart-pointers/ + 2022-11-21 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/08-feedback-2/ + 2022-11-21 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/09-concurrency/ + 2022-11-28 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/10-design-patterns/ + 2022-12-05 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/11-async-1/ + 2022-12-12 + + + https://mimuw-jnp2-rust.github.io/lessons/old/2022Z/12-project-feedback/ + 2022-12-29 + + + https://mimuw-jnp2-rust.github.io/lessons/project-showcase/ + 2022-10-17 + +