Hlavní navigace

Práce s makry v programovacím jazyku Rust

Pavel Tišnovský

Důležitou součástí jazyka Rust je jeho systém maker umožňující zjednodušení zápisu některých konstrukcí. Oproti makrosystému známému z C či C++ je makrosystém v Rustu bezpečnější, což si ostatně ukážeme na několika příkladech.

Obsah

1. Práce s makry v programovacím jazyku Rust

2. Makra ve vyšších programovacích jazycích

3. Dvě varianty maker pro Rust 0.x a stabilní varianta pro Rust 1.x

4. První makro: tisk zprávy na standardní výstup

5. Za jakých okolností a jakým způsobem překladač hlásí chyby v makrech?

6. Metaproměnné v makrech

7. Rozdíl mezi textovými substitucemi a makry zapisovanými do AST

8. Volání funkcí v makru (rozdíl mezi compile time a runtime)

9. Pravidla pro určení, která větev makra se má expandovat

10. Složitější pravidla a použití separátoru =>

11. Typický demonstrační příklad: makro pro trasování vyhodnocování výrazů

12. Vylepšení trasovacího makra a použití stringify!

13. Repositář s demonstračními příklady

14. Odkazy na Internetu

1. Práce s makry v programovacím jazyku Rust

Jedním z poměrně propracovaných konceptů programovacího jazyka Rust je jeho systém maker, který programátorům umožňuje v případě potřeby vytvářet i složitější programové konstrukce popř. si ušetřit práci a nechat si část programového kódu vygenerovat. Je to umožněno především díky existenci takového typu makrosystému, který je založen na nepřímé manipulaci se symboly, které vzniknou až po parsingu zdrojového kódu a které tvoří AST (Abstract Syntax Tree). Ovšem hned na tomto místě je vhodné poznamenat, že makra používaná v jazyku Rust nemají mnoho společného s makry, které známe například z jazyků C či C++, popř. s makry, na jejichž zpracování je založen jazyk m4 (ostatně některé typické a známé problémy klasických céčkových maker si ukážeme v dalším textu).

Poznámka: i když se systém maker v C a Rustu v mnoha ohledech odlišuje, mají jednu důležitou vlastnost společnou – jsou zpracována již v čase překladu (compile time) a nikoli v čase běhu programu (runtime).

S makry jsme se již setkali a ve zdrojovém kódu je poznáme velmi snadno podle toho, že se za jejich jméno zapisuje vykřičník. To je užitečné, protože použití makra se sice podobá volání funkce či metody, ovšem mechanismus použití makra je zcela odlišný. V následujícím kódu jsou makra zvýrazněna tučným písmem:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
 
    println!("vector has {} items", vector.len());
 
    for i in 0..vector.len() {
        println!("item #{} = {}", i+1, vector[i]);
    }
}
fn main() {
    let vector = vec![1; 10];
 
    println!("vector has {} items", vector.len());
 
    for i in 0..vector.len() {
        println!("item #{} = {}", i+1, vector[i]);
    }
}

Z těchto dvou ukázek je patrné, kde lze makra použít:

  • „Nové“ programové konstrukce, zde konkrétně možnost vytvoření vektoru s použitím syntaxe vyhrazené původně pro pole.
  • Situace, kdy je zapotřebí přistupovat nejenom k vyhodnocenému výrazu, ale i k jeho původní formě.

2. Makra ve vyšších programovacích jazycích

V programovacích jazycích C, C++ i m4 se makra používají pro „pouhé“ provádění textových substitucí v průběhu načítání zdrojových kódů, zatímco makrosystém implementovaný v jazyku Rust je založen na modifikaci AST, což na jednu stranu umožňuje mnohem hlubší zásahy do kódu, na stranu druhou jsou makra bezpečnější („hygienická“). V tomto ohledu má jazyk Rust poměrně blízko k LISPovským jazykům, v nichž je většinou makrosystém prakticky nedílnou součástí programovacího jazyka, protože jsou v něm realizovány mnohdy i základní programové konstrukce (navíc je LISP homoikonickým jazykem, což situaci dále zjednodušuje). Asi nejtypičtějším příkladem použití maker v LISPu je makro loop použité v Common Lispu (na druhou stranu někteří vývojáři soudí, že podobná makra zbytečně do Common Lispu přidávají imperativní kód). Některé vlastnosti tohoto makra jsou popsány na stránce http://www.ai.sri.com/pkar­p/loop.html. Podobným způsobem se s makry pracuje i v dalších jazycích založených na LISPu; příkladem je Clojure.

3. Dvě varianty maker pro Rust 0.x a stabilní varianta pro Rust 1.x

Ve starších verzích programovacího jazyka Rust bylo možné používat dva paralelní makrosystémy. Jeden typ maker se nazývat běžně makra, druhý typ pak compiler plugins. Každý z těchto systémů měl své přednosti ale i zápory. Pravděpodobně největším záporem pluginů byl (a doposud je) fakt, že je do jisté míry závislý na interních strukturách překladače. To znamená, že by se mohl celý Rust stát závislým na jediném překladači, takže se tvůrci tohoto jazyka rozhodli, že prozatím bude oficiálně podporován pouze jeden makrosystém. Pokud ke stabilizaci compiler plugins nakonec dojde, bude možné v těchto makrech přímo manipulovat s AST, což by mohlo vést k tomu, že bude možné si upravit samotnou sémantiku Rustu (což má opět své výhody, ale i mnohé zápory).

4. První makro: tisk zprávy na standardní výstup

Ukažme si nejprve velmi jednoduché makro, které v místě svého použití vygeneruje příkaz println!(„Hello!“);. Shodou okolností je i tento příkaz makrem, to však ve skutečnosti vůbec nevadí, protože makra je možné zpracovávat rekurzivně. Deklarace nového makra zpočátku vypadá poněkud zvláštně, a to kvůli tomu, že jedno makro může mít ve skutečnosti několik podob (viz další kapitoly). Makro je pojmenované hello_world a při jeho použití se nevyžadují žádné parametry (to zajišťují první prázdné závorky):

macro_rules! hello_world {
    () => (
        println!("Hello!");
    )
}

Použití takto jednoduchého makra vypadá podobně jako volání funkce, pouze nesmíme zapomenout na přidání vykřičníku na konec jména makra:

fn main() {
    hello_world!();
}

Po překladu a spuštění se skutečně na standardní výstup vypíše řetězec „Hello!“:

Hello!

Pokud budete někdy potřebovat zjistit, jak se makro v čase překladu vašeho zdrojového kódu expanduje, je možné použít volbu –pretty=expanded. Tato volba je prozatím takzvaně „nestabilní“ (výstup se může v budoucnosti změnit), což vyžaduje použití další volby -Z unstable-options a navíc musíme počítat s tím, že překladač vypíše varovná hlášení:

rustc --pretty=expanded -Z unstable-options 187_macro_hello.rs

5. Za jakých okolností a jakým způsobem překladač hlásí chyby v makrech?

Již v předchozích článcích jsme se přesvědčili o tom, že jednou z velmi dobrých vlastností překladače Rustu jsou (relativně) srozumitelná chybová hlášení, takže si vyzkoušejme, jak je tomu v případě použití maker (protože například v případě céčka bývá složité najít skutečnou chybu). V následujícím příkladu se v makru volá neexistující funkce foobar:

macro_rules! hello_world {
    () => (
        foobar("Hello!");
    )
}
 
fn main() {
    hello_world!();
}

V tomto velmi jednoduchém případě překladač velmi přesně určil, kde k chybě došlo a navíc i zjistil, ve kterém místě programového kódu nalezneme expanzi problémového makra (připomeňme si, že první číslo za názvem souboru značí číslo řádku):

 --> 188_error_in_expansion.rs:3:9
  |
3 |         foobar("Hello!");
  |         ^^^^^^
188_error_in_expansion.rs:8:5: 8:20 note: in this expansion of hello_world! (defined in 188_error_in_expansion.rs)
 
error: aborting due to previous error

Pokud ovšem makro nikde nebudeme volat, k žádné chybě při překladu ani v runtime nedojde. To je další rozdíl oproti běžným funkcím, které se překládají a kontrolují vždy, i když ve skutečnosti nemusí být nikdy volány:

macro_rules! hello_world {
    () => (
        foobar("Hello!");
    )
}
 
fn main() {
}

6. Metaproměnné v makrech

Makro, které jsme si představili v předchozích dvou kapitolách, má ve skutečnosti velmi omezené možnosti použití, protože mu nemůžeme předat žádné parametry. Zkusme si tedy vytvořit nepatrně složitější makro akceptující jeden parametr, který se bude jmenovat $expression. Někdy se tento parametr označuje názvem metaproměnná (metavariable):

macro_rules! trace {
    ($expression:expr) => (
        println!("{:?}", $expression);
    )
}
 
fn main() {
    trace!(1+2);
}

Povšimněte si, že za jménem parametru/metaproměnné se nachází označení jejího druhu (designator). V současnosti jsou povoleny následující označení jednotlivých typů uzlů v AST:

  1. expr (pravděpodobně nejčastěji používaný – výraz)
  2. ident (taktéž často používaný – jméno proměnné či funkce)
  3. block (programový blok umístěný ve složených závorkách)
  4. item (funkce, struktura, modul atd.)
  5. pat (pattern)
  6. stmt (statement, celý příkaz)
  7. path (například ::std::mem::replace)
  8. stmt (statement)
  9. tt (token tree)
  10. ty (type)

Tento příklad po svém spuštění vypíše výslednou hodnotu výrazu 1+2, ovšem do makra se ve skutečnosti předává celý výraz, nikoli až jeho výsledek:

3

7. Rozdíl mezi textovými substitucemi a makry zapisovanými do AST

Již v úvodní kapitole jsme si naznačili, že makra v Rustu se odlišují od maker používaných v jazycích C a C++. V céčku je totiž expanze maker součástí preprocesoru, který používá velmi zjednodušený pohled na zdrojové kódy, takže může provádět pouze relativně omezené textové substituce. To vede k některým známým problémům. Podívejme se například na následující příklad, v němž máme naivní deklaraci makra určeného pro výpočet druhé mocniny. Následně je toto makro použito při výpočtu druhé mocniny hodnoty 10 a taktéž druhé mocniny výrazu 5+5:

#include <stdio.h>
 
#define sqr(x) x*x
 
int main(void)
{
    printf("%d\n", sqr(10));
    printf("%d\n", sqr(5+5));
    return 0;
}

Podle způsobu použití makra by bylo možné očekávat, že se dvakrát vypíše hodnota 100, ve skutečnosti tomu tak ale není:

100
35

Celkem jednoduše zjistíme proč, a to použitím preprocesoru jazyka C:

cpp 191_expansion_in_c.c

Na konci výstupu preprocesoru nalezneme zdrojový kód funkce main s expandovaným makrem. Z této expanze je ihned patrné, kde nastal problém – namísto (5+5)*(5+5) se počítá 5+5*5+5, což ovšem přesně odpovídá způsobu expanze makra a provedení pouhé textové substituce – náhradě jména makra za jeho tělo:

int main(void)
{
    printf("%d\n", 10*10);
    printf("%d\n", 5+5*5+5);
    return 0;
}

Jedno z běžných řešení tohoto problému spočívá v použití závorek okolo všech parametrů makra v jeho těle, tedy například takto:

#include <stdio.h>
 
#define sqr(x) (x)*(x)
 
int main(void)
{
    printf("%d\n", sqr(10));
    printf("%d\n", sqr(5+5));
 
    int x = 2;
    printf("%d\n", sqr(x));
    printf("%d\n", sqr(x++));
 
    return 0;
}

Nyní již dostaneme v první části správné výsledky, ovšem poslední volání se možná bude lišit od očekávání (funkce by se chovala jinak a vracela by 9, nikoli 6):

100
100
4
6

Překladač (minimálně gcc) správně hlásí potenciální problémy:

gcc -Wall -ansi 192_expansion_in_c_2
192_expansion_in_c_2.c: In function ‘main’:
192_expansion_in_c_2.c:12:25: warning: operation on ‘x’ may be undefined [-Wsequence-point]
     printf("%d\n", sqr(x++));
                          ^
192_expansion_in_c_2.c:3:17: note: in definition of macro ‘sqr’
 #define sqr(x) (x)*(x)

Pro zajímavost se podívejme, jak expanze vypadá u „uzávorkovaných“ parametrů:

int main(void)
{
    printf("%d\n", (10)*(10));
    printf("%d\n", (5+5)*(5+5));
 
    int x = 2;
    printf("%d\n", (x)*(x));
    printf("%d\n", (x++)*(x++));
 
    return 0;
}

Naproti tomu v Rustu tento problém nemáme, neboť makra nejsou implementována textovou expanzí, ale přímou modifikací AST. Povšimněte si, že v těle makra žádné nadbytečné závorky není nutné zapisovat (operátor ++ v Rustu neexistuje):

macro_rules! sqr {
    ($expression:expr) => ($expression * $expression);
}
 
fn main() {
    println!("{}", sqr!(10));
    println!("{}", sqr!(5+5));
 
    let x = 2;
    println!("{}", sqr!(x));
    println!("{}", sqr!(x+1));
}

Výsledek po spuštění:

100
100
4
9

8. Volání funkcí v makru (rozdíl mezi compile time a runtime)

Makra se od běžných funkcí liší především v tom, že volání funkcí je provedeno až v době běhu programu, tedy v runtime, zatímco expanze maker probíhá již v době, kdy se program teprve překládá. V tomto ohledu se od sebe C/C++ a Rust příliš neliší, což si můžeme názorně ukázat. Opět začněme céčkovým kódem:

#include <stdio.h>
 
#define sqr(x) (x)*(x)
 
int getx(void)
{
    puts("getx() called");
    return 2;
}
 
int main(void)
{
    printf("%d\n", sqr(getx()));
 
    return 0;
}

Po překladu a spuštění můžeme vidět, že se funkce getx volá dvakrát. Pokud by sqr byla normální funkce, vyhodnotil by se její parametr (výraz volající getx) samozřejmě pouze jedenkrát:

getx() called
getx() called
4

Prakticky totožným způsobem můžeme stejně pojmenované makro vytvořit a použít v Rustu:

macro_rules! sqr {
    ($expression:expr) => ($expression * $expression);
}
 
fn getx() -> i32 {
    println!("getx() called");
    2
}
 
fn main() {
    println!("{}", sqr!(getx()));
}

Po překladu a spuštění můžeme opět vidět, že se funkce getx volá dvakrát:

getx() called
getx() called
4

9. Pravidla pro určení, která větev makra se má expandovat

Opusťme nyní porovnávání Rustu s céčkem a ukažme si nové možnosti maker v Rustu. Jednou z nejzajímavějších vlastností maker je schopnost překladače určit, která větev makra se má expandovat. Podobá se to pattern matchingu, protože se porovnává jak počet parametrů makra, tak i jejich typ (výraz, identifikátor atd.). Podívejme se na jednoduché makro nazvané power, které v případě, že se mu předá jeden výraz, vrátí druhou mocninu tohoto výrazu a v případě, že se mu předají výrazy dva, provede výpočet baseexponent. V tomto druhém případě voláme metodu pow a tudíž musí dojít k přetypování (to nesouvisí se samotným makrem, ale s typovým systémem Rustu):

macro_rules! power {
    ($base:expr)                 => ($base*$base);
    ($base:expr, $exponent:expr) => (($base as i32).pow($exponent));
}
 
fn main() {
    for base in 0..10 {
         println!("{:4} {:4} {:4}", base, power!(base), power!(base, 3));
    }
}

Po překladu a spuštění tohoto příkladu získáme tři sloupce – hodnoty 0 až 9, druhé mocniny těchto hodnot a třetí mocniny:

   0    0    0
   1    1    1
   2    4    8
   3    9   27
   4   16   64
   5   25  125
   6   36  216
   7   49  343
   8   64  512
   9   81  729

Samozřejmě nejsme omezeni pouze na výpočet druhých a třetích mocnin:

macro_rules! power {
    ($base:expr)                 => ($base*$base);
    ($base:expr, $exponent:expr) => (($base as i32).pow($exponent));
}
 
fn main() {
    for base in 0..10 {
        for exponent in 2..6 {
             print!("{:5}  ", power!(base, exponent));
        }
        println!("");
    }
}

Výsledek běhu tohoto příkladu:

    0      0      0      0
    1      1      1      1
    4      8     16     32
    9     27     81    243
   16     64    256   1024
   25    125    625   3125
   36    216   1296   7776
   49    343   2401  16807
   64    512   4096  32768
   81    729   6561  59049

10. Složitější pravidla a použití separátoru =>

Možnosti „pattern matchingu“ při výběru větví maker mohou jít ještě dále, protože při volání makra se může použít separátor =>, ale i některé další znaky se speciálním významem (stačí si vzpomenout na makro vec!). V následujícím příkladu máme vytvořeno makro nazvané unifunc akceptující výraz a za ním separátor ⇒ následovaný jedním ze slov zero, ident či sqr. Tato slova se skutečně zapisují bez uvozovek:

macro_rules! unifunc {
    ($x:expr => zero )   => (0);
    ($x:expr => ident)   => ($x);
    ($x:expr => sqr  )   => ($x * $x);
}
 
fn main() {
    println!("{}", unifunc!(2 => zero));
    println!("{}", unifunc!(2 => ident));
    println!("{}", unifunc!(2 => sqr));
 
    println!("{}", unifunc!(10 => zero));
    println!("{}", unifunc!(10 => ident));
    println!("{}", unifunc!(10 => sqr));
}

Výsledek překladu a spuštění příkladu:

0
2
4
0
10
100

Vzhledem k tomu, že je očekáván výraz, můžeme namísto číselné konstanty napsat například toto:

    println!("{}", unifunc!(10+2*3 => sqr));
    256

Nebo:

    println!("{}", unifunc!(if 10<2 {10} else {100} => sqr));
    10000

11. Typický demonstrační příklad: makro pro trasování vyhodnocování výrazů

Na závěr si ukažme typický příklad použití maker – trasování vyhodnocování výrazů. Budeme chtít, aby se u každého výrazu uzavřeného do makra trace (či debug) vypsal jak původní výraz, tak i jeho vypočtená hodnota, což se hodí například pro účely logování, pro implementaci asercí atd. Připomeňme si, že funkcemi podobné funkcionality nedosáhneme, protože těm se předává již vyhodnocený výraz. První verze makra může vypadat takto (není plně funkční!):

macro_rules! trace {
    ($expression:expr) => (
        println!("{} = {}", $expression, $expression);
    )
}
 
fn main() {
    trace!(1+2);
 
    let x = 6;
    let y = 7;
 
    trace!(x*y);
}

Výsledek nás ovšem příliš nepotěší, protože i když se samotnému makru trace předají nevyhodnocené výrazy, dojde k jejich vyhodnocení v rámci makra println:

3 = 3
42 = 42

12. Vylepšení trasovacího makra a použití stringify!

Jedním z řešení je použití dalšího makra nazvaného stringify, které výraz nevyhodnotí, ale převede ho na řetězec:

macro_rules! trace {
    ($expression:expr) => (
        println!("{:?} = {}", stringify!($expression), $expression);
    )
}
 
fn main() {
    trace!(1+2);
 
    let x = 6;
    let y = 7;
 
    trace!(x*y);
    trace!(if x<y {x*2} else {y/10});
}

Nyní se již skutečně vypíše jak původní výraz, tak i jeho výsledek:

"1 + 2" = 3
"x * y" = 42
"if x < y { x * 2 } else { y / 10 }" = 12

Pokud vám nevyhovují uvozovky zobrazené okolo nevyhodnoceného výrazu, postačuje makro upravit následovně:

macro_rules! trace {
    ($expression:expr) => (
        println!("{} = {}", stringify!($expression), $expression);
    )
}

13. Repositář s demonstračními příklady

Všechny dnes popisované demonstrační příklady byly, podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/pre­sentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:

Příklad Odkaz
187_macro_hello.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/187_macro_hello.rs
188_error_in_expansion.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/188_error_in_expansion.rs
189_no_expansion_no_error.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/189_no_expansion_no_error­.rs
190_metavariable.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/190_metavariable.rs
191_expansion_in_c.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/191_expansion_in_c.c
192_expansion_in_c2.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/192_expansion_in_c2.c
193_expansion_in_rust.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/193_expansion_in_rust.rs
194_expansion_in_c3.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/194_expansion_in_c3.c
195_expansion_in_rust2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/195_expansion_in_rust2.rs
196_matching_in_macro.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/196_matching_in_macro.rs
197_matching_in_macros2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/197_matching_in_macros2­.rs
198_matching_in_macros3.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/198_matching_in_macros3­.rs
199_tracing1.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/199_tracing1.rs
200_tracing2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/200_tracing2.rs

14. Odkazy na Internetu

  1. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  2. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  3. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  4. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  5. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  6. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  7. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  8. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  9. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  10. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  11. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  12. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  13. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  14. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  15. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  16. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  17. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  18. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  19. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  20. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  21. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  22. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  23. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  24. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  25. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  26. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  27. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  28. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  29. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  30. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  31. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  32. Rust – home page
    https://www.rust-lang.org/en-US/
  33. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  34. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  35. The Rust Programming Language
    https://doc.rust-lang.org/book/
  36. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  37. Go – home page
    https://golang.org/
  38. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  39. Rust vs Go (dva roky staré hodnocení, od té doby došlo k posunům v obou jazycích)
    http://jaredforsyth.com/2014/03/22/rust-vs-go/
  40. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  41. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  42. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  43. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  44. Go na Redditu
    https://www.reddit.com/r/golang/
  45. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  46. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  47. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  48. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  49. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  50. Of the emerging systems languages Rust, D, Go and Nim, which is the strongest language and why?
    https://www.quora.com/Of-the-emerging-systems-languages-Rust-D-Go-and-Nim-which-is-the-strongest-language-and-why
  51. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  52. UTF-8 Everywhere
    http://utf8everywhere.org/
  53. Rust by Example
    http://rustbyexample.com/
  54. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  55. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  56. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  57. Porovnání Go, D a Rustu na OpenHubu:
    https://www.openhub.net/lan­guages/compare?language_na­me[]=-1&language_name[]=-1&language_name[]=dmd&lan­guage_name[]=golang&langu­age_name[]=rust&language_na­me[]=-1&measure=commits
  58. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  59. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  60. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference
Našli jste v článku chybu?
17. 2. 2017 19:13
goto fail (neregistrovaný)

Nezapomen taky na volitelne bloky za podminkami a smyckami. Je to prece super usetrit si napsani dvou {} a pak resit problemy typu Apple goto fail apod. Mozna bych vzpomenul i na templaty a krasne hlaseni pripadnych chyb (2-3 strany nejsou vyjimkou)

17. 2. 2017 1:37

Jo a ještě druhý důvod pro to "trhání očí". Programovací jazyk je jazyk (lidský) a ne bytekód. A přistupuješ k němu stejne. Ze své zkušenosti považuješ něco za spisovné (čistý kód) a něco za hovorové (ošklivě naprasený kód). Často má spisovnost kořen v lepší srozumitelnosti, ale někdy je prostě lepší výjimka než striktní pravidlo (protože se jedná o subjekt o kterém se má přemýšlet jinak - například Bůh má mít velké písmeno i přes to že to není jméno), a někdy je to dáno čistě zvykem (někdo to k…