Hlavní navigace

Ukazatele v Rustu aneb temná strana Síly 2

Pavel Tišnovský

V dalším článku o jazyce Rust budeme pokračovat v popisu vlastností ukazatelů (pointerů), které sice v Rustu existují, ale jsou používány s mnohem menší frekvencí než například v jazyku C.

Obsah

1. Přístup ke složkám datové struktury přes ukazatel

2. Změna složek datové struktury ve chvíli, kdy již byla odstraněna z paměti

3. Použití metody as_ptr()

4. Ukazatelová aritmetika: metoda offset()

5. Přístup mimo prvky pole přes ukazatel

6. Zápis do paměti přes ukazatel

7. Ukazatel NULL

8. Pokus o přístup do paměti přes ukazatel NULL

9. Operace std::ptr::write()

10. Chování aplikace při přímém přiřazení nové hodnoty do proměnné

11. Chování aplikace při použití operace std::ptr::write()

12. Kdy nepoužít ukazatele?

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

14. Odkazy na Internetu

1. Přístup ke složkám datové struktury přes ukazatel

Důležité upozornění: tento článek se, podobně jako článek předchozí, zabývá relativně okrajovou částí programovacího jazyka Rust. V praxi byste se s použitím ukazatelů v Rustu neměli setkat příliš často, a to především z toho důvodu, že mnohé vlastnosti ukazatelů jsou nahrazeny jinými prostředky – referencemi, „řezy“ (slice), typem „Box“, typem „Rc“ atd. Na druhou stranu je ovšem dobré znát i temné a nebezpečné zákoutí programovacího jazyka.

Vraťme se k demonstračnímu příkladu, který jsme si ukázali na konci předchozího článku. V tomto příkladu je deklarován datový typ (struktura) Complex, k níž existuje i destruktor. Následně vytvoříme hodnotu tohoto typu a adresu, na níž je hodnota uložena, předáme do ukazatele. V příkladu je dále ukázáno, že k prvkům datové struktury je možné přistupovat i přes ukazatel: (*pointer).real. Přitom je nutné mít na paměti, že v Rustu neexistuje obdoba operátoru – > známého z C a C++:

#[derive(Debug)]
struct Complex {
    real: f32,
    imag: f32,
}
 
impl Complex {
    fn new(real: f32, imag: f32) -> Complex {
        println!("Constructing complex number: {:}+{:}i", real, imag);
        Complex{real:real, imag:imag}
    }
}
 
impl Drop for Complex {
    fn drop(&mut self) {
        println!("Dropping complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
fn main() {
    let mut value: Complex = Complex::new(1.0, 2.0);
    let pointer: *mut Complex;
 
    pointer = &mut value;
 
    println!("{:?}", value);
    unsafe {
        println!("{:?}", *pointer);
    }
 
    value.real = 10.0;
    value.imag = 20.0;
 
    println!("{:?}", value);
    unsafe {
        println!("{:?}", *pointer);
    }
 
    unsafe {
        (*pointer).real = 20.0;
        (*pointer).imag = 40.0;
    }
 
    println!("{:?}", value);
 
    unsafe {
        println!("{:?}", *pointer);
    }
}

Podívejme se, jaké zprávy se vypíšou při spuštění tohoto příkladu:

Constructing complex number: 1+2i
Complex { real: 1, imag: 2 }
Complex { real: 1, imag: 2 }
Complex { real: 10, imag: 20 }
Complex { real: 10, imag: 20 }
Complex { real: 20, imag: 40 }
Complex { real: 20, imag: 40 }
Dropping complex number: 20+40i

2. Změna složek datové struktury ve chvíli, kdy již byla odstraněna z paměti

Při použití ukazatelů překladač programovacího jazyka Rust předpokládá, že programátor přesně ví, co dělá, takže se neprovádí prakticky žádné kontroly životnosti/viditelnost proměnných. V následujícím příkladu má proměnná value omezenou živostnost na „svůj“ blok {}, zatímco ukazatel nazvaný pointer k proměnné přistupuje i ve chvíli, kdy již byla opuštěna oblast její platnosti (to bude patrné po spuštění aplikace):

#[derive(Debug)]
struct Complex {
    real: f32,
    imag: f32,
}
 
impl Complex {
    fn new(real: f32, imag: f32) -> Complex {
        println!("Constructing complex number: {:}+{:}i", real, imag);
        Complex{real:real, imag:imag}
    }
}
 
impl Drop for Complex {
    fn drop(&mut self) {
        println!("Dropping complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
fn main() {
    let pointer: *mut Complex;
 
    {
        let mut value: Complex = Complex::new(1.0, 2.0);
        pointer = &mut value;
 
        println!("{:?}", value);
        unsafe {
            println!("{:?}", *pointer);
        }
 
        value.real = 10.0;
        value.imag = 20.0;
 
        println!("{:?}", value);
        unsafe {
            println!("{:?}", *pointer);
        }
 
        unsafe {
            (*pointer).real = 20.0;
            (*pointer).imag = 40.0;
        }
        println!("{:?}", value);
        unsafe {
            println!("{:?}", *pointer);
        }
    }
 
    // !!!
    unsafe {
        (*pointer).real = -1.1;
        (*pointer).imag = -2.2;
        println!("{:?}", *pointer);
    }
}

Velmi zajímavé věci nastanou při spuštění aplikace. Povšimněte si především toho, že skutečně vypršela oblast viditelnosti proměnné typu Complex a dokonce byl zavolán její destruktor. Teprve poté jsme přes ukazatel zapsali do paměti, která ovšem již proměnné nepatří! V tomto konkrétním případě sice nedošlo k pádu aplikace (což souvisí se způsobem alokace zásobníkových rámců), ale samozřejmě se jedná o sémanticky špatně napsaný program:

Constructing complex number: 1+2i
Complex { real: 1, imag: 2 }
Complex { real: 1, imag: 2 }
Complex { real: 10, imag: 20 }
Complex { real: 10, imag: 20 }
Complex { real: 20, imag: 40 }
Complex { real: 20, imag: 40 }
Dropping complex number: 20+40i
Complex { real: -1.1, imag: -2.2 }

Představte si navíc tento (sémanticky taktéž chybný) příklad, který opět půjde přeložit:

fn get_ptr_to_complex() -> *mut Complex {
    let mut value: Complex = Complex::new(1.0, 2.0);
    println!("{:?}", value);
    return &mut value;
}

3. Použití metody as_ptr()

U některých typů je možné získat ukazatel na hodnotu daného typu pomocí metody as_ptr(). Z datových typů, s nimiž se často setkáte, se jedná především o řetězce (str) a řezy poli (slice). Vzhledem k tomu, že v naprosté většině případů dokáže překladač Rustu automaticky volat metody pro slice přímo nad zdrojovými poli (pro které je řez vytvořen), je možné metodu as_ptr() volat i pro celé pole:

    let array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer: *const i32 = array.as_ptr();
 
    println!("{:?}", array);

Poznámka: ve skutečnosti je řez/slice interně reprezentován právě ukazatelem + počtem prvků.

4. Ukazatelová aritmetika: metoda offset()

Oproti programovacímu jazyku C nejsou v Rustu některé operace s ukazateli prováděny přes „klasickou“ ukazatelovou aritmetiku (přičtení offsetu k ukazateli přetíženým operátorem + atd.), ovšem namísto toho je možné použít metodu offset(), která vypočte offset na základě datového typu ukazatele – jinými slovy pokud se jedná o ukazatel na hodnotu typu i8 nebo pole i8, bude offet použit přímo, u typu i32 bude offset vynásoben čtyřmi, protože jeden prvek i32 zabírá v paměti čtyři bajty. Podívejme se na jednoduchý příklad, v němž se vytvoří pole s pěti prvky typu i32 a následně se přistoupí k prvnímu prvku přímo přes ukazatel, dále ke stejnému prvku přes offset (nulový) a poté ke druhému prvku pole:

fn main() {
    let array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer: *const i32 = array.as_ptr();
 
    println!("{:?}", array);
    unsafe {
        println!("{}", *pointer);
        println!("{}", *pointer.offset(0));
        println!("{}", *pointer.offset(1));
    }
}

Po překladu a spuštění by se mělo vypsat:

[1, 2, 3, 4, 5]
1
1
2

Typ ukazatele se odvodí automaticky, takže ho nemusíme explicitně zapisovat:

fn main() {
    let array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer = array.as_ptr();
 
    println!("{:?}", array);
    unsafe {
        println!("{}", *pointer);
        println!("{}", *pointer.offset(0));
        println!("{}", *pointer.offset(1));
    }
}

Ukazatel ovšem můžeme získat například i pro druhý prvek pole, čímž se situace změní, protože offsety budou počítány relativně k tomuto prvku. Zde můžeme použít záporného offsetu k přístupu do předchozího prvku:

fn main() {
    let array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer: *const i32 = &array[1] as *const i32;
 
    println!("{:?}", array);
    unsafe {
        println!("{}", *pointer);
        println!("{}", *pointer.offset(-1));
        println!("{}", *pointer.offset(1));
    }
}

Po překladu a spuštění by se mělo vypsat:

[1, 2, 3, 4, 5]
2
1
3

5. Přístup mimo prvky pole přes ukazatel

Pravděpodobně jste si povšimli, že metodu offset() voláme v bloku unsafe. To je vyžadováno překladačem z toho prostého důvodu, že není (a obecně ani nemůže být) prováděna žádná kontrola, jestli přes offset stále přistupujeme k platným adresám či zda je offset zadán špatně. V následujícím programu hned na třech místech přistupujeme do paměti, která nám vlastně nepatří:

fn main() {
    let array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer: *const i32 = &array[0] as *const i32;
 
    println!("{:?}", array);
    unsafe {
        println!("{}", *pointer);
        println!("{}", *pointer.offset(1));
 
        // !!!
        println!("{}", *pointer.offset(10));
 
        // !!!
        println!("{}", *pointer.offset(-1));
 
        // !!!
        println!("{}", *pointer.offset(-10));
    }
}

V mém případě se program podařilo spustit bez segfaultu (což však není zaručeno), ovšem samozřejmě přečteme pseudonáhodné hodnoty:

[1, 2, 3, 4, 5]
1
2
32678
1808587265
0

6. Zápis do paměti přes ukazatel

Přes metodu offset() získáme vlastně jen jiný ukazatel, takže můžeme provádět i nepřímý zápis. V následujícím příkladu se nejdříve vypíšou původní hodnoty pětiprvkového pole, následně se dva prvky změní, a to nepřímo přes ukazatel a posléze se vypíše nový obsah pole. Samozřejmě i zápis přes ukazatel a výpočet offsetu musí být umístěn v bloku unsafe:

fn main() {
    let mut array: [i32; 5] = [1, 2, 3, 4, 5];
 
    let pointer: *mut i32 = array.as_mut_ptr();
 
    println!("{:?}", array);
    unsafe {
        *pointer = 100;
        *pointer.offset(2) = 200;
    }
    println!("{:?}", array);
}

Příklad výstupu:

[1, 2, 3, 4, 5]
[100, 2, 200, 4, 5]

7. Ukazatel NULL

V programovacím jazyku Rust je možné pracovat i s ukazatelem obsahujícím speciální hodnotu NULL. Ta se vytváří jednoduše – funkcí ptr::null() pro ukazatele na konstantní hodnoty popř. funkcí ptr::null_mut() pro ukazatele na hodnoty měnitelné. Použití těchto funkcí je snadné:

use std::ptr;
 
fn main() {
    let pointer: *const i32 = ptr::null();
 
    println!("{:p}", pointer);
}

Příklad výpisu asi skalní céčkaře nepřekvapí:

0x0

Inicializace ukazatele na měnitelnou hodnotu:

use std::ptr;
 
fn main() {
    let pointer: *mut i32 = ptr::null_mut();
 
    println!("{:p}", pointer);
}

Příklad výpisu (skutečně se zde jedná o stejnou adresu), ovšem její typ je odlišný:

0x0

Pokud budeme chtít ukazatel sice inicializovat na hodnotu NULL, ale později adresu změnit, musíme deklarovat měnitelnou (mutable) proměnnou:

use std::ptr;
 
fn main() {
    let mut pointer: *const i32 = ptr::null();
 
    println!("{:p}", pointer);
 
    let value: i32 = 42;
    pointer = &value;
 
    println!("{:p}", pointer);
}

Příklad výpisu:

0x0
0x7fff2aab8c1c

8. Pokus o přístup do paměti přes ukazatel NULL

Přístup do paměti přes ukazatel nastavený na NULL je sice možný – překladač takovou konstrukci nebude detekovat ani ji nebude hlásit jako chybu – ale na všech moderních 32bitových a 64bitových architekturách vždy dojde k pádu aplikace v runtime (což je asi jediné rozumné chování). Otestování je snadné.

Čtení:

use std::ptr;
 
fn main() {
    let pointer: *const i32 = ptr::null();
 
    println!("{:p}", pointer);
 
    unsafe {
        println!("{}", *pointer);
    }
}

Jedna z mála možností, jak v Rustu dosáhnout segfaultu:

$ ./test
0x0
Segmentation fault
$ echo $?
139

Zápis:

use std::ptr;
 
fn main() {
    let pointer: *mut i32 = ptr::null_mut();
 
    println!("{:p}", pointer);
 
    unsafe {
        *pointer = 42;
    }
}

Nahlásí se stejná chyba:

$ ./test
0x0
Segmentation fault
$ echo $?
139

9. Operace std::ptr::write()

Další vyloženě nebezpečnou operací je operace představována funkcí std::ptr::write(). Tato funkce provádí zdánlivě banální operaci – zápis nové hodnoty na zadanou adresu:

use std::ptr;
 
fn main() {
    let mut x = 0;
    let pointer: *mut i32 = &mut x;
    let y = 42;
 
    println!("{:p}", pointer);
    println!("{}", x);
 
    unsafe {
        ptr::write(pointer, y);
    }
    println!("{}", x);
}

Ve skutečnosti se však provede jen přepis staré hodnoty hodnotou novou, ale již se neprovede korektní zrušení staré hodnoty: nezavolá se destruktor atd. Proto je nutné tuto operaci použít skutečně jen ve chvíli, kdy víme, co děláme.

10. Chování aplikace při přímém přiřazení nové hodnoty do proměnné

Podívejme se nyní, jak se aplikace chová ve chvíli, kdy do proměnné přiřadíme jinou hodnotu (k tomu se vůbec nepoužívají ukazatele):

use std::ptr;
 
#[derive(Debug)]
struct Complex {
    real: f32,
    imag: f32,
}
 
impl Complex {
    fn new(real: f32, imag: f32) -> Complex {
        println!("Constructing complex number: {:}+{:}i", real, imag);
        Complex{real:real, imag:imag}
    }
}
 
impl Drop for Complex {
    fn drop(&mut self) {
        println!("Dropping complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
fn main() {
    let mut value: Complex = Complex::new(1.0, 2.0);
    let value2: Complex = Complex::new(100.0, 200.0);
    let pointer: *mut Complex = &mut value;
 
    println!("{:p}", pointer);
    println!("{:?}", value);
 
    value = value2;
    println!("{:p}", pointer);
    println!("{:?}", value);
}

Podle očekávání získáme následující výsledek – jsou zkonstruována dvě komplexní čísla a tudíž se taktéž zavolají dva destruktory ve chvíli, kdy končí životnost hodnot:

Constructing complex number: 1+2i
Constructing complex number: 100+200i
0x7fffe54b4658
Complex { real: 1, imag: 2 }
Dropping complex number: 1+2i
0x7fffe54b4658
Complex { real: 100, imag: 200 }
Dropping complex number: 100+200i

Podobně tomu bude ve chvíli, kdy upravíme funkci main() tak, aby se přiřazení provedlo přes ukazatel:

fn main() {
    let mut value: Complex = Complex::new(1.0, 2.0);
    let value2: Complex = Complex::new(100.0, 200.0);
    let pointer: *mut Complex = &mut value;
 
    println!("{:p}", pointer);
    println!("{:?}", value);
 
    unsafe {
        *pointer = value2;
    }
 
    println!("{:p}", pointer);
    println!("{:?}", value);
}

Překladač Rustu je v tomto případě dost chytrý na to, aby poznal, že přiřazením v bloku unsafe končí životnost prvního komplexního čísla.

11. Chování aplikace při použití operace std::ptr::write()

Třetí modifikace příkladu spočívá v použití std::ptr::write() namísto přiřazení:

use std::ptr;
 
#[derive(Debug)]
struct Complex {
    real: f32,
    imag: f32,
}
 
impl Complex {
    fn new(real: f32, imag: f32) -> Complex {
        println!("Constructing complex number: {:}+{:}i", real, imag);
        Complex{real:real, imag:imag}
    }
}
 
impl Drop for Complex {
    fn drop(&mut self) {
        println!("Dropping complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
fn main() {
    let mut value: Complex = Complex::new(1.0, 2.0);
    let value2: Complex = Complex::new(100.0, 200.0);
    let pointer: *mut Complex = &mut value;
 
    println!("{:p}", pointer);
    println!("{:?}", value);
 
    unsafe {
        ptr::write(pointer, value2);
    }
 
    println!("{:p}", pointer);
    println!("{:?}", value);
}

Zde je již chování diametrálně odlišné, neboť se nezavolá destruktor pro první komplexní číslo!

Constructing complex number: 1+2i
Constructing complex number: 100+200i
0x7fff05ac3478
Complex { real: 1, imag: 2 }
0x7fff05ac3478
Complex { real: 100, imag: 200 }
Dropping complex number: 100+200i

Tuto operaci má tedy smysl použít jen v několika ojedinělých případech, kdy přesně víme, že nechceme volat destruktory staré hodnoty.

12. Kdy nepoužít ukazatele?

Jak již bylo naznačeno v úvodní kapitole, používají se ukazatele v Rustu jen v relativně malém množství případů, typicky při nutnosti volání nativních funkcí přes FFI či při implementaci efektivních datových struktur (viz zdrojové kódy pro Vec). Důležitější je pro céčkaře vědět, kdy naopak ukazatele nepoužít, protože Rust nabízí lepší (typicky bezpečnější) řešení:

  • Pokud potřebujete předat parametr do funkce odkazem a ne hodnotou, použijte referenci.
  • Pokud potřebujete alokovat strukturu na haldě, použijte typ Box. Rust si sám ohlídá viditelnost/životnost struktury.
  • Pokud navíc potřebujete automatickou správu paměti s počítáním referencí, je k dispozici typ Rc a Arc.
  • Někdy se v C používají ukazatele proto, aby se mohla vrátit „žádná hodnota“ (tedy NULL) nebo „chyba“ (většinou taktéž NULL). V takovém případe je mnohem lepší použít typ Option nebo Result (liší se především sémantikou)

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

Všechny dnes popisované demonstrační příklady byly, ostatně 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. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:

14. Odkazy na Internetu

  1. The Rustonomicon: The Dark Arts of Advanced and Unsafe Rust Programming
    https://doc.rust-lang.org/nomicon/
  2. Primitive Type pointer
    https://doc.rust-lang.org/std/primitive.pointer.html
  3. Cargo: správce projektů a balíčků pro programovací jazyk Rust
    https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/
  4. Network Communication and Serialization in Rust
    https://www.safaribookson­line.com/blog/2014/01/28/net­work-communication-serialization-rust/
  5. Crate bincode
    http://tyoverby.com/binco­de/bincode/index.html
  6. Struct std::fs::File
    https://doc.rust-lang.org/std/fs/struct.File.html
  7. Trait std::io::Seek
    https://doc.rust-lang.org/std/io/trait.Seek.html
  8. Trait std::io::Read
    https://doc.rust-lang.org/std/io/trait.Read.html
  9. Trait std::io::Write
    https://doc.rust-lang.org/std/io/trait.Write.html
  10. Trait std::io::BufRead
    https://doc.rust-lang.org/std/io/trait.BufRead.html
  11. Module std::io::prelude
    https://doc.rust-lang.org/std/io/prelude/index.html
  12. std::net::IpAddr
    https://doc.rust-lang.org/std/net/enum.IpAddr.html
  13. std::net::Ipv4Addr
    https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html
  14. std::net::Ipv6Addr
    https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
  15. TcpListener
    https://doc.rust-lang.org/std/net/struct.TcpLis­tener.html
  16. TcpStream
    https://doc.rust-lang.org/std/net/struct.TcpStream.html
  17. Binary heap (Wikipedia)
    https://en.wikipedia.org/wi­ki/Binary_heap
  18. Binární halda (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Bin%C3%A1rn%C3%AD_halda
  19. Halda (datová struktura)
    https://cs.wikipedia.org/wi­ki/Halda_%28datov%C3%A1_struk­tura%29
  20. Struct std::collections::HashSet
    https://doc.rust-lang.org/std/collections/struc­t.HashSet.html
  21. Struct std::collections::BTreeSet
    https://doc.rust-lang.org/std/collections/struc­t.BTreeSet.html
  22. Struct std::collections::BinaryHeap
    https://doc.rust-lang.org/std/collections/struc­t.BinaryHeap.html
  23. Set (abstract data type)
    https://en.wikipedia.org/wi­ki/Set_%28abstract_data_ty­pe%29#Language_support
  24. Associative array
    https://en.wikipedia.org/wi­ki/Associative_array
  25. Hash Table
    https://en.wikipedia.org/wi­ki/Hash_table
  26. B-tree
    https://en.wikipedia.org/wiki/B-tree
  27. Pedro Celis: Robin Hood Hashing (naskenované PDF!)
    https://cs.uwaterloo.ca/re­search/tr/1986/CS-86–14.pdf
  28. Robin Hood hashing
    http://codecapsule.com/2013/11/11/ro­bin-hood-hashing/
  29. Robin Hood hashing: backward shift deletion
    http://codecapsule.com/2013/11/17/ro­bin-hood-hashing-backward-shift-deletion/
  30. Module std::collections
    https://doc.rust-lang.org/std/collections/
  31. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  32. Struct std::collections::VecDeque
    https://doc.rust-lang.org/std/collections/struc­t.VecDeque.html
  33. Struct std::collections::LinkedList
    https://doc.rust-lang.org/std/collections/struc­t.LinkedList.html
  34. Module std::fmt
    https://doc.rust-lang.org/std/fmt/
  35. Macro std::println
    https://doc.rust-lang.org/std/macro.println.html
  36. Enum std::result::Result
    https://doc.rust-lang.org/std/result/enum.Result.html
  37. Module std::result
    https://doc.rust-lang.org/std/result/
  38. Result
    http://rustbyexample.com/std/re­sult.html
  39. Rust stdlib: Option
    https://doc.rust-lang.org/std/option/enum.Option.html
  40. Module std::option
    https://doc.rust-lang.org/std/option/index.html
  41. Rust by example: option
    http://rustbyexample.com/std/op­tion.html
  42. Rust by example: if-let
    http://rustbyexample.com/flow_con­trol/if_let.html
  43. Rust by example: while let
    http://rustbyexample.com/flow_con­trol/while_let.html
  44. Rust by example: Option<i32>
    http://rustbyexample.com/std/op­tion.html
  45. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  46. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  47. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  48. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  49. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  50. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  51. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  52. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  53. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  54. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  55. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  56. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  57. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  58. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  59. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  60. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  61. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  62. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  63. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  64. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  65. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  66. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  67. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  68. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  69. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  70. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  71. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  72. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  73. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  74. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  75. Rust – home page
    https://www.rust-lang.org/en-US/
  76. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  77. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  78. The Rust Programming Language
    https://doc.rust-lang.org/book/
  79. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  80. Go – home page
    https://golang.org/
  81. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  82. 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/
  83. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  84. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  85. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  86. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  87. Go na Redditu
    https://www.reddit.com/r/golang/
  88. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  89. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  90. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  91. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  92. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  93. 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
  94. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  95. UTF-8 Everywhere
    http://utf8everywhere.org/
  96. Rust by Example
    http://rustbyexample.com/
  97. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  98. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  99. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  100. 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
  101. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  102. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  103. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference
Našli jste v článku chybu?