<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Steno &amp; PL</title>
    <description>This is a personal blog. Unless otherwise stated, the opinions
expressed here are my own, and not those of my past or present
employers.</description>
    <link>https://blog.waleedkhan.name/pl/</link>
    <atom:link href="https://blog.waleedkhan.name/pl/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 03 Apr 2026 23:57:16 +0000</pubDate>
    <lastBuildDate>Fri, 03 Apr 2026 23:57:16 +0000</lastBuildDate>
    <generator>Jekyll v4.3.4</generator>
    
      <item>
        <title>Kodowanie w Rust modułów w stylu ML</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;Programiści OCaml/SML uczący się języka Rust.&lt;/li&gt;
        &lt;li&gt;Programiści funkcjonalni, którzy wcześniej nie mieli styczności z języków takimi jak OCaml czy Haskell, ale mogą być zainteresowani pewną techniką abstrakcji.&lt;/li&gt;
        &lt;ul&gt;&lt;li&gt;
        Należy zauważyć, że termin “moduł w stylu ML” odnosi się do &lt;a href=&quot;https://en.wikipedia.org/wiki/ML_(programming_language)&quot;&gt;rodziny języków programowania ML&lt;/a&gt;, a nie do &lt;a href=&quot;https://pl.wikipedia.org/wiki/Uczenie_maszynowe&quot;&gt;uczenia maszynowego&lt;/a&gt;.
        &lt;/li&gt;&lt;/ul&gt;
      &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;Doświadczenie w OCaml i Rust.&lt;/li&gt;
        &lt;li&gt;Doświadczenie w teorii typów.&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Pomocny&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#stwierdzenie-problemu&quot; id=&quot;markdown-toc-stwierdzenie-problemu&quot;&gt;Stwierdzenie problemu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cachowanie-funkcji&quot; id=&quot;markdown-toc-cachowanie-funkcji&quot;&gt;Cachowanie funkcji&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#cachowanie-indywidualnej-funkcji&quot; id=&quot;markdown-toc-cachowanie-indywidualnej-funkcji&quot;&gt;Cachowanie indywidualnej funkcji&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#cachowanie-funkcji-generyczne&quot; id=&quot;markdown-toc-cachowanie-funkcji-generyczne&quot;&gt;Cachowanie funkcji generyczne&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#uważanie-funkcji-jako-wartość&quot; id=&quot;markdown-toc-uważanie-funkcji-jako-wartość&quot;&gt;Uważanie funkcji jako wartość&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kodowanie-modułów&quot; id=&quot;markdown-toc-kodowanie-modułów&quot;&gt;Kodowanie modułów&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#moduły-w-ocaml&quot; id=&quot;markdown-toc-moduły-w-ocaml&quot;&gt;Moduły w OCaml&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#associated-typy&quot; id=&quot;markdown-toc-associated-typy&quot;&gt;“Associated” typy&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#parametryzowanie-tylko-nad-typami&quot; id=&quot;markdown-toc-parametryzowanie-tylko-nad-typami&quot;&gt;Parametryzowanie tylko nad typami&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#inne-kodowania&quot; id=&quot;markdown-toc-inne-kodowania&quot;&gt;Inne kodowania&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;stwierdzenie-problemu&quot;&gt;Stwierdzenie problemu&lt;/h2&gt;

&lt;p&gt;Problem: chcesz dodać do istniejącego typu implementującego pewnego trait nową działalność, która będzie wymagała używania wewnętrznego stanu.&lt;/p&gt;

&lt;p&gt;Problem: chcesz wziąć istniejący typ spełniający jakąś cechę i rozszerzyć go o nową funkcjonalność — w tym nowy stan wewnętrzny.&lt;/p&gt;

&lt;p&gt;Jeśli nie chcesz dodać wewnętrznego stanu, istnieje kilka możliwych rozwiązań:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Używać domyślnych implementacji metod trait.&lt;/li&gt;
  &lt;li&gt;Używać “extension traits”.&lt;/li&gt;
  &lt;li&gt;Używać &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl dyn&lt;/code&gt; &lt;em&gt;(uwaga techniczna: będzie to wymagać dynamicznego wywoływania na czasie wykonywania)&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Używać wolnych (globalnych) funkcji, które są generyczne i akceptują dowolny typ implementujący trait.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na przykład: implementowanie warstwa cachowania [ang. “caching”] tylko &lt;em&gt;raz&lt;/em&gt;, które mogło wtedy zastosować się do &lt;em&gt;dowolnej&lt;/em&gt; implementacji jakiegoś trait &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Moduły w stylu ML są naturalnym rozwiązaniem dla tego rodzaju problemów. Bardziej wydajne są one niż tradycyjne “moduły”, jak uważa się w popularnych językach programowania. Zarówno moduły w stylu ML, jak i tradycyjne moduły obsługują:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rozdzielanie kodu na logiczne przestrzenie nazw [ang. “namespaces”].&lt;/li&gt;
  &lt;li&gt;Definiowanie interfejsów.&lt;/li&gt;
  &lt;li&gt;Abstrahowanie typów danych i ukrywanie szczegółów implementacji.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ponadto moduły w stylu ML mogą być parametryzowane na &lt;em&gt;typach&lt;/em&gt; (podobnie szablonom [ang. “templates”] lub typom generycznym w innych językach programowania), a także _innych modułach _(omówione w tym artykule).&lt;/p&gt;

&lt;p&gt;Pomimo, że Rust pochodzi z OCaml, nie dziedziczy systemu modułów w stylu ML. Zamiast, system “trait” w Rust przypomina &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_class&quot;&gt;typeclasses z Haskell&lt;/a&gt;. Jednak, korzystający dodatkowych funkcji, możemy symulować w Rust moduły w stylu ML.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Uwaga pedagogiczna: przykładem kanonicznym modułów w stylu ML nad typeclasses chyba jest utworzenie nowego typu “tree-set”, który akceptuje inne kolejności, a nie ogranicza się do jednej kolejności na typ. Sądzę, że ten przykład się nudzi, bo nie rozwiązuje Prawdziwych Problemów Inżynierii, i bo rozwiązanie typeclasses — utworzenie “typ opakowania” [ang. “wrapper type”] z nową kolejnością — wymaga mniej więcej tyle samo pracy dla programisty, co używanie modułów.)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;cachowanie-funkcji&quot;&gt;Cachowanie funkcji&lt;/h2&gt;

&lt;p&gt;Załóżmy, że mamy kosztowne obliczenia, które chcemy cachować (być może wysyłania zapytania do bazy danych lub wykonują dużo pracy związanej z procesorem [ang. “CPU-bound”]). Dla przykładu użyjemy funkcji, która oblicza długość łańcucha:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Called expensive function str_len&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cachowanie-indywidualnej-funkcji&quot;&gt;Cachowanie indywidualnej funkcji&lt;/h3&gt;

&lt;p&gt;Jak napisać funkcję która działa jak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str_len&lt;/code&gt;, ale cachuje wyniki w pamięci podręcznej w taki sposób, że nigdy nie obliczy dwukrotnie tej samej pary klucz-wartość? Jednym ze sposobów jest wygenerowanie funkcji opakowania z zmiennym wewnętrznym pamięcią podręczną i wywołanie podstawowe funkcji &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str_len&lt;/code&gt; kiedy klucza nie ma w pamięci podręcznej:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Declare that we return an `FnMut` rather than an `Fn`&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// because the returned closure may modify mutable state&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// (and thus isn&apos;t safe to call in parallel -- not relevant&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// in this example).&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Use the `move` keyword to move the mutable `cache`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// into the returned closure object. Unlike a variable defined&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// in the closure body itself, this cache will be shared across&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// *all* invocations of the new function.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// For simplicity, we clone the `&amp;amp;str` as the cache key&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// rather than worry about the lifetime annotations here.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// If we didn&apos;t do this, it&apos;s possible that our internal `cache`&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// would hold a reference to a `str` even after that `str` was freed.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_owned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look up the entry in our cache,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// or create a new one if none exists.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.or_insert_with_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prints:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Called expensive function str_len&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Cached str len 1/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Cached str len 2/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oczywiście, możliwe jest również utworzenie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct&lt;/code&gt;, która cachuje wewnętrzną pamięcię podręczną i po prostu wymaganie wywołującemu wywołania opakowaną funkcję za pomocą metody &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.call&lt;/code&gt; &lt;em&gt;(uwaga techniczna: Rust obecnie nie obsługuje przeciążania operatora wywołania funkcji)&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;cachowanie-funkcji-generyczne&quot;&gt;Cachowanie funkcji generyczne&lt;/h3&gt;

&lt;p&gt;A teraz abstrahujemy o jeden poziom: a jak chcemy napisać funkcję generyczną do cachowania, która może cachować wyniki &lt;em&gt;dowolnej&lt;/em&gt; funkcji, a nie tylko konkretnie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str_len&lt;/code&gt;? &lt;em&gt;(Uwaga techniczna: aby uprościć sytuację, ograniczymy się do cachowania wyników tylko tych funkcji, które akceptują dokładnie jeden parametr, ponieważ Rust obecnie nie obsługuje “variadic generics”.)&lt;/em&gt; Możemy to osiągnąć za pomocą funkcji generycznej:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Create a function which is generic over the key and value types,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// and accepts a &quot;base&quot; function which takes a key and returns a value.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// In order to store the key in the cache, we had to add the additional&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// bounds `Eq + Hash` to `K`. To simplify the lifetimes, we added&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// the lifetime bound `&apos;static` to `K` and the bound `Clone` to `V`&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// (and cloned the value before returning it).&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Eq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Clone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;V&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.or_insert_with_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A potem nieznacznie dostosowujemy podany parametr, żeby działały typy w tym przykładzie:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// To make some types work, add an additional `&amp;amp;`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// to the parameter type.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cache_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prints:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Called expensive function str_len&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Cached str len 1/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Cached str len 2/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;uważanie-funkcji-jako-wartość&quot;&gt;Uważanie funkcji jako wartość&lt;/h3&gt;

&lt;p&gt;Oczywiście z powodu, że zadaliśmy sobie tyle trudu, aby stworzyć tą pamięcię podrzęczną, pewnie chcemy podać ją przekazywać i używać. Idealnie moglibyśmy pisać to tak:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// `impl T` does not compile (yet).&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Eq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Clone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cache_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Niestety, produkuje ten błąd:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error[E0658]: `impl Trait` in type aliases is unstable
  --&amp;gt; src/bin/main3-pass-function.rs:24:50
   |
24 | type CachedF&amp;lt;K: Eq + Hash + &apos;static, V: Clone&amp;gt; = impl FnMut(K) -&amp;gt; V;
   |                                                  ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #63063 &amp;lt;https://github.com/rust-lang/rust/issues/63063&amp;gt; for more information

error: non-defining opaque type use in defining scope
  --&amp;gt; src/bin/main3-pass-function.rs:35:5
   |
35 |     accepts_cached_str_len(cached_str_len);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: used non-generic type `&amp;amp;&apos;static str` for generic parameter
  --&amp;gt; src/bin/main3-pass-function.rs:24:14
   |
24 | type CachedF&amp;lt;K: Eq + Hash + &apos;static, V: Clone&amp;gt; = impl FnMut(K) -&amp;gt; V;
   |              ^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nie możemy używać &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl Fn&lt;/code&gt; w “type alias”, więc musimy to wypisać:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached str len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cache_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Czyli każde użycie naszej typu funkcji musi zawierać również parametry generycznych typów. To “zaraźliwe” zachowanie szybko staje się nieporęczne w praktyce dla programów nawet o średniej wielkości.&lt;/p&gt;

&lt;h2 id=&quot;kodowanie-modułów&quot;&gt;Kodowanie modułów&lt;/h2&gt;

&lt;h3 id=&quot;moduły-w-ocaml&quot;&gt;Moduły w OCaml&lt;/h3&gt;

&lt;p&gt;Moduły w stylu ML mogą ukrywać typy generyczne w sposób, w jaki same funkcje generyczne nie mogą osiągać &lt;em&gt;(uwaga techniczna: używają pewnej formę &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_system#Existential_types&quot;&gt;typów “existential”&lt;/a&gt;)&lt;/em&gt;. Aby wyrazić podobny przykład za pomocą modułów w stylu ML, możesz przejrzeć (i chyba nie zrozumieć) następujący kod OCaml:&lt;/p&gt;

&lt;div class=&quot;language-ocaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sig&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StrLenBackend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unit&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Computing length of string %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sig&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute_cached&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hashtbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hashtbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compute_cached&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hashtbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_opt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Some&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compute&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hashtbl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ostatecznie chodzi o to, że możesz napisać funkcję taką jak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accepts_cached_str_len&lt;/code&gt;, za pomocą typu niegenerycznego, takiego jak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachedStrLenBackend.t&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ocaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accepts_cached_str_len&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;nn&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Cached len 1/2: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compute_cached&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nn&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Cached len 2/2: %d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compute_cached&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt;
  
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;accepts_cached_str_len&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;bp&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;associated-typy&quot;&gt;“Associated” typy&lt;/h3&gt;

&lt;p&gt;Aby osiągnąć coś podobnego do modułów w stylu ML, możemy używać &lt;a href=&quot;https://doc.rust-lang.org/rust-by-example/generics/assoc_items/types.html&quot;&gt;Rust typy “associated”&lt;/a&gt; [ang. “associated types”]. Najpierw, zmieniamy nasze typy funkcji w traits &lt;em&gt;(uwaga techniczna: ostatecznie, to jest pewną formą “&lt;a href=&quot;https://en.wikipedia.org/wiki/Defunctionalization&quot;&gt;defunctionalization&lt;/a&gt;”).&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We declare &quot;associated types&quot; for this trait. For any type &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// which implements `Backend`, it must declare corresponding `Key`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and `Value` types, and include an implementation of `compute`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// adhering to those types.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A teraz stworzymy naszą funkcję &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str_len&lt;/code&gt; jako nowy typ i implementację trait &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backend&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This type doesn&apos;t need state, but you could imagine&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// embedding e.g. a database connection in this struct.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Normally, types without any data aren&apos;t very useful, but&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// this is one of the key ideas of the &quot;encoding of ML-style&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// modules&quot;. Instead of state (or in addition to state), the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// type instead has associated types and functions,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// declared below.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This type corresponds to the &quot;closed-over&quot; variables&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// of a closure object.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Computing length of key {key:?}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Jak definiujemy generyczną wersję cachowanego backend, który działa dla dowolnego rodzaju &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backend&lt;/code&gt;? Prosta wersja funkcji generycznej tak jak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compute_cached&amp;amp;lt;B: Backend&amp;gt;&lt;/code&gt; nie działa, bo chcemy, żeby przechowywał stan. Zamiast tego deklarujemy inny typ, który jest sparametryzowany na typie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backend&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We require that the wrapped backend adheres to these bounds&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// in order to call any of the functions in this `impl`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (including `new`, which is how you would get a new value&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// of this type).&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Previously, we had to add the appropriate bounds to &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// each generic type in each place that it appeared.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Using a `trait`, we can instead declare the bounds&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// just once on the `impl`.&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Eq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.cache&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.or_insert_with_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.backend&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Następnie skonstruujemy wartości tych typów i przekazujemy je dalej. Inaczej niż wcześniej, możemy nawet stwarzać “type aliases” dla cachowanego typu funkcji — bez konieczności ponownego określenia parametrów typów generycznych w każdym miejscu.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenBackend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prints:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Computing length of key &quot;foo&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Cached len 1/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Cached len 2/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;parametryzowanie-tylko-nad-typami&quot;&gt;Parametryzowanie tylko nad typami&lt;/h3&gt;

&lt;p&gt;Przekazywanie dalej wartości, które ucieleśniają moduły jest właściwie najpotężniejszą formą tej techniki, odpowiadające “modułom pierwszej-klasy” [ang. “first-class modules”], ale jest to prawdopodobnie  najbardziej naturalna forma wyrażania w Rust, ponieważ większość programistów zajmuje się raczej wartościami niż typami. Idealnie byłoby, gdyby optimizer wyeliminował narzut związany z przekazywaniem wartości, która nie zawiera danych.&lt;/p&gt;

&lt;p&gt;Rzadko trzeba to robić, ale można wywoływać tylko na podstawie typu i zakazywać nawet budować wartości. Jest to również interesujące z pedagogicznego punktu widzenia jako przykład “kierowanego-przez-typ” [ang. “type-directed”] generowania kodu w Rust. Aby przepisać powyższy przykład bez tworzenia &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backend&lt;/code&gt;, usuniemy wszędzie parametr &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;self&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// We no longer take a value of type `&amp;amp;self`.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// In Rust, it&apos;s not possible to make a value of the type&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// of an empty enum.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&apos;static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Computing length of key {key:?}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Następnie dostosujemy  nasz &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachedBackend&lt;/code&gt;, żeby bezpośrednio użył &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B&lt;/code&gt;, zamiast wywołać metody na wartości typu &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B&lt;/code&gt;, tak jak wywołanie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B::compute&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Note that we no longer have a member of type `B`.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Backend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Eq&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.cache&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.or_insert_with_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenBackend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prints:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Computing length of key &quot;foo&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Cached len 1/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached len 1/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Cached len 2/2: 3&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cached len 2/2: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.compute_cached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This weird syntax is equivalent to `CachedBackend&amp;lt;StrLenBackend&amp;gt;::new()`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// in some other languages. It&apos;s written with an extra `::` due to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// technical restrictions on the syntax.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Note that, unlike before, we never pass a *value* into&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// the `CachedBackend`, only a type.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// We could also write the type annotation on the left side and let&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// type inference figure it out:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_also_works&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StrLenBackend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Or we could use our previously-defined type alias:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_also_works&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CachedStrLenBackend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;accepts_cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cached_str_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;inne-kodowania&quot;&gt;Inne kodowania&lt;/h3&gt;

&lt;p&gt;Moduły w stylu ML obejmują inne funkcje niż powyższe. Oto krótka dyskusja o kodowaniu tamtych funkcji:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include&lt;/code&gt;:
    &lt;ul&gt;
      &lt;li&gt;Można używać poprzednio dyskutowane sposoby, tak jak “extension traits”.&lt;/li&gt;
      &lt;li&gt;Albo możesz używać makr — widziałem to w systemach produkcyjnych. (Istnieją dobrze udokumentowane problemy z makrami, których tutaj nie omówię.)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Aktualizowanie modułów, czyli tworzenie nowej definicji istniejącego modułu z przedefiniowanym jednym z typów “associated”.
    &lt;ul&gt;
      &lt;li&gt;Poza makrami, nie widziałem kodowania na to.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Typowanie strukturalne [ang. “&lt;a href=&quot;https://en.wikipedia.org/wiki/Structural_type_system&quot;&gt;structural typing&lt;/a&gt;”]
    &lt;ul&gt;
      &lt;li&gt;Rust po prostu nie pozwala strukturalnego typowania obiektów podobnych do modułów. Możesz spróbować zmienić swoje implicytne strukturalne typy na eksplicytne hierarchię traits.&lt;/li&gt;
      &lt;li&gt;Albo możesz używać makr.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;“Applicative” vs “generative” funktory
    &lt;ul&gt;
      &lt;li&gt;Zobacz &lt;a href=&quot;https://stackoverflow.com/q/52161048/344643&quot;&gt;https://stackoverflow.com/questions/52161048/applicative-vs-generative-functors&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Zależy od konkretnego kodowania modułu. Abstrakcje typów są wprowadzone ręcznie typami “associated” i zawsze są nierówne, podczas gdy typy przekazywane przez parametr typu generycznego mogą być uważane równe. Więc jeśli chcesz funktor generatywnego, możesz bezpośrednio wprowadzić nowy typ abstrakcyjny poprzez kolejną warstwę &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trait&lt;/code&gt; z typem “associated”.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zobacz też &lt;a href=&quot;http://blog.ezyang.com/2013/05/the-ast-typing-problem/&quot;&gt;The AST typing problem&lt;/a&gt;, abe znaleźć powiązane pytania dotyczące praktycznego kodowania modułów.&lt;/p&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;17&amp;nbsp;Jun&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/kodowanie-w-rust-modułów-w-stylu-ml/&quot;&gt;Kodowanie w Rust modułów w stylu ML&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/kodowanie-w-rust-modułów-w-stylu-ml/&quot;;
    this.page.identifier = &quot;kodowanie-w-rust-modułów-w-stylu-ml/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Sat, 17 Jun 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/kodowanie-w-rust-modułów-w-stylu-ml/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/kodowanie-w-rust-modułów-w-stylu-ml/</guid>
        
        <category>rust</category>
        
        
      </item>
    
      <item>
        <title>Szybkie formatowanie stosu zatwierdzeń</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;Inżynierowie oprogramowanie pracujący z Git, którzy używają &quot;patch stacks&quot;/&quot;stacked diffs&quot;, na przykład w jaki sposób Git jest używany w projektach Git i Linux, a także w wielu firmach wprowadzających &quot;&lt;a href=&quot;https://trunkbaseddevelopment.com/&quot;&gt;trunk-based development&lt;/a&gt;&quot;.&lt;/li&gt;
        &lt;li&gt;&lt;em&gt;Nie&lt;/em&gt; użytkownicy Git, który uważają, że graf zatwierdzeń Git powinien odzwierciedlać rzeczywisty proces rozwijania.&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;Moja praca nad &lt;a href=&quot;https://github.com/arxanas/git-branchless&quot;&gt;git-branchless&lt;/a&gt;.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Prawdopodobnie niezważany.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;p&gt;Pewna kategoria programistów używa Git z przypływem pracy “patch stack”, na którym zbierają ciąg małych, indywidualnie przeglądanych zatwierdzeń, które razem realizują znaczną zmianę. W takich przykładach, często przydatne jest uruchamianie linters lub formatters na każdym zatwierdzeniu w stosie i zastosowanie wyników. Może to być jednak żmudne, a naiwne podejście może powodować niepotrzebne konflikty scalania. (Jednym z obejść jest zastosowanie formatter na każdym zatwierdzeniu w stosie &lt;em&gt;wstecz&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;Komenda &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git test&lt;/code&gt; z git-branchless przedstawia rozwiązanie do szybkiego uruchamiania formatters, itd., na całym stosie zatwierdzeń bez produkowania konfliktów scalania. Dodatkowo, może to zostać wykonywany równoległe, i zapisuje wyniki, żeby ponowne formatowania tego samego zatwierdzenia zostawił pominięte. Możesz zobaczyć &lt;a href=&quot;https://github.com/arxanas/git-branchless/discussions/803&quot;&gt;post z ogłoszeniem&lt;/a&gt; lub &lt;a href=&quot;https://github.com/arxanas/git-branchless/wiki/Command:-git-test&quot;&gt;dokumentację &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git test&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Oto demonstracja formatowania zatwierdzeń w stosie (przewiń do 0:35, aby zobaczyć tylko demonstrację &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git test fix&lt;/code&gt;):&lt;/p&gt;

&lt;video src=&quot;https://user-images.githubusercontent.com/454057/219904589-79657aed-9356-4f87-a0e4-bdcfbe691621.mov&quot; controls=&quot;controls&quot; muted=&quot;muted&quot;&gt;&lt;/video&gt;

&lt;p&gt;Ja zwykle ustawiam &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; fmt na alias na coś takiego&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git test run --exec &apos;cargo fmt --all&apos; --strategy worktree --jobs 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;11&amp;nbsp;Apr&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/formatowanie-stosu-zatwierdzeń/&quot;&gt;Szybkie formatowanie stosu zatwierdzeń&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/formatowanie-stosu-zatwierdzeń/&quot;;
    this.page.identifier = &quot;formatowanie-stosu-zatwierdzeń/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Tue, 11 Apr 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/formatowanie-stosu-zatwierdzeń/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/formatowanie-stosu-zatwierdzeń/</guid>
        
        <category>git</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>e69de29bb2d1d6434b8b29ae775ad8c2e48c5391</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;Inżynierowie oprogramowania pracujący z Git, którzy natrafili na tą wartośc i chcą potwierdzić, że ma ona szczególne znaczenie.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;Ogólne doświadczenie w Git.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Pomocny.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;p&gt;Git stosuje hash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e69de29bb2d1d6434b8b29ae775ad8c2e48c5391&lt;/code&gt; dla oznaczenia pustego blob (pusty plik).&lt;/p&gt;

&lt;p&gt;W ramach potwierdzenia, możemy ręcznie zbudować zawartość takiego obiektu Git i obliczyć jego hash. Format zawartości obiektu to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;typ&amp;gt; &amp;lt;rozmiar&amp;gt;\0&amp;lt;zawartość&amp;gt;&lt;/code&gt; (przy czym &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\0&lt;/code&gt; oznacza &lt;a href=&quot;https://pl.wikipedia.org/wiki/Null_(znak)&quot;&gt;znak null&lt;/a&gt;). W tym przypadku, typ to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blob&lt;/code&gt; rozmiar wynosi &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; i nie ma zawartości. Można obliczyć hash za pomocą jednej z poniższych komend:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;blob 0\0&apos;&lt;/span&gt; | shasum
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391  -

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;blob 0\0&apos;&lt;/span&gt; | openssl dgst &lt;span class=&quot;nt&quot;&gt;-sha1&lt;/span&gt;  
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Zobacz też &lt;em&gt;&lt;a href=&quot;https://stackoverflow.com/q/47465226/344643&quot;&gt;Why does Git hash only the Contents of A File?&lt;/a&gt;&lt;/em&gt; na powiązaną dyskusję.&lt;/p&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;14&amp;nbsp;Mar&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391/&quot;&gt;e69de29bb2d1d6434b8b29ae775ad8c2e48c5391&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391/&quot;;
    this.page.identifier = &quot;e69de29bb2d1d6434b8b29ae775ad8c2e48c5391/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Tue, 14 Mar 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/e69de29bb2d1d6434b8b29ae775ad8c2e48c5391/</guid>
        
        <category>git</category>
        
        <category>reference</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>Moja historia płacy</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;Obecny inżynierzy oprogramowania szukający informacji dla swojego poszukiwania pracy lub negocjacja&lt;/li&gt;
        &lt;li&gt;Osoby ciekawe o kompensacji w branży inżyniera oprogramowania w US (“poziom 3”).&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;Moja historia pracy.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Wstępnie pomocny, ale ostrożny.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#historia-płacy&quot; id=&quot;markdown-toc-historia-płacy&quot;&gt;Historia płacy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;historia-płacy&quot;&gt;Historia płacy&lt;/h2&gt;

&lt;p&gt;Inni udostępniali &lt;a href=&quot;https://www.jvt.me/posts/2021/09/09/public-salary-history/&quot;&gt;swoją płacę w rynku inżyniera oprogramowania&lt;/a&gt;. Z tych samych powodów, udostępniam własną płacę &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1agEtwxcpS0HB8HSJJjy2u6wIUbi5LFUQMAus4ADu7mU/edit?usp=sharing&quot;&gt;na tym połączeniu Google Sheets&lt;/a&gt;. Uważam tą informację trochę sensytywną, więc musisz poprosić o pozwolenie aby zobaczyć.&lt;/p&gt;

&lt;p&gt;W ramach &lt;a href=&quot;https://blog.pragmaticengineer.com/software-engineering-salaries-in-the-netherlands-and-europe/&quot;&gt;trójmodalnego modelu płacy&lt;/a&gt;, ponieważ pracowałem w US dla “&lt;a href=&quot;https://en.wikipedia.org/wiki/Big_Tech&quot;&gt;big tech&lt;/a&gt;” i finansów, plasuję się dobrze w “poziomu 3”. Zatem, ta informacją jest głównie przydatna dla kogoś negocjującego z firmą poziomu 3.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;W przypadku big tech, kompensacja zwykle jest dostana w postaci pensji, akcji (&lt;a href=&quot;https://en.wikipedia.org/wiki/Restricted_stock&quot;&gt;RSU&lt;/a&gt;), i rocznej premii opartej na wynikach.&lt;/li&gt;
  &lt;li&gt;W przypadku finansy, kompensacja zwykle jest dostana w postaci pensji i rocznej premii opartej na wynikach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jeśli masz pytania dotyczące kompensacji lub negocjacji jako inżynier oprogramowania w US, &lt;a href=&quot;/pl/o-mnie/&quot;&gt;moje dane kontaktowe znajdziesz tutaj&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;08&amp;nbsp;Mar&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/historia-płacy/&quot;&gt;Moja historia płacy&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/historia-płacy/&quot;;
    this.page.identifier = &quot;historia-płacy/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/historia-płacy/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/historia-płacy/</guid>
        
        <category>finance</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>Ulepszenie przyrostowe czasy testowania Rust</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;Potencjalni i średnio-zaawansowani deweloperzy Rust, którzy martwią się wolnymi czasami kompilacji, ich skalą i praktykami zapobiegawczymi.&lt;/li&gt;
        &lt;li&gt;Zaawansowani deweloperzy Rust, którzy pomogą mi poprawić czas kompilacji.&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;Moje doświadczenie w Rust.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Trochę udany; trochę rozczarowany.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Rust jest znany z wolnego czasu kompilacji. Spędziłem dużo czasu próbując poprawić przyrostowe czasy kompilacji testowania dla mojego projektu &lt;a href=&quot;https://github.com/arxanas/git-branchless&quot;&gt;git-branchless&lt;/a&gt; w &lt;a href=&quot;https://github.com/arxanas/git-branchless/pull/650&quot;&gt;https://github.com/arxanas/git-branchless/pull/650&lt;/a&gt;. Oto dyskusja wyników.&lt;/p&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#streszczenie-wykonawcze&quot; id=&quot;markdown-toc-streszczenie-wykonawcze&quot;&gt;Streszczenie wykonawcze&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#szczegóły-projektu&quot; id=&quot;markdown-toc-szczegóły-projektu&quot;&gt;Szczegóły projektu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#podzielenie-się-na-więcej-crates&quot; id=&quot;markdown-toc-podzielenie-się-na-więcej-crates&quot;&gt;Podzielenie się na więcej crates&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#czas-bez-operacji&quot; id=&quot;markdown-toc-czas-bez-operacji&quot;&gt;Czas bez operacji&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#koniec-profilowania&quot; id=&quot;markdown-toc-koniec-profilowania&quot;&gt;Koniec profilowania?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#koniec-kolejnych-pomysłów&quot; id=&quot;markdown-toc-koniec-kolejnych-pomysłów&quot;&gt;Koniec kolejnych pomysłów?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;streszczenie-wykonawcze&quot;&gt;Streszczenie wykonawcze&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;“Przyrostowe testowania” odnosi się tylko do zmiany kodu testu integracyjnego i przebudowanie. Kod źródłowy pozostaje niezmienony.&lt;/li&gt;
  &lt;li&gt;Ostatecznie udało mi się skrócić przyrostowy czas testowania z ~6.9sek do ~1.7sek (~4x). Inne sposoby na skrócenie czasu kompilacji przyniosły niewielką lub żadną poprawę.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;W celach informacyjnych, oto najlepsze artykuły dotyczące konceptualnego zrozumienia modelu kompilacji Rust i poprawy czasu kompilacji:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://matklad.github.io/2021/09/04/fast-rust-builds.html&quot;&gt;Fast Rust Builds&lt;/a&gt; (&lt;a href=&quot;https://matklad.github.io&quot;&gt;matklad.github.io&lt;/a&gt;, 2021)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://matklad.github.io/2021/05/31/how-to-test.html&quot;&gt;How to Test&lt;/a&gt; (&lt;a href=&quot;https://matklad.github.io&quot;&gt;matklad.github.io&lt;/a&gt;, 2021)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fasterthanli.me/articles/why-is-my-rust-build-so-slow&quot;&gt;Why is my Rust build so slow?&lt;/a&gt; (&lt;a href=&quot;https://fasterthanli.me/&quot;&gt;fasterthanli.me&lt;/a&gt;, 2021)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://endler.dev/2020/rust-compile-times/&quot;&gt;Tips for Faster Rust Builds&lt;/a&gt; (&lt;a href=&quot;https://endler.dev&quot;&gt;endler.dev&lt;/a&gt;, 2020-2022)
    &lt;ul&gt;
      &lt;li&gt;W rzeczywistości, wówczas nie czytałem tego artykułu, ale przeczytałem kiedy napisałem ten artykuł.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;szczegóły-projektu&quot;&gt;Szczegóły projektu&lt;/h2&gt;

&lt;p&gt;Oto jak duży był mój projekt &lt;a href=&quot;https://github.com/arxanas/git-branchless&quot;&gt;git-branchless&lt;/a&gt; przed pull request:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-branchless/src&lt;/code&gt;: 12060 linii.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-branchless/tests&lt;/code&gt;: 12897 linii.
    &lt;ul&gt;
      &lt;li&gt;Zauważ, że w dużej mierze opiera się na “snapshot testing”, więc większość tych linii kodu to “multiline string literals”.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-branchless-lib/src&lt;/code&gt;: 12406 linii.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dopuszczalne są długie czasy kompilacji i &lt;a href=&quot;https://pl.wikipedia.org/wiki/Konsolidacja_(informatyka)&quot;&gt;konsolidacja&lt;/a&gt;, ale aby zoptymalizować sprzężenie zwrotnego programowania, potrzebuję krótkich czasów testowania. W szczególności chcę iterować nad pracowaniem pewnego testu. (&lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ&lt;/a&gt; ma fajną funkcję automatycznego ponownego uruchamiania danego testu, gdy występuje zmiana w kodu źródła ale trwanie zbyt długo ponownej kompilacji zmniejsza jej przydatność.)&lt;/p&gt;

&lt;p&gt;Na początek zbudowanie binary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_amend&lt;/code&gt; (który testuje subcommand &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amend&lt;/code&gt;) po dodaniu pojedynczego komentarza do jego pliku testu (bez zmian w library!) wymaga ~6.9sek:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;nt&quot;&gt;--prepare&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;echo &quot;// @nocommit test&quot; &amp;gt;&amp;gt;git-branchless/tests/command/test_amend.rs&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cargo test --test mod --no-run&apos;&lt;/span&gt;   
Benchmark 1: cargo &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--test&lt;/span&gt; mod &lt;span class=&quot;nt&quot;&gt;--no-run&lt;/span&gt;
  Time &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean ± σ&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:      6.927 s ±  0.123 s    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;User: 7.652 s, System: 1.738 s]
  Range &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;min … max&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:    6.754 s …  7.161 s    10 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tragedia! To nie duży projekt, a zmieniamy tylko testy, więc nie powinno to wymagać tak długiego czasu iteracji.&lt;/p&gt;

&lt;h2 id=&quot;podzielenie-się-na-więcej-crates&quot;&gt;Podzielenie się na więcej crates&lt;/h2&gt;

&lt;p&gt;W pull request wyodrębniłem do dodatkowych dziewięciu crates, co daje przyrostowy czas kompilacji testu wynoszący ~1.7sek (~4x poprawa).&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;nt&quot;&gt;--prepare&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;echo &quot;// @nocommit test&quot; &amp;gt;&amp;gt;git-branchless/tests/test_amend.rs&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cargo test --test test_amend --no-run&apos;&lt;/span&gt;   
Benchmark 1: cargo &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--test&lt;/span&gt; test_amend &lt;span class=&quot;nt&quot;&gt;--no-run&lt;/span&gt;
  Time &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean ± σ&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:  	1.771 s ±  0.012 s	&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;User: 1.471 s, System: 0.330 s]
  Range &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;min … max&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:	1.750 s …  1.793 s	10 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Jeśli chodzi o poprawę względną, to znaczna, ale jeśli chodzi o poprawę bezwzględną, sądzę, że jest raczej słaba. Bym oczekiwał 100-200ms na parsing, macro expansion, typechecking, i code generation (bez optimacji) dla pliku tego rozmiaru (~1k linii, głownie długi wartości łańcuchowe).&lt;/p&gt;

&lt;p&gt;Dodatkowe, dzielenie na wiele crates utrudnia rozpowszechnianie projektu za pośrednictwem &lt;a href=&quot;https://crates.io/&quot;&gt;crates.io&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Każdy crate wymaga indywidualnego publikowania na crates.io (nie można opublikować crate z zależnościami Git w crates.io).
    &lt;ul&gt;
      &lt;li&gt;Dlatego muszę zarządzać wersjami i licencji każdego crate.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Według mojej ankiety wśród użytkowników, większość moich użytkowników instaluje program za pomocą &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt;. Na pytanie “Jak zainstalowałeś git-branchless?”, odpowiedzi są następujące (stan na ):
    &lt;ul&gt;
      &lt;li&gt;7/18 (38.9%): przez &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo install git-branchless&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;4/18 (22.2%): za pośrednictwem tradycyjnego systemowego menedżera pakietów.&lt;/li&gt;
      &lt;li&gt;4/18 (22.2%): przez Nix lub NixOS&lt;/li&gt;
      &lt;li&gt;2/18 (11.1%): klonując repozytorium i ręcznie budując i instalując.&lt;/li&gt;
      &lt;li&gt;1/18 (5.6%): przez artefakt kompilacji z GitHub Actions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Niestety, muszę publicznie udostępniać wewnętrzne moduły na crates.io tylko po to, aby uzyskać rozsądne czasy kompilacji.&lt;/p&gt;

&lt;h2 id=&quot;czas-bez-operacji&quot;&gt;Czas bez operacji&lt;/h2&gt;

&lt;p&gt;Wtedy, mierzę czas bez operacji [ang. “no-op”] na ~350ms dla mniejszej testowe crate z niewieloma zależnościami:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;s1&quot;&gt;&apos;cargo test -p git-branchless-test --no-run&apos;&lt;/span&gt; 		 
Benchmark 1: cargo &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; git-branchless-test &lt;span class=&quot;nt&quot;&gt;--no-run&lt;/span&gt;
  Time &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean ± σ&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:     344.2 ms ±   3.5 ms    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;User: 246.2 ms, System: 91.9 ms]
  Range &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;min … max&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:   340.4 ms … 351.0 ms    10 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To jest nieoczekiwane. Spodziewałbym się, że czas na kompilację bez operacji będzie podobny do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;, może 15ms:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;s1&quot;&gt;&apos;git status&apos;&lt;/span&gt;
Benchmark 1: git status
  Time &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean ± σ&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 	 13.5 ms ±   2.5 ms    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;User: 4.9 ms, System: 6.1 ms]
  Range &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;min … max&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:    11.1 ms …  24.7 ms    197 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tu może być jakiś głębszy problem. Dokumentacja &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo nextest&lt;/code&gt; ostrzega, że niektóre systemy przeciw malware mogą wprowadzać sztuczne opóźnienie uruchamiania podczas sprawdzania plików wykonywalnych:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A typical sign of this happening is even the simplest of tests in cargo nextest run taking more than 0.2 seconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zgodnie z dokumentacją, oznaczyłem moje oprogramowanie terminala jako “Developer Tools” w systemie macOS, ale nie udało mi się skrócić czasu kompilacji bez operacji.&lt;/p&gt;

&lt;h2 id=&quot;koniec-profilowania&quot;&gt;Koniec profilowania?&lt;/h2&gt;

&lt;p&gt;Spróbowalem z subcommand crate, który niedawno stworzyłem bez wielu zaleźności:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;nt&quot;&gt;--prepare&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;echo &quot;// @nocommit test&quot; &amp;gt;&amp;gt;git-branchless-test/tests/test_test.rs&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cargo test -p git-branchless-test --no-run&apos;&lt;/span&gt;
Benchmark 1: cargo &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; git-branchless-test &lt;span class=&quot;nt&quot;&gt;--no-run&lt;/span&gt;
  Time &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;mean ± σ&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:  	1.855 s ±  0.034 s	&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;User: 1.476 s, System: 0.335 s]
  Range &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;min … max&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:	1.831 s …  1.939 s	10 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wykres czasu kompilacji według &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo build&lt;/code&gt; nie pomaga. Tylko pokazuje to, że budowany test wymaga 100% czasu:&lt;/p&gt;

&lt;figure class=&quot;figure&quot;&gt;
        &lt;a href=&quot;/assets/posts/rust-przyrostowe-czasy-testowania/cargo-timings-git-branchless-test-crate.png&quot;&gt;&lt;img class=&quot;center-image&quot; src=&quot;/assets/posts/rust-przyrostowe-czasy-testowania/cargo-timings-git-branchless-test-crate.png&quot; alt=&quot;The timing graph for building the `git-branchless-test` crate.&quot; title=&quot;The timing graph for building the `git-branchless-test` crate.&quot; /&gt;&lt;/a&gt;
        &lt;figcaption class=&quot;caption&quot;&gt;&lt;p&gt;The timing graph for building the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-branchless-test&lt;/code&gt; crate.&lt;/p&gt;
&lt;/figcaption&gt;
      &lt;/figure&gt;

&lt;h2 id=&quot;koniec-kolejnych-pomysłów&quot;&gt;Koniec kolejnych pomysłów?&lt;/h2&gt;

&lt;p&gt;Oto niektóre pomysły, które nie zadziałały:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Połączenie testów integracyjnych w jeden binary. (W każdym razie chętnie uruchamiam indywidualne binaries, jeśli to konieczne.)&lt;/li&gt;
  &lt;li&gt;Zmniejszenie głównych monomorfozacji (wywołania &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsRef&lt;/code&gt; itp. Pojawiły się w &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo llvm-lines&lt;/code&gt;, ale ich zmniejszenie nie zdawało się poprawiać czasu kompilacji).&lt;/li&gt;
  &lt;li&gt;Używanie &lt;a href=&quot;https://github.com/mozilla/sccache&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sccache&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Używanie &lt;a href=&quot;https://nexte.st/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo nextest&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Używanie &lt;a href=&quot;https://github.com/michaeleisel/zld&quot;&gt;zld&lt;/a&gt;/&lt;a href=&quot;https://github.com/rui314/mold&quot;&gt;mold&lt;/a&gt;/&lt;a href=&quot;https://github.com/bluewhalesystems/sold&quot;&gt;sold&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Ustawienie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile.dev.split-debuginfo = “unpacked”&lt;/code&gt; (dla systemu macOS).&lt;/li&gt;
  &lt;li&gt;Ustawienie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile.dev.build-override.opt-level = 3&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Niestety, jest dużo makr proceduralnych, zwłaszcza &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#[instrument]&lt;/code&gt; z crate &lt;a href=&quot;https://docs.rs/tracing/latest/tracing/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Ustawienie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile.dev.debug = 0&lt;/code&gt;. W rzeczywistości skraca czas kompilacji o 20ms, ale samo w sobie nie wystarczy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Więc teraz utknąłem. To najwięcej, jak mogę skrócić przyrostowe czasy testowania Rust. Daj znać, jeśli masz jakieś inne pomysły.&lt;/p&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;07&amp;nbsp;Feb&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/rust-przyrostowe-czasy-testowania/&quot;&gt;Ulepszenie przyrostowe czasy testowania Rust&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/rust-przyrostowe-czasy-testowania/&quot;;
    this.page.identifier = &quot;rust-przyrostowe-czasy-testowania/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Tue, 07 Feb 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/rust-przyrostowe-czasy-testowania/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/rust-przyrostowe-czasy-testowania/</guid>
        
        <category>rust</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>System kontroli wersja Yandex Arc</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;Deweloperzy systemów kontroli źródła, tak jak Git i Mercurial.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;https://discord.com/channels/1042895022950994071/1042907270473850890/1068630001240514691&quot;&gt;Moje notatki w Discordzie&lt;/a&gt;.&lt;/li&gt;
        &lt;li&gt;Moje doświadczenie w programowaniu dla systemów kontroli źródła.&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;https://habr.com/ru/company/yandex/blog/482926/&quot;&gt;Oficjalny blog Yandex&lt;/a&gt;.&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Lekko zainteresowany.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;p&gt;Ostatnio był przeciek koda z &lt;a href=&quot;https://pl.wikipedia.org/wiki/Yandex&quot;&gt;Yandex&lt;/a&gt;. Ja nie spradzałem plików, ale temat właśnie przypomina nam, że Yandex utrzymuje swoje duże &lt;a href=&quot;https://monorepo.tools/&quot;&gt;monorepo&lt;/a&gt;, a nawet zbudował własny &lt;a href=&quot;https://pl.wikipedia.org/wiki/System_kontroli_wersji&quot;&gt;system kontroli źródła&lt;/a&gt; [ang. “source control system”], żeby sobie z tym radzić, o nazwie “Arc”.&lt;/p&gt;

&lt;p&gt;Oryginalny artykuł z Yandex (2020): &lt;a href=&quot;https://habr.com/ru/company/yandex/blog/482926/&quot;&gt;https://habr.com/ru/company/yandex/blog/482926/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Krótkie notatki (pierwotnie opublikowane na &lt;a href=&quot;https://discord.com/channels/1042895022950994071/1042907270473850890/1068630001240514691&quot;&gt;Discordzie&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Wydaje się, że jest oparty nad &lt;a href=&quot;https://subversion.apache.org/&quot;&gt;SVN&lt;/a&gt; na back-end.&lt;/li&gt;
  &lt;li&gt;Prowadzają &lt;a href=&quot;https://trunkbaseddevelopment.com/&quot;&gt;trunk-based development&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;6 mln zatwierdzeń, 2 mln plików, 2TB rozmiar repo.&lt;/li&gt;
  &lt;li&gt;Wypróbowali &lt;a href=&quot;https://www.mercurial-scm.org/&quot;&gt;Mercurial&lt;/a&gt;, ale nie rozwiązał problemów wydajności.&lt;/li&gt;
  &lt;li&gt;Używają numery generacji [ang. “generation numbers”] do obliczania “merge-bases”.
    &lt;ul&gt;
      &lt;li&gt;Jest to teraz dostępne w Gitu poprzez &lt;a href=&quot;https://git-scm.com/docs/commit-graph&quot;&gt;mechanizm commit-graph&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Prawdopodobnie oparty nad Gitem do front-end UI, ale narzekają, że UI Gita jest zły, więc go ulepszają.&lt;/li&gt;
  &lt;li&gt;Używany wewnętrznie przez 20% deweloperzy w momencie napisania.&lt;/li&gt;
  &lt;li&gt;Ciężko używa &lt;a href=&quot;https://en.wikipedia.org/wiki/Virtual_file_system&quot;&gt;wirtualnego system plików&lt;/a&gt; [ang. “virtual filesystem”, “VFS”].
    &lt;ul&gt;
      &lt;li&gt;Używają &lt;a href=&quot;https://en.wikipedia.org/wiki/Filesystem_in_Userspace&quot;&gt;FUSE&lt;/a&gt; na macOS, chociaż wsparcie VFS’ów na macOS jest niestabilne w tym czasie; może zmienili od tamtego czasu?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Używa &lt;a href=&quot;https://cloud.yandex.com/en/services/ydb&quot;&gt;Yandex Database&lt;/a&gt; (YDB) jako bazę danych na back-end, z jakimś narzędziem przekładania z SVN’a.&lt;/li&gt;
  &lt;li&gt;W ramach systemu code review, zatwierdzenie Arc’a będzie przedkładane w zatwierdzenie SVN’a, w tym dodatkowe metadane z Arc’a.&lt;/li&gt;
  &lt;li&gt;Niezauważalnie używa zatwierdzenia dla “working copy” aby przeprowadzać niektóre algorytmy, który zawierać “untracked” pliki, ponieważ zaopatrzywa VFS.
    &lt;ul&gt;
      &lt;li&gt;Wspomniałem o tym w kontekście &lt;a href=&quot;https://github.com/martinvonz/jj&quot;&gt;Jujutsu VCS&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;W sumie, nie wydaje mi się, że ma wiele do awansowania systemów kontroli wersji w porównaniu z dużymi firmami tech, takimi jak Google i Meta.&lt;/p&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;27&amp;nbsp;Jan&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/yandex-arc/&quot;&gt;System kontroli wersja Yandex Arc&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/yandex-arc/&quot;;
    this.page.identifier = &quot;yandex-arc/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Fri, 27 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/yandex-arc/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/yandex-arc/</guid>
        
        <category>git</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>Używanie `tracing` w aplikacji Rust CLI</title>
        <description>&lt;div class=&quot;publication-notes&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td&gt;Docelowi odbiorcy&lt;/td&gt;
      &lt;td&gt;Użytkownicy Rust.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pochodzenie&lt;/td&gt;
      &lt;td&gt;&lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;https://github.com/arxanas/git-branchless/discussions/732&quot;&gt;Mój post na GitHub&lt;/a&gt;.&lt;/li&gt;
        &lt;li&gt;Moje doświadczenie w Rust.&lt;/li&gt;
      &lt;/ul&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nastrój&lt;/td&gt;
      &lt;td&gt;Informujący.&lt;/td&gt;
    &lt;/tr&gt;
    
&lt;tr&gt;
  &lt;td&gt;Język&lt;/td&gt;
  &lt;td&gt;Obcy. Moim pierwszym język jest angielki. Jeśli zobaczysz błąd, niezależnie od tego, czy chodzi o pisownię, gramatykę czy dobór słow, skomentuj to!&lt;/td&gt;
&lt;/tr&gt;


  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/epage&quot;&gt;@epage&lt;/a&gt; w &lt;a href=&quot;https://github.com/arxanas/git-branchless/discussions/732&quot;&gt;https://github.com/arxanas/git-branchless/discussions/732&lt;/a&gt; zapytał:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Widzę, że mówią o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt;, ale głównie z perspektywy “web” i jestem ciekawy, jak to doświadczenie przekłada się na CLI. Zamierzam zastosować go w niektórych mniejszych CLI, żeby zdobyć trochę doświadczenia, aby zrozumieć, w jaki sposób może pomóc &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ponownie publikuję moją odpowiedź tutaj, &lt;a href=&quot;https://blog.waleedkhan.name/on-bullet-points/&quot;&gt;w formie wypunktowanej&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#dla-cargo&quot; id=&quot;markdown-toc-dla-cargo&quot;&gt;Dla Cargo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#pełne-notatki&quot; id=&quot;markdown-toc-pełne-notatki&quot;&gt;Pełne notatki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;dla-cargo&quot;&gt;Dla Cargo&lt;/h2&gt;

&lt;p&gt;W przypadku Cargo chciałbym podkreślić następujące punkty:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Zachęca do robienia właściwych rzeczy za pomocą makr do ustrukturyzowanego logowania [ang. “structured logging”]. Dodatkowe obliczenia nie są przeprowadzane, jeśli nie zostaną używane przez subskrybenta, co w przeciwnym razie mogłoby spowodować regres wydajności.&lt;/li&gt;
  &lt;li&gt;Śledzenie “spantraces” w celu profilowania jest niewygodne, ponieważ trzeba ręcznie dodawać adnotacje do każdej funkcji, która ma być śledzona. Nie wiem, czy można to poprawić za pomocą “stack traces”.&lt;/li&gt;
  &lt;li&gt;Można łatwo wysyłać dane do wielu różnych subskrybentów, w tym do niestandardowych (…jeśli wiadomo, jakie funkcje wywołać). Na przykład, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing_subscriber::fmt_layer::Layer::with_writer&lt;/code&gt; pozwala zbudować warstwę, która zużywa sformatowane informacje logowania i zapisuje je w dowolnym miejscu.
    &lt;ul&gt;
      &lt;li&gt;Można używać gotowych komponentów, takich jak subskrybent śledzenia Chrome [ang. “Chrome tracing”, jakaś].&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Ktoś może to skonfigurować raz, a inni nie muszą się tym zbytnio przejmować.&lt;/li&gt;
  &lt;li&gt;Dla mnie, profilowanie okazało się bardzo skuteczne.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;pełne-notatki&quot;&gt;Pełne notatki&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Przez używanie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eyre&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color_eyre&lt;/code&gt; można dołączyć parametry funkcji do “spantraces”. Jest to dość cenne podczas debugowania, ponieważ widzę m.in. OID zatwierdzenia [ang. “commit OID”] w “spantrace” dla zatwierdzenia, które powoduje problemy, bez konieczności ręcznego wyodrębniania go przez dodanie większej liczby logów.&lt;/li&gt;
  &lt;li&gt;Konfigurowanie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing&lt;/code&gt; po raz pierwszy jest uciążliwe, ale po tym w dużej mierze nie musisz go dotykać.
    &lt;ul&gt;
      &lt;li&gt;Oto moja funkcja, która to robi: &lt;a href=&quot;https://github.com/arxanas/git-branchless/blob/4b76af669258e80a6f6eb4ddf45bbb358da80248/git-branchless/src/commands/mod.rs#L437&quot;&gt;https://github.com/arxanas/git-branchless/blob/4b76af669258e80a6f6eb4ddf45bbb358da80248/git-branchless/src/commands/mod.rs#L437&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Z powodzeniem skonfigurowałem instrukcje logowania do drukowania za pomocą mojego własnego typu (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Effects&lt;/code&gt;), zamiast drukowania bezpośrednio na stdout/stderr, ponieważ może to blokować liczniki postępu [ang. “progress meters”] lub inne interaktywnie dane wyjście.&lt;/li&gt;
      &lt;li&gt;Skonfigurowałem go tak, że podprocesy nie zostaną zawarte w śledzeniu nadrzędnym; otrzymują własne pliki wyjściowe. Szczerze mówiąc, nie sądzę, żebym musiał badać śledzenie podprocesów.&lt;/li&gt;
      &lt;li&gt;Kiedyś było to mniej ergonomiczne, ale ostatnio ulepszyli coś związanego z kompozycją warstw, na przykład filtrowanie jednej warstwy na podstawie innej warstwy; zobacz &lt;a href=&quot;https://github.com/arxanas/git-branchless/commit/5428f1b9dbed356accf854774cad053c22d19b1f&quot;&gt;https://github.com/arxanas/git-branchless/commit/5428f1b9dbed356accf854774cad053c22d19b1f&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Niektóre komunikaty o błędach dotyczące kompozycji warstw mogą być nieczytelne z powodu domyślnego użycia polimorfizmu statycznego. Na przykład trudno jest warunkowo skonstruować warstwę do włączenia. Typy stają się bardzo długimi łańcuchami zagnieżdżonych parametrów typu ogólnego [ang. “generic type parameters”].
        &lt;ul&gt;
          &lt;li&gt;Na szczęście &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;Layer&amp;gt;&lt;/code&gt; jest również warstwą, więc możesz przekazać &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some(layer)&lt;/code&gt; lub &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; w zależności od stosownego warunku, zamiast próbować bezpośrednio wywoływać metody rejestru.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Makro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#[instrument]&lt;/code&gt; to jedyny realistyczny sposób dołączania funkcji do “spantraces” dla &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eyre&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color_eyre&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Niszczy to autopoprawki [ang. “autofixes”, tak jak w IDE] (prawdopodobnie należy to uznać za błąd w &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt;).&lt;/li&gt;
      &lt;li&gt;Może to wymazać lokalizację błędów, ponieważ czasami błąd jest przypisywany do funkcji jako całości, a nie do konkretnej linii.&lt;/li&gt;
      &lt;li&gt;Może to wydłużyć czas kompilacji, ponieważ jest to makro proceduralne [ang. “procedural macro”, &lt;a href=&quot;https://doc.rust-lang.org/reference/procedural-macros.html&quot;&gt;pojęcia z Rusta&lt;/a&gt;]. Jak być może wiesz, procedury makr proceduralnych, takie jak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syn&lt;/code&gt; [ang. &lt;a href=&quot;https://crates.io/crates/syn&quot;&gt;Rust crate zwany “syn”&lt;/a&gt;, nie ma czegoś wspólnego z słowem “syn”], zwykle znajdują się na ścieżce krytycznej [ang. “critical path”] kompilacji, ale jeśli zobowiążesz się w ogóle nie używać makr proceduralnych, możesz je pominąć.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Profilowanie poprzez zrzut do warstwy śledzenia Chrome jest skuteczne.
    &lt;ul&gt;
      &lt;li&gt;Spójrz na &lt;a href=&quot;https://github.com/arxanas/git-branchless/wiki/Runbook#profiling&quot;&gt;https://github.com/arxanas/git-branchless/wiki/Runbook#profiling&lt;/a&gt;, aby uzyskać szczegółowe informacje na temat procedur profilowania.&lt;/li&gt;
      &lt;li&gt;Wielokrotnie korzystałem z tego systemu do debugowania problemów/regresji w związku wydajnością. Istniejące wizualizatory śledzenia Chrome są całkiem przydatne do analizy podziału spędzonego czasu.&lt;/li&gt;
      &lt;li&gt;Zawarcie parametrów funkcji było wyjątkowo przydatne, ponieważ mogłem bezpośrednio zobaczyć wycinek danych wyjściowych profilowania, który trwał zbyt długo, i sprawdzić odpowiedni OID zatwierdzenia, dzięki czemu mogłem zbadać i przetestować to konkretne zatwierdzenie.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Spróbowałem użyć śledzenia jako danych wejściowych do mojego własnego systemu raportowania postępów (tak, aby wywołania funkcji były bezpośrednio powiązane z paskami postępu, które pojawiają się na ekranie). Było to możliwe, ale ostatecznie zrezygnowałem z tego podejścia, ponieważ operacje logiczne nie odpowiadały dokładnie jeden do jednego wywołaniom funkcji, a zbyt łatwo było zapomnieć o poprawnym opatrzeniu metodą adnotacją.&lt;/li&gt;
  &lt;li&gt;W ogóle nie próbowałem śledzenia z async/.await.&lt;/li&gt;
  &lt;li&gt;W ogóle nie próbowałem &lt;a href=&quot;https://github.com/tokio-rs/console&quot;&gt;https://github.com/tokio-rs/console&lt;/a&gt;, ale chciałbym wiedzieć, czy jest to dla Was przydatne.&lt;/li&gt;
  &lt;li&gt;Ustrukturyzowane logowanie jest świetne.
    &lt;ul&gt;
      &lt;li&gt;Samo użycie makr ustrukturyzowanego logowania z reprezentacjami debugowania (np. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warn(?var, &quot;Message&quot;)&lt;/code&gt;) jest znacznie bardziej ergonomiczne niż ręczne interpolowanie wartości do stringów.&lt;/li&gt;
      &lt;li&gt;Obsługuje również takie rzeczy, jak renderowanie znaków niedrukowalnych, gdzie wcześniej musiałbym uważać, aby uwzględnić wartości stringów w cudzysłowach itp.&lt;/li&gt;
      &lt;li&gt;Zasadniczo można zobaczyć te zdarzenia i ich wartości w wynikach profilowania, chociaż nie musiałem profilować na podstawie pojedynczych zdarzeń, tylko na podstawie całego zakresu.&lt;/li&gt;
      &lt;li&gt;Jest to bardziej wydajne, ponieważ nie będziemy budować sznurów, jeśli wartości nie zostaną zużyte przez żadnego subskrybenta w czasie wykonywania.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Istnieje dziwna niezgodność między wersją &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tracing_subscriber&lt;/code&gt; używaną przez mój kod i &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color_eyre&lt;/code&gt;, co oznacza, że ​​zablokowałem ją w znanej dobrej wersji tutaj: &lt;a href=&quot;https://github.com/arxanas/git-branchless/pull/533/&quot;&gt;https://github.com/arxanas/git-branchless/pull/533/ zatwierdzenia/e97954a9a9fab4039ad269d6b8982bb8bd95b133&lt;/a&gt;.
    &lt;ul&gt;
      &lt;li&gt;Myślę, że &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color_eyre&lt;/code&gt; musi po prostu zaktualizować swoją wersję śledzenia-subskrybenta, ale od tamtej pory się tym nie zajmowałem.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;25&amp;nbsp;Jan&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/śledzenie-aplikacji-rust-cli/&quot;&gt;Używanie `tracing` w aplikacji Rust CLI&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/śledzenie-aplikacji-rust-cli/&quot;;
    this.page.identifier = &quot;śledzenie-aplikacji-rust-cli/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Wed, 25 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/śledzenie-aplikacji-rust-cli/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/śledzenie-aplikacji-rust-cli/</guid>
        
        <category>rust</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
      <item>
        <title>Interaktywne blogi</title>
        <description>
&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#niech-żyje-ten-blog&quot; id=&quot;markdown-toc-niech-żyje-ten-blog&quot;&gt;Niech żyje ten blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;p&gt;Dla mnie, najlepszym platformą na blogging jest &lt;a href=&quot;https://www.google.com/docs&quot;&gt;Google Docs&lt;/a&gt;, bo obsługuje poniższe:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Współpracować na żywo.&lt;/li&gt;
  &lt;li&gt;Pozostawiać komentarze bezpośrednio w tekście, a nie w ograniczonym obszarze.&lt;/li&gt;
  &lt;li&gt;Sugerować zmiany bezpośrednio w tekście. Cóż miły sposób na poprawianie tekstu!
    &lt;ul&gt;
      &lt;li&gt;Z tego powodu również wolę Wikis od Git’a, jeśli chodzi o pisanie dokumentacji, bo bariera wejścia jest znacznie mniejsza.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Niestety, Google Docs nie jest zwłaszcza dostępny:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Wydaje się, że nie będzie indeksowany przez wyszukiwarki?&lt;/li&gt;
  &lt;li&gt;Wymaga Javascript.&lt;/li&gt;
  &lt;li&gt;Wymaga Google, czemu niektórzy ludzie sprzeciwiają się ze względu na prywatność.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dlatego nie wydaję w ten sposób postów na blogu.&lt;/p&gt;

&lt;style type=&quot;text/css&quot;&gt;
@keyframes hypermedia {
  0%, 100% {
    left: -1em;
    top: 0.4em;
    z-index: 1;
    font-size: normal;
  }
  
  25% {
    font-size: 1.3em;
  }
  
  75% {
    font-size: 0.7em;
  }

  50% {
    left: 97%;
    top: -0.6em;
    z-index: 1;
    font-size: normal;
  }
  
  51%, 99% {
    z-index: -1;
  }
}

.hypermedia {
  letter-spacing: 0.1em;
  position: relative;
  font-variant: small-caps;
}

.hypermedia::before {
  content: &quot;✨&quot;;
  position: absolute;
  animation-name: hypermedia;
  animation-duration: 6s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}
&lt;/style&gt;

&lt;p&gt;Ale w dobie &lt;span class=&quot;hypermedia&quot;&gt;hipermediów&lt;/span&gt; te funkcje powinny być standardem! Powinniśmy &lt;span class=&quot;hypermedia&quot;&gt;dyskutować&lt;/span&gt;, a nie wyrzucać artykuły &lt;span class=&quot;hypermedia&quot;&gt;w próżnię&lt;/span&gt;!&lt;/p&gt;

&lt;p&gt;Medium wyświetlało komentarze w tekście, ale &lt;a href=&quot;https://medium.com/@jashan/how-to-make-the-best-of-a-broken-commenting-system-113c8cc1fe71&quot;&gt;już tego nie robi&lt;/a&gt; (nie żebym chciał wydawać posty na Medium). Nie widziałem wielu innych blogów, które zapraszają do dyskusji za pomocą interaktywnych funkcji.&lt;/p&gt;

&lt;p&gt;Kiedyś czytałem &lt;em&gt;&lt;a href=&quot;https://dev.realworldocaml.org/&quot;&gt;Real World OCaml&lt;/a&gt;&lt;/em&gt;, kiedy jego drugie wydanie było w wersji roboczej. Po każdym zdaniu znajdował się link na pozostawianie komentarza. To jest naprawdę fajny sposób na pisanie książki! Dlaczego akurat książka jest bardziej interaktywna niż nasze blogi?&lt;/p&gt;

&lt;h2 id=&quot;niech-żyje-ten-blog&quot;&gt;Niech żyje ten blog&lt;/h2&gt;

&lt;p&gt;Od niedawna można pozostawiać komentarze na akapitach mojego blogu, najeżdżając/dotykając i klikając link “Skomentuj”, który się pojawi.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Inne rozwiązania:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://utteranc.es&quot;&gt;https://utteranc.es&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus&lt;/a&gt;
        &lt;ul&gt;
          &lt;li&gt;Używany już do komentarzy na tym blogu, chociaż jest trochę podejrzany, jeśli chodzi o korzystanie plików cookie.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Każda z tych opcji wymaga tworzenie własnego wątku komentarza dla każdego akapitu, na którym można skomentować.
        &lt;ul&gt;
          &lt;li&gt;Ich UIs nie zostały zaprojektowane tak, aby były zwarte, więc nie pasują dobrze między akapitami na blogu. Byłoby ogromny formularz po każdym akapitu.&lt;/li&gt;
          &lt;li&gt;Spowodowałoby to niepotrzebne obciążenie serwerów komentarzy, trochę niegrzecznie dla nich.&lt;/li&gt;
          &lt;li&gt;Spowodowałoby to wolniejsze ładowanie strony podczas zapytania do kilku wątków komentarzy.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Możesz czerpać inspirację z projektu takiego jak SideComments.js.&lt;/li&gt;
      &lt;li&gt;Moja implementacja niestety opiera się na GitHub jako dostawcy uwierzytelniania i bazie danych. Jestem pewien, że wielu czytelników nie będzie miało kont GitHub’a.
        &lt;ul&gt;
          &lt;li&gt;To był dla mnie najłatwiejszy sposób na zaimplementowanie.&lt;/li&gt;
          &lt;li&gt;GitHub API jest wystarczająco wyrazisty, aby wysłać zapytanie o wszystkie komentarze dla pewnego dokumentu w jednym żądaniu.&lt;/li&gt;
          &lt;li&gt;GitHub API nie wymaga uwierzytelniania ani klucza API do wysyłania żądań!
            &lt;ul&gt;
              &lt;li&gt;Przypuszczalnie będziesz ograniczany bardziej agresywnie niż w przypadku uwierzytelnienia.&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;Otwarcie nowej strony GitHub Issues’a w celu pozostawiania komentarza jest niewygodnie.&lt;/li&gt;
          &lt;li&gt;Patrząc wstecz, być może powinienem był użyć GitHub Discussions’a zamiast GitHub Issues’a jako bazy daty, ponieważ są to… dyskusje.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Akapity są identyfikowane za pomocą permalinku/”slug” i poprzez pobranie kilku pierwszych znormalizowanych bajtów danych w akapicie i zakodowanie jako base64.
        &lt;ul&gt;
          &lt;li&gt;Teoretycznie identyfikatory akapitów nie są zatem stabilne, jeśli treść zmienia się później, ale wydaje się, że jest to niewielki problem.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Oto implementacja w momencie pisania tego tekstu (137 linii kodu): &lt;a href=&quot;https://github.com/arxanas/blog/blob/c34f0e18b81ed1d1b22636eaef2cabe7b6afd77e/scripts/github-comment-links.js&quot;&gt;github-comment-links.js&lt;/a&gt;.
        &lt;ul&gt;
          &lt;li&gt;Nowoczesne API przeglądarki sprawiają, że zapytanie do API GitHub’a jest dość proste.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;11&amp;nbsp;Jan&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/interaktywne-blogi/&quot;&gt;Interaktywne blogi&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;



&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/interaktywne-blogi/&quot;;
    this.page.identifier = &quot;interaktywne-blogi/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Wed, 11 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/interaktywne-blogi/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/interaktywne-blogi/</guid>
        
        <category>rant</category>
        
        <category>writing</category>
        
        
      </item>
    
      <item>
        <title>Gdzie są moje funkcji Git’a z przyszłości?</title>
        <description>&lt;h2 id=&quot;git-jest-do-bani&quot;&gt;Git jest do bani&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;System kontroli wersji “Git”&lt;/a&gt; od 15+ lat przynosi nam nieszczęście. Od momentu powstania, tysiące osób próbowało stworzyć nowe klienty Git’a aby poprawić jego użyteczność.&lt;/p&gt;

&lt;p&gt;Jednak, prawie każda z nich skupiła się budować ładną fasadę nad Git’em, żeby robić te same operacji, które właśnie robi Git, jakby interfejs linii komend Git’a był szczytem użyteczności.&lt;/p&gt;

&lt;p&gt;Nikt nie raczy się zastanowić: jakie są &lt;em&gt;przepływy pracy&lt;/em&gt;, które osoby właśnie chcą wykonywać? Jakie są &lt;em&gt;funkcji&lt;/em&gt;, ułatwiające takie przepływy pracy? Zamiast, dostajemy klienty, które uważają, że &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -i&lt;/code&gt; to najlepszy sposób, aby zmienić komunikat zatwierdzenia, aby edytować zatwierdzenie, aby rozdzielić zatwierdzenie — albo, że warto nawet pokazać w UI tę funkcję.&lt;/p&gt;

&lt;div class=&quot;series-and-toc&quot;&gt;

  &lt;p class=&quot;toc-header&quot;&gt;Spis treści:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#git-jest-do-bani&quot; id=&quot;markdown-toc-git-jest-do-bani&quot;&gt;Git jest do bani&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#rubryka&quot; id=&quot;markdown-toc-rubryka&quot;&gt;Rubryka&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#klienty&quot; id=&quot;markdown-toc-klienty&quot;&gt;Klienty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#wyróżnienia&quot; id=&quot;markdown-toc-wyróżnienia&quot;&gt;Wyróżnienia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powiązane-posty&quot; id=&quot;markdown-toc-powiązane-posty&quot;&gt;Powiązane posty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#komentarze&quot; id=&quot;markdown-toc-komentarze&quot;&gt;Komentarze&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;rubryka&quot;&gt;Rubryka&lt;/h2&gt;

&lt;p&gt;Rozmyślałem o przepływach pracy, które często wykonuję, i sprawdziłem kilka klientów Git’a (niektóre z nich są GUIs, i niektóre TUIs), żebym zrozumiał, jak dobrze je obsługują.&lt;/p&gt;

&lt;p&gt;Wielu z moich czytelników nie dba o tych przepływach pracy, ale to nie tylko sprawa samych przepływach pracy; chodzi o decyzję, by nie używać wadliwych prymitywnych oferowanych przez Git.&lt;/p&gt;

&lt;p&gt;Przepływy pracy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reword&lt;/code&gt;&lt;/strong&gt;: Trzeba być możliwe aktualizować komunikat zatwierdzenia, który nie jest wypożyczany.
    &lt;ul&gt;
      &lt;li&gt;Aktualizowanie komunikatu zatwierdzenia nie spowoduje konfliktu scalania, zatem nie jest potrzebny wymagać, że zatwierdzenie jest wypożyczany.&lt;/li&gt;
      &lt;li&gt;Też powinno być możliwe aktualizować komunikat zatwierdzenia, który jest przodkiem wielu gałęzi, bez porzucania niektórych z tych gałęzi, ale nie róbmy sobie nadziei…&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync&lt;/code&gt;&lt;/strong&gt;: Trzeba być możliwe synchronizować wszystkie moje gałęzi (albo dowolny podzbiór) przez scalanie lub zmianę bazy [ang. “rebase”], w jednej operacji!
    &lt;ul&gt;
      &lt;li&gt;Robię to cały czas! Praktycznie pierwsza rzecz każdego ranka przychodząc do pracy.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;split&lt;/code&gt;&lt;/strong&gt;: Trzeba być szczególne polecenie, aby dzielić zatwierdzenie na dwa lub więcej zatwierdzeń, w tym zatwierdzenia, które nie są aktualnie wypożyczane.
    &lt;ul&gt;
      &lt;li&gt;Dzielenie zatwierdzenia nie spowoduje konfliktu scalania, zatem nie jest potrzebny wymagać, że zatwierdzenie jest wypożyczany.&lt;/li&gt;
      &lt;li&gt;Nie akceptuję rozwiązań za pomocą &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -i&lt;/code&gt;, ponieważ sprawdzanie stanu repozytorium podczas zmiany bazy jest bardzo mylące.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preview&lt;/code&gt;&lt;/strong&gt;: Przed wykonaniem scalania albo zmianą bazy, trzeba być możliwy podgląd wyniku, w tym ewentualne konflikty.
    &lt;ul&gt;
      &lt;li&gt;W ten sposób, nie muszę rozpoczynać scalania/zmiany bazy, żeby zobaczyć, czy się powiedzie, albo czy będzie trudno mi rozwiązać konflikty.&lt;/li&gt;
      &lt;li&gt;Konfliktami scalania może są najokropniejsza rzecz w używaniu Git’a, dlatego powinno być bardzo łatwiej zajmować się nimi (albo unikać zajmowania się nimi!).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undo&lt;/code&gt;&lt;/strong&gt;: Trzeba być możliwe cofnąć się z dowolnej operacji, najlepiej obejmujące śledzone-ale-niezatwierdzone zmiany.
    &lt;ul&gt;
      &lt;li&gt;To nie to samo, co cofnięcie [ang. “revert”] zatwierdzenia. Cofnięcie zatwierdzenia tworzy całkowicie nowe zatwierdzenie z odwrotnych zmian, ale cofnięcie operacji powinno przywrócić repozytorium do stanu, w jakim znajdowało się przed wykonaniem operacji, więc nie byłoby pierwotnego zatwierdzenia do przywrócenia.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;large-load&lt;/code&gt;&lt;/strong&gt;: UI powinien szybko ładować duże repozytorium.
    &lt;ul&gt;
      &lt;li&gt;UI nie powinien zawieszać się w żadnym momencie i powinien pokazywać przydatne informacje zaraz po załadowaniu. Nie powinieneś czekać na załadowanie całego repozytorium, zanim będziesz mógł sprawdzić zatwierdzenia i gałęzi.&lt;/li&gt;
      &lt;li&gt;Program może działać wolno przy pierwszym wywołaniu w celu zbudowania niezbędnych pamięci podręcznych, ale musi reagować szybko na kolejne wywołania.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;large-ops&lt;/code&gt;&lt;/strong&gt;: UI powinien reagować szybko podczas wykonywania różnych operacji, takich jak sprawdzanie zatwierdzeń i gałęzi, lub scalanie i zmianę bazy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dodatkowe punkty:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Przyznam honorowe punkty ujemne każdemu klientowi, który ośmieli się potraktować git rebase -i tak, jakby był fundamentalnym prymitywem.&lt;/li&gt;
  &lt;li&gt;Przyznam honorowe punkty bonusowe każdemu klientowi, który wydaje się szanować empiryczne badania użyteczności dla Git (lub innych VCS).
    &lt;ul&gt;
      &lt;li&gt;Gitless:&lt;a href=&quot;https://gitless.com/&quot;&gt; https://gitless.com/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;IASGE:&lt;a href=&quot;https://investigating-archiving-git.gitlab.io/&quot;&gt; https://investigating-archiving-git.gitlab.io/&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Z powodu, że nie zapisałem nic z tego, te kryteria są tylko po to, aby każdy sprzedawca tych klientów mógł wiedzieć, czy jestem pod wrażeniem, czy rozczarowany.&lt;/p&gt;

&lt;h2 id=&quot;klienty&quot;&gt;Klienty&lt;/h2&gt;

&lt;p&gt;Wybrałem arbitralnie kilku klientów z tej listy klientów. Z pewnością mylę się co do niektórych z tych punktów (lub zmieniły się od ostatniego razu), więc skomentuj.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Aktualizacja 2022-01-09: Dodałem IntelliJ.&lt;/li&gt;
  &lt;li&gt;Aktualizacja 2022-01-10: Dodałem Tower.&lt;/li&gt;
  &lt;li&gt;Aktualizacja 2023-05-28: Podniosłem ocenę &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reword&lt;/code&gt; dla Magit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Załączyłem mój własny projekt git-branchless, ale się nie liczy jako przykład innowacji w branży. Tylko jest tu, aby pokazać, że wiele z tych przepływów pracy jest naprawdę możliwych.&lt;/p&gt;

&lt;style type=&quot;text/css&quot;&gt;
th.rotate {
  /* Something you can count on */
  height: 140px;
  white-space: nowrap;
}

th.rotate &gt; div {
  transform: 
    translate(20px, 51px) /* magic numbers */
    rotate(320deg);
  width: 30px;
}
th.rotate &gt; div &gt; span {
  padding: 5px 10px;
}

#data th:nth-child(even) &gt; div &gt; span, #data td:nth-child(even) {
  background-color: #eee;
}

&lt;/style&gt;

&lt;table id=&quot;data&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;Git CLI&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.gitkraken.com/&quot;&gt;GitKraken&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://git-fork.com/&quot;&gt;Fork&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.sourcetreeapp.com/&quot;&gt;Sourcetree&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.sublimemerge.com/&quot;&gt;Sublime Merge&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.syntevo.com/smartgit/&quot;&gt;SmartGit&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.git-tower.com/&quot;&gt;Tower&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://gitup.co/&quot;&gt;GitUp&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://magit.vc/&quot;&gt;Magit&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/jesseduffield/lazygit&quot;&gt;Lazygit&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/extrawurst/gitui&quot;&gt;Gitui&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/arxanas/git-branchless&quot;&gt;git-branchless&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
    &lt;th class=&quot;rotate&quot;&gt;&lt;div&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/martinvonz/jj&quot;&gt;Jujutsu&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;reword&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;❌&amp;nbsp;&lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;
  
  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;sync&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;
  
  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;split&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;❌&amp;nbsp;&lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;
  
  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;preview&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;3&lt;/sup&gt;&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;3&lt;/sup&gt;&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;3&lt;/sup&gt;&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;4&lt;/sup&gt;&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;5&lt;/sup&gt;&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;6&lt;/sup&gt;&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;undo&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❓&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;⚠️&amp;nbsp;&lt;sup&gt;7&lt;/sup&gt;&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;large-load&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;8&lt;/sup&gt;&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;9&lt;/sup&gt;&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;
  
  &lt;tr&gt;
    &lt;th&gt;&lt;code&gt;large-ops&lt;/code&gt;&lt;/th&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;8&lt;/sup&gt;&lt;/td&gt; &lt;!-- Git CLI --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- GitKraken --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Fork --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Sourcetree --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Sublime Merge --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- SmartGit --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- Tower --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- GitUp --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- IntelliJ --&gt;
    &lt;td&gt;✅&amp;nbsp;&lt;sup&gt;9&lt;/sup&gt;&lt;/td&gt; &lt;!-- Magit --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Lazygit --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- Gitui --&gt;
    &lt;td&gt;✅&lt;/td&gt; &lt;!-- git-branchless --&gt;
    &lt;td&gt;❌&lt;/td&gt; &lt;!-- jj --&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Uwagi:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;sup&gt;1&lt;/sup&gt; Można to zrobić za pomocą &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -i&lt;/code&gt; lub odpowiednika, ale nie jest to ergonomiczne i działa tylko w przypadku zatwierdzeń osiągalnych z &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;, a nie z innych gałęzi.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;2&lt;/sup&gt; Zmiana komunikatu zatwierdzenia można wykonać bez wypożyczaniu zatwierdzenia, ale tylko na zatwierdzeń osiągalnych z &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;. Może istnieją dodatkowe ograniczenia.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;3&lt;/sup&gt; Częściowe wsparcie. Może pokazać, czy można przewijać scalanie do przodu, ale bez dodatkowych szczegołów.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;4&lt;/sup&gt; Można to robić za pomocą &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;magit-merge-preview&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;5&lt;/sup&gt; Częściowe wsparcie. Jeśli operacja spowoduje konflikt scalania, i opcja &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--merge&lt;/code&gt; nie została przekazana, zamiast tego zostanie przerwana i pokaże liczbę plików w stanu konfliktu.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;6&lt;/sup&gt; Jujutsu nie pozwala na podgląd konfliktów scalania, ale scalanie i rebase zawsze się udają, a konflikty są przechowywane w zatwierdzeniu, a potem możesz cofnąć operację, jeśli nie chcesz zajmować się konfliktami scalania. W razie potrzeby możesz nawet przywrócić starą wersję zatwierdzenia po przeprowadzeniu scalania/zmiany bazy. Pozwala to uniknąć przerywania przepływu pracy, co jest ostatecznym celem tej funkcji, dlatego oceniam, że wystarczy dla tej kategorii.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;7&lt;/sup&gt; Obsługa cofania jest eksperymentalna i zależy na reflogu, który &lt;a href=&quot;https://github.com/arxanas/git-branchless/wiki/Architecture#comparison-with-the-reflog&quot;&gt;nie może cofnąć wszystkich rodzajów operacji&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;8&lt;/sup&gt; Git ma problemy z niektórymi operacjami na dużych repozytoriach i można je ulepszyć, ale uznamy to za podstawową wydajność dla dużych repozytoriów.&lt;/li&gt;
  &lt;li&gt;&lt;sup&gt;9&lt;/sup&gt; Chyba Magit ma taką samą wydajność jak Git, ale nie sprawdziłem, bo nie używam Emacs’a.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;wyróżnienia&quot;&gt;Wyróżnienia&lt;/h2&gt;

&lt;p&gt;Pochwały:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitUp: najbardziej innowacyjny GUI dla Git’a spośród powyższych.&lt;/li&gt;
  &lt;li&gt;GitKraken: wprowadza innowację w niektórych obszarach, takie jak ulepszona obsługa scentralizowanych przepływów pracy przez ostrzeganie o współbieżnie edytowanych plikach. Te obszary nie są napisane powyżej; Po prostu zauważyłem je przy innych okazjach.&lt;/li&gt;
  &lt;li&gt;Sublime Merge: niesamowicie responsywny, jak można się spodziewać po ludziach odpowiedzialnych za &lt;a href=&quot;https://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Tower: za przyjemną implementację cofania.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wady:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fork: za utrudnienie wyszukiwania dokumentacji (bo “git fork undo” zazwyczaj produkuje wyniki cofania rozwidlenia w ogóle, a nie dla klienta Fork).&lt;/li&gt;
  &lt;li&gt;SmartGit: z braki we wszystkich testowanych kategoriach.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;powiązane-posty&quot;&gt;Powiązane posty&lt;/h2&gt;

&lt;p&gt;Poniżej znajduje się kilka ręcznie wybranych postów, które mogą Cię zainteresować.&lt;/p&gt;

&lt;table class=&quot;related-posts&quot;&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Title&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;


  &lt;tr&gt;
    &lt;td&gt;05&amp;nbsp;Jan&amp;nbsp;2023&lt;/td&gt;
    &lt;td class=&quot;this-post&quot;&gt;
      
      (this&amp;nbsp;post)
      
      &lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;/git-ui-funkcji/&quot;&gt;Gdzie są moje funkcji Git’a z przyszłości?&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Chcesz zobaczyć więcej moich postów? Obserwuj mnie &lt;a href=&quot;https://twitter.com/arxanas&quot;&gt;na Twitterze&lt;/a&gt; albo subskrybuj &lt;a href=&quot;/pl/feed.xml&quot;&gt;za pomocą RSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;komentarze&quot;&gt;Komentarze&lt;/h2&gt;

&lt;ul&gt;

&lt;li&gt;&lt;a class=&quot;icon-hacker-news&quot; href=&quot;https://news.ycombinator.com/item?id=34301543 &quot;&gt;Discussion on Hacker News&lt;/a&gt;&lt;/li&gt;


&lt;li&gt;&lt;a class=&quot;icon-lobsters&quot; href=&quot;https://lobste.rs/s/7tnnbq/where_are_my_git_ui_features_from_future &quot;&gt;Discussion on Lobsters&lt;/a&gt;&lt;/li&gt;


&lt;/ul&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/scripts/github-comment-links.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;
&lt;script&gt;

/**
 *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
 *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables */
var disqus_config = function () {
    this.page.url = &quot;https://blog.waleedkhan.name/pl/git-ui-funkcji/&quot;;
    this.page.identifier = &quot;git-ui-funkcji/&quot;;
};

(function() { // DON&apos;T EDIT BELOW THIS LINE
    var d = document, s = d.createElement(&apos;script&apos;);
    s.src = &apos;//waleedkhan-name.disqus.com/embed.js&apos;;
    s.setAttribute(&apos;data-timestamp&apos;, +new Date());
    (d.head || d.body).appendChild(s);
})();
&lt;/script&gt;

&lt;noscript&gt;Please enable JavaScript to view the &lt;a href=&quot;https://disqus.com/?ref_noscript&quot;&gt;comments powered by Disqus.&lt;/a&gt;&lt;/noscript&gt;

</description>
        <pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://blog.waleedkhan.name/pl/git-ui-funkcji/</link>
        <guid isPermaLink="true">https://blog.waleedkhan.name/pl/git-ui-funkcji/</guid>
        
        <category>git</category>
        
        <category>rant</category>
        
        <category>software-engineering</category>
        
        
      </item>
    
  </channel>
</rss>
