Hlavní navigace

Práce s řetězci v programovacím jazyku Rust

Pavel Tišnovský

V dnešním článku se budeme zabývat problematikou práce s řetězci. Nejedná se přitom o zcela triviální téma, protože práce s řetězci v Rustu se hned v několika ohledech odlišuje od ostatních céčkových programovacích jazyků.

Obsah

1. Práce s řetězci v programovacím jazyku Rust

2. Statický řetězec a jeho tisk

3. Předání statického řetězce do funkce

4. Úplná deklarace statického řetězce

5. Modifikace proměnné typu &str

6. Statický řetězec jako návratový typ funkce

7. Typ String: řetězec umístěný na haldě

8. Problematika vlastnictví proměnné typu String

9. Korektní řešení – dereference proměnné typu String

10. Spojování řetězců

11. Jak spojit dva řetězce typu String

12. Převod řetězce na sekvenci bajtů a znaků

13. Získání podřetězce „krájením“

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

15. Odkazy na Internetu

1. Práce s řetězci v programovacím jazyku Rust

Pozorný čtenář si možná všiml, že jsme se prozatím v seriálu o programovacím jazyku Rust nezabývali způsobem práce s řetězci. To sice může být poněkud překvapivé, protože se jedná o základní součást prakticky všech programovacích jazyků, ale ve skutečnosti má v Rustu význam se prací s řetězci začít zabývat až ve chvíli, kdy rozumíme dalším problematikám, především použitím referencí, použitím měnitelných a neměnitelných proměnných, vlastnictvím objektů a taktéž způsobem alokace objektů na haldě. V této chvíli jsme se již všemi zmíněnými problematikami „prokousali“, takže známe většinu informací nutných pro efektivní práci s řetězci.

V programovacím jazyku Rust jsou řetězce interně ukládány s využitím kódování UTF-8, což sice může znít překvapivě, ovšem přináší to i některé výhody. Autoři tohoto jazyka správně poukazují na to, že v současnosti prakticky všechny webové služby, XML soubory, velká část HTML stránek atd. stejně kódování UTF-8 používají, takže nemá význam neustále provádět konverzi mezi tímto kódováním a například UCS-4 (UTF-32). Navíc je při zpracování rozsáhlých XML souborů formát UTF-8 výhodnější z hlediska spotřeby operační paměti. Největší nevýhodou použití UTF-8 je nemožnost získat a vrátit n-tý znak v řetězci v konstantním čase. Pokud by se tato operace prováděla velmi často, lze samozřejmě použít vhodný objekt, který například „obaluje“ pole čtyřbajtových širokých znaků v UCS-4/UTF-32.

Mimochodem: řešení založené na formátu UTF-16, které částečně používá například Java, je vlastně polovičaté a přináší ty horší vlastnosti z obou světů – znaky jsou stále ukládány v proměnném počtu bajtů, ale spotřeba paměti je u běžných řetězců (konfigurační soubory, angličtina…) v porovnání s UTF-8 dvojnásobná.

V programovacím jazyku Rust existuje hned několik datových typů určených pro úschovu řetězců. Obecně se tyto typy rozdělují do dvou skupin podle toho, zda se jedná o takzvanou „slice“ variantu (sem spadají řetězce umístěné v kódovém segmentu) a o řetězce obalené vhodným kontejnerem a typicky umístěné na haldě. V každé skupině nalezneme čtyři typy: řetězce s kódováním UTF-8, řetězce kompatibilní s céčkem (nutno použít při volání céčkových knihoven), řetězce kompatibilní s operačním systémem (může se jednat o typ shodný s předchozím) a konečně řetězce, které mohou uchovávat cestu k souborům a adresářům (opět – nemusí se nutně jednat o zcela odlišný datový typ). V dalším textu nás budou zajímat především typy str a String:

Vlastnost Varianta „slice“ Varianta „Owned“
UTF-8 string str String
Kompatibilní s C CStr CString
Kompatibilní s OS OsStr OsString
Cesta (v OS) Path PathBuf

2. Statický řetězec a jeho tisk

Popis práce s řetězci v programovacím jazyku Rust začneme zcela jednoduchým příkladem, v němž je deklarována proměnná nazvaná message, do které se uloží reference na konstantní řetězec. Obsah této proměnné je následně vypsán na standardní výstup:

fn main() {
    let message = "Hello world!";
    println!("{}", message);
}

Překlad a spuštění prvního demonstračního příkladu:

rustc 117_str.rs
./117_str
Hello world!

V tomto případě se do proměnné message ve skutečnosti uloží reference na statický řetězec, který je neměnitelný a který je součástí výsledného spustitelného binárního souboru. Konkrétně se tento řetězec nachází v sekci nazvané .rodata, která má nastavená práva Exec a Read, nikoli však Write (ostatně i proto se jedná o neměnitelný řetězec). Jen malý důkaz, že tomu tak skutečně je:

rustc 117_str.rs
 
objdump  -S -j .rodata 117_str | grep Hello
 
   468c0:       48 65 6c 6c 6f 20 77 6f 72 6c 64 21                 Hello world!

Kód se tedy podobá céčkové deklaraci:

char *str = "Hello world!";

3. Předání statického řetězce do funkce

Vzhledem k tomu, že proměnná message obsahuje „pouze“ referenci na řetězec, můžeme jí předat do nějaké funkce, a to dokonce tolikrát, kolikrát potřebujeme. Jinými slovy – v tomto případě díky předávání referencí nedochází ke změně vlastníka objektu, takže překladač předání povolí. Povšimněte si, že typem parametru není str, ale skutečně &str:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!";
    print_str(message);
    print_str(message);
    print_str(message);
}

Překlad a spuštění druhého demonstračního příkladu:

rustc 118_str_pass_to_function.rs
./118_str_pass_to_function
Hello world!
Hello world!
Hello world!

Poznámka: toto je zdaleka nejčastější způsob předávání řetězců do funkcí a setkáme se s ním v prakticky všech skutečných aplikacích naprogramovaných v Rustu. Existují i další způsoby (použití String atd.), ale ty již nejsou tak časté.

4. Úplná deklarace statického řetězce

Ve skutečnosti vypadá přesná deklarace datového typu statického řetězce (interně uloženého do sekce .rodata) následovně:

&'static str

a nikoli pouze:

&str

Můžeme se o tom snadno přesvědčit překladem následujícího příkladu:

fn print_str(message: &'static str) {
    println!("{}", message);
}
 
fn main() {
    let message : &'static str = "Hello world!";
    print_str(message);
    print_str(message);
    print_str(message);
}

Rozdíly jsou z hlediska programátora minimální (ve druhém případě se může jednat o řetězec alokovaný kdekoli jinde), ovšem jak uvidíme v dalších kapitolách, někdy jsme nuceni do deklarace přidat modifikátor 'static, proto se s tímto modifikátorem v některých zdrojových kódech potkáme.

Překlad a spuštění třetího demonstračního příkladu:

rustc 119_str_full_declarations.rs 
./119_str_full_declarations
Hello world!
Hello world!
Hello world!

Poznámka: řetězcový literál „Hello world!“ je sice vždy statickým řetězcem, ovšem ne všechny řetězce jsou statické, tj. existující po celou dobu běhu programu už do jeho začátku. Tyto „nestatické“ řetězce vznikají například konverzí z objektu String popsaného níže.

5. Modifikace proměnné typu &str

Pokusme se zjistit, co se stane ve chvíli, kdy se pokusíme do proměnné přiřadit jednu referenci na řetězec a po určité době referenci druhou. U proměnné prozatím neuvedeme žádné modifikátory, tj. necháme překladač, aby sám odvodil, jakého je proměnná typu:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!";
    print_str(message);
    message = "Something else";
    print_str(message);
    print_str(message);
}

Překlad čtvrtého demonstračního příkladu nedopadne dobře, protože překladač ohlásí chybu ve chvíli, kdy se do stejné proměnné pokusíme přiřadit jinou referenci:

rustc 120_str_attempt_to_modify.rs
 
error[E0384]: re-assignment of immutable variable `message`
 --> 120_str_attempt_to_modify.rs:8:5
  |
6 |     let message = "Hello world!";
  |         ------- first assignment to `message`
7 |     print_str(message);
8 |     message = "Something else";
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ re-assignment of immutable variable
 
error: aborting due to previous error

Náprava je snadná; stačí si vzpomenout, že jsme podobnou problematiku řešili již v úvodním článku:

// nekorektní
fn main() {
    let i = 0;
    while i<10 {
        println!("pocitadlo: {}", i);
        i = i + 1;
    }
}
 
// korektní
fn main() {
    let mut i = 0;
    while i<10 {
        println!("pocitadlo: {}", i);
        i = i + 1;
    }
}

Pokusme se tedy použít modifikátor mut:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let mut message = "Hello world!";
    print_str(message);
    message = "Something else";
    print_str(message);
    print_str(message);
}

Překlad i spuštění pátého demonstračního příkladu již proběhne v pořádku:

rustc 121_str_mutable.rs
./121_str_mutable 
Hello world!
Something else
Something else

6. Statický řetězec jako návratový typ funkce

Práce s řetězci pravděpodobně až do této chvíle není nijak překvapivá, pouze si stačí uvědomit, že pracujeme s referencemi na neměnitelné řetězce představované typem &str. Zajímavější problém nastane ve chvíli, kdy budeme potřebovat z nějaké funkce vrátit řetězec. Zkusme nejdříve aplikovat až doposud získané znalosti a vytvořit funkci vracející &str:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn return_str() -> &str {
    "Hello world!"
}
 
fn main() {
    let message = return_str();
 
    print_str(message);
}

V tomto případě se překlad nezdaří, i když z první části chybového hlášení nemusí být zcela zřejmé, proč tomu tak je. Problém spočívá v tom, že řetězec vrácený z funkce nemá zajištěnu životnost mimo oblast platnosti této funkce (obecně jsou totiž všechny hodnoty vytvářeny na zásobníku, i když v případě řetězců je to poněkud složitější):

error[E0106]: missing lifetime specifier
 --> 122_str_return_error.rs:5:20
  |
5 | fn return_str() -> &str {
  |                    ^ expected lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
  = help: consider giving it a 'static lifetime
 
error: aborting due to previous error

Druhá část chybového hlášení je již z uživatelského hlediska mnohem přívětivější, protože nám přímo radí, co musíme udělat. Je nutné uvést nám již známý modifikátor 'static označující takovou hodnotu, která sice existuje po celou dobu běhu programu, ovšem nejedná se o globální proměnnou (ostatně stačí si vzpomenout na statické proměnné v céčku s velmi podobným významem. Druhá varianta příkladu bude vypadat takto:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn return_str() -> &'static str {
    "Hello world!"
}
 
fn main() {
    let message = return_str();
 
    print_str(message);
}

Nyní již překlad proběhne v pořádku.

Poznámka: jedná se o jednu z mála situací, kdy budete muset explicitně použít modifikátor 'static.

7. Typ String: řetězec umístěný na haldě

Zopakujme si, že datový typ str, s nímž se v naprosté většině případů pracuje pouze přes referenci typu &str, představuje z pohledu programátora neměnitelný (immutable) řetězec existující v původní podobě buď po celou dobu běhu programu, nebo od svého explicitního vytvoření. V praxi si však s takto se chovajícími řetězci mnohdy nevystačíme, protože budeme potřebovat vytvářet nové řetězce, spojovat je, měnit jejich obsah atd. V takovém případě je nutné použít odlišný typ objektu, který je v v programovacím jazyku Rust představován typem String. Tento typ objektu obaluje řetězec alokovaný na haldě.

Takový řetězec může měnit svůj obsah, může se zvětšovat, můžeme použít přetížený operátor + atd. Samozřejmě za tyto vlastnosti musíme zaplatit, a to konkrétně nutností alokací popř. realokací řetězců. Z tohoto důvodu je vždy nutné se rozmyslet, zda si v dané části aplikace nevystačíme s neměnitelnými řetězci. V případě potřeby je samozřejmě možné provést konverzi mezi &str a String či naopak, převod String→&str se dokonce obejde bez alokací paměti či přesunu znaků a proto je velmi rychlý.

8. Problematika vlastnictví proměnné typu String

Objekt typu String lze vytvořit několika způsoby. Můžeme například použít konverzi z konstantního řetězce s využitím metody to_string(). Výsledkem bude skutečně objekt typu String, který lze předat do proměnné, obsah této proměnné předat do volané funkce atd.:

fn print_str(message: String) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!".to_string();
    print_str(message);
}

Objekt typu String se při předávání do proměnné či do volané funkce chová naprosto stejným způsobem, jako jakýkoli jiný objekt (vzpomeňme například na naše komplexní čísla Complex). To mj. znamená, že se předáním mění i vlastnictví objektu (ownership), takže po prvním zavolání funkce print_str() již proměnná message daný řetězec nevlastní! Můžeme se o tom snadno přesvědčit pokusem o překlad následujícího demonstračního příkladu:

fn print_str(message: String) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!".to_string();
    print_str(message);
    print_str(message);
    print_str(message);
}

Překladač oznámí chybu u druhého a třetího volání funkce print_str. Navíc nám naznačuje, že objekty typu String neimplementují trait Copy, což sice může vypadat neprakticky, ale opak je pravdou, protože většinou nepotřebujeme, aby nám překladač automaticky klonoval potenciálně obrovské bloky paměti:

error[E0382]: use of moved value: `message`
 --> 124_String_not_working.rs:8:15
  |
7 |     print_str(message);
  |               ------- value moved here
8 |     print_str(message);
  |               ^^^^^^^ value used here after move
  |
  = note: move occurs because `message` has type `std::string::String`, which does not implement the `Copy` trait
 
error[E0382]: use of moved value: `message`
 --> 124_String_not_working.rs:9:15
  |
7 |     print_str(message);
  |               ------- value moved here
8 |     print_str(message);
9 |     print_str(message);
  |               ^^^^^^^ value used here after move
  |
  = note: move occurs because `message` has type `std::string::String`, which does not implement the `Copy` trait
 
error: aborting due to 2 previous errors

Poznámka: nejedná se o nám neznámý rys jazyka, protože jsme se s ním již několikrát setkali, například zde:

struct Complex {
    real: f32,
    imag: f32,
}
 
fn print_complex(c:Complex) {
    println!("complex number: {}+{}i", c.real, c.imag);
}
 
fn main() {
    let c1 = Complex{real:1.0, imag:2.0};
    println!("complex number: {}+{}i", c1.real, c1.imag);
    print_complex(c1);
    println!("complex number: {}+{}i", c1.real, c1.imag);
}

9. Korektní řešení – dereference proměnné typu String

Výše zmíněný problém má ve skutečnosti velmi snadné řešení, resp. přesněji řečeno dokonce dvě řešení. První řešení spočívá ve vytvoření kopie řetězce, což však většinou nechceme a ani nepotřebujeme. Druhé řešení spočívá v předání reference na objekt typu String, takže se vlastnictví nezmění. Referenci umíme získat – použije se zápis &proměnná. Výsledkem je nám již známý typ &str (nejedná se přitom o žádnou magii, ale o explicitní implementaci traitu Deref. Vzhledem k tomu, že konverze String→&str je velmi rychlá (prakticky zadarmo), používá se velmi často, což je ostatně důvod platnosti výše uvedeného tvrzení, že se prakticky v každé aplikaci setkáme s funkcemi akceptujícími parametr/parametry typu &str.

Upravený příklad vypadá takto:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!".to_string();
    print_str(&message);
    print_str(&message);
    print_str(&message);
}

Překlad a spuštění:

rustc 125_String_to_str_coercion.rs 
./125_String_to_str_coercion 
Hello world!
Hello world!
Hello world!

10. Spojování řetězců

Vzhledem k tomu, že objekty typu String obsahují měnitelné řetězce, nebude velkým překvapením, že existuje několik pomocných metod, například insert_str(), push_str(), trim(), trim_left() či trim_right() určených pro modifikaci řetězců. Navíc je pro objekty typu String přetížen operátor +, který dovoluje připojit další řetězec (typu str resp. &str). Podívejme se na jednoduchý příklad:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let mut message = "Hello world!".to_string();
    print_str(&message);
    message.push_str("\n42");
    print_str(&message);
    message = message + "\n***";
    print_str(&message);
}

Překlad a spuštění tohoto příkladu dopadne podle očekávání:

rustc 126_String_concatenation.rs 
./126_String_concatenation 
Hello world!
Hello world!
42
Hello world!
42
***

Poznámka: každá z výše zmíněných metod končících na _str existuje i ve variantě bez této přípony. Ovšem v tomto případě se k řetězci připojuje jediný znak typu char. Příkladem mohou být metody insert() a insert_str(), přičemž první metoda slouží k přidání znaku na určenou pozici, zatímco metoda druhá slouží k přidání řetězce (typu &str).

11. Jak spojit dva řetězce typu String

Z předchozí kapitoly již víme, že pomocí operátoru + můžeme k řetězci obaleném objektem typu String přidat řetězec typu &str;. Na druhou stranu ovšem není možné + použít pro spojení dvou Stringů, o čemž se ostatně můžeme snadno přesvědčit:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message1 : String = "Hello".to_string();
    let message2 : String = "world".to_string();
 
    print_str(&message1);
    print_str(&message2);
 
    let message = message1 + " " + message2 + "!";
 
    print_str(&message);

Překladač ohlásí chybu při pokusu o připojení druhého Stringu:

error[E0308]: mismatched types
  --> 127_String_concatenation_2.rs:12:36
   |
12 |     let message = message1 + " " + message2 + "!";
   |                                    ^^^^^^^^ expected &str, found struct `std::string::String`
   |
   = note: expected type `&str`
   = note:    found type `std::string::String`
 
error: aborting due to previous error

Řešení je velmi snadné – musíme použít referenci na objekt typu String pomocí operátoru &:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message1 : String = "Hello".to_string();
    let message2 : String = "world".to_string();
 
    print_str(&message1);
    print_str(&message2);
 
    let message = message1 + " " + &message2 + "!";
 
    print_str(&message);
}

12. Převod řetězce na sekvenci bajtů a znaků

Vzhledem k tomu, že řetězce jsou interně ukládány v kódování UTF-8, můžeme se na ně dívat dvěma pohledy. Buď se jedná o sekvenci bajtů nebo o sekvenci znaků, přičemž obecně může být znaků méně než bajtů (všechny znaky kromě základních znaků ASCII jsou reprezentovány větším množstvím bajtů). Opět si to můžeme snadno vyzkoušet, protože pro objekt typu String existují metody vracející iterátory umožňující procházet řetězcem po bajtech i po znacích:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message = "Hello world!";
 
    for byte in message.as_bytes() {
        println!("{}", byte);
    }
 
    for char in message.chars() {
        println!("{}", char);
    }
 
    print_str(message);
}

Po spuštění získáme následující výstup naznačující, že počet ASCII znaků se v tomto případě rovná počtu bajtů, kterými jsou znaky reprezentovány:

72
101
108
108
111
32
119
111
114
108
100
33
H
e
l
l
o

w
o
r
l
d
!
Hello world!

Zkusme nyní tuzemská nabodeníčka:

fn print_str(message: &str) {
    println!("{}", message);
}
 
fn main() {
    let message = "ěščř";
 
    for byte in message.as_bytes() {
        println!("{}", byte);
    }
 
    for char in message.chars() {
        println!("{}", char);
    }
 
    print_str(message);
}

Zde je již patrné, že řetězec je uložen v osmi bajtech a přitom představuje čtyři znaky:

196
155
197
161
196
141
197
153
ě
š
č
ř
ěščř

13. Získání podřetězce „krájením“

Vzhledem k výše uvedenému rozdílu při chápání řetězců jako sekvence bajtů či znaků jazyk Rust nepodporuje přímé indexování znaků v řetězci (typu &str). Ovšem, což je poměrně zajímavé, je podporováno získání podřetězce zápisem proměnná[from..to], přičemž from a to jsou indexy bajtů nikoli znaků. To může být dosti matoucí a může dokonce docházet k běhovým chybám, když se „trefíme“ do bajtu uprostřed znaku. Ukažme si způsob použití:

fn print_str(message: &str) {
    println!("'{}'", message);
}
 
fn main() {
    let message = "Hello world!";
 
    let part1 = &message[0..5]; // pet znaku 0,1,2,3 a 4
    let part2 = &message[5..6]; // jeden znak, to je "krome"
    let part3 = &message[6..12];
 
    print_str(part1);
    print_str(part2);
    print_str(part3);
}

Po spuštění se vypíše:

'Hello'
' '
'world!'

Výsledkem „krájení“ je opět řetězec typu str, takže lze napsat i tento kód:

fn print_str(message: &str) {
    println!("'{}'", message);
}
 
fn main() {
    let message = "Hello world!";
 
    let part = &message[0..10][2..5];
    print_str(part);
}

S výsledkem „llo“.

14. 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
117_str.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/117_str.rs
118_str_pass_to_function.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/118_str_pass_to_function­.rs
119_str_full_declarations.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/119_str_full_declaration­s.rs
120_str_attempt_to_modify.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/120_str_attempt_to_modi­fy.rs
121_str_mutable.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/121_str_mutable.rs
122_str_return_error.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/122_str_return_error.rs
123_str_return_working.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/123_str_return_working.rs
124_String_not_working.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/124_String_not_working.rs
125_String_to_str_coercion.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/125_String_to_str_coerci­on.rs
126_String_concatenation.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/126_String_concatenation­.rs
127_String_concatenation2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/127_String_concatenation.rs
128_String_concatenation3.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/128_String_concatenation.rs
129_str_indexing.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/129_str_indexing.rs
130_str_slice.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/130_str_slice.rs
131_str_slice_from_slice.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/131_str_slice_from_slice­.rs

15. Odkazy na Internetu

  1. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  2. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  3. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  4. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  5. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  6. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  7. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  8. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  9. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  10. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  11. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  12. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  13. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  14. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  15. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  16. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  17. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  18. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  19. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  20. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  21. Rust – home page
    https://www.rust-lang.org/en-US/
  22. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  23. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  24. The Rust Programming Language
    https://doc.rust-lang.org/book/
  25. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  26. Go – home page
    https://golang.org/
  27. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  28. 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/
  29. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  30. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  31. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  32. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  33. Go na Redditu
    https://www.reddit.com/r/golang/
  34. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  35. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  36. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  37. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  38. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  39. 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
  40. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  41. UTF-8 Everywhere
    http://utf8everywhere.org/
  42. Rust by Example
    http://rustbyexample.com/
  43. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  44. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  45. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  46. 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
  47. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  48. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  49. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference
Našli jste v článku chybu?
28. 1. 2017 10:37
atarist (neregistrovaný)

Jen možná doplním, že v céčku se ten řádek:

char *str="Hello";

může chovat rozdílně ve chvíli, kdy se pokusíme zapsat:

str[4]='*';

V Unixech na procesorech s ochranou paměti (=všechny moderní) to segfaultne, ale třeba v DOSu normálně půjde ten konstantní řetězec přepsat, na MCU se nemusí stát nic (do FLASH nezapíšeš, ale ochrana se taky nekoná), mnohdy to ani nepůjde přeložit, protože na opravdu malých MCU ani nejsou instrukce pro zápis do code segmentu.

29. 1. 2017 8:34
xxxxx (neregistrovaný)

Myslel jsem to obecně, ale platí i pro const. Protože klasické C s const moc nepočítá. Pokud už ho 'C' překladač vezme, knihovny s ním nepočítají. Když už jsme u stringů stringů, strstr výsledek je bez const, vstupem přitom mohou být const. Platí obecně, protože C přetypování const poinetrů na non-const někdy dovolí /max. varuje, někdy ani to ne/, C++ zakáže.