Tak na tohle pozor. Tyhle unsafe ukazatele se v praxi používají (kromě některých extrémních případů jako je implementace ovladačů HW v Rustu apod.) prakticky jenom při volání knihoven napsaných v C. Při normálním programování v Rustu nejsou potřeba a už v žádném případě tu nejsou od toho, aby s nimi člověk zvyklý na C nebo C++ imitoval tyto jazyky, nebo dokonce obcházel borrow checker v mylné představě, že to jinak nejde a že "vím, co dělám" a překladač mě zbytečně buzeruje. Kromě referencí a tohoto nutného zla má Rust totiž ještě další typy ukazatelů: Box, Rc a Arc, které pokrývají všechny "normální" scénáře, jsou idiomatické a hlavně bezpečné. Pokud můžu autorovi doporučit, bylo by dobré tohle v dalším díle objasnit.
Díky za zpětnou vazbu. Trošku jsem upravil první kapitolu, ta věta s varováním je teď v info boxu. Jinak se samozřejmě bez ukazatelů dá v mnoha případech obejít, ostatně i proto jsou zmíněny až ve 26. části a ne hned na začátku (což by například bylo nutné v céčku, ok ne hned na začátku, ale už ve chvíli, kdy se vysvětlují standardní I/O funkce).
Ani já v C++ nepoužívám ukazatele, a pointerovou aritmetiku a vůbec netuším, proč je mi pořád předhazováno něco o nebezpečnosti toho jazyka a proč bych se měl kvůli tomu, že někdo neumí C++ já určit nový programovací jazyk.
V C++ když už použijuju T*, tak to má jeden z dvou možných důvodů. Prvním důvodem je, že bych normálně použil referenci, která je bezpečnější, ale z nějakého důvodu potřebuju vyjádřit hodnotu "nic", takže k tomu použiju nullptr. Dalším důvodeom často bývají výkonnostní záležitosti, kde chytré pointery mohou zdržovat, ale i to už vytlačuju a spíš používám referenci na pointer.
Tenhle seriál čtu každý díl od od začátku a jen mě utvrzuje, že někteří jsou fakt blázni. Než aby se naučili pořádně jednu věc, raději si vytvoří novou, duplicitní věc. Proč? Je za tím snad nějaká snaha prožít si pět minut slávy? Nebo peníze? A nebo mě autor prostě nedokáže přesvědčit že Rust umí něco co C++ ne.
Od C++11 (unique_ptr, ...) se dá psát hodně robustně i v C++
Seriál po očku sleduji (Rust jinak neznám) a myslel jsem, že jeho výhoda je v tom, že kritické stavy ohlídá už v compile time. Třeba vždy ohlídá návratovou error hodnotu. Ale nějak se to tu pak zamotalo, že mi připadlo, že to také neplatí vždy (ale mohu se mýlit).
Jinak to asi je opravdu stylem psaní. I v C++ se dá psát opatrně a v Rust (jak je vidět) se dají také dělat prasárny. Jen, možná, Rust vede začátečníky k menším průšvihům (odhaduji).
unique_ptr v podstatě nic neřeší, chybu uděláš i tak velmi snadno:
auto a = std::make_unique<int>();
*a = 1;
a.reset();
// ....
*a = 2;
Tohle se bez jediného warningu přeloží a výsledkem je SIGSEGV. V Rustu taková chyba (bez použití unsafe) udělat nelze, chytí to překladač v compile time.
Heh, to je jak říkat, že Rust nic neřeší, protže i v Rust můžeš použít *const.
Jakmile musíš nezbytně použít Reset (natož bez parametru), znamená to, že máš něco špatně - proč pak používáš unique?
Říkal jsem, že i v C++ se už dá psát robustně. Ne, že Tě C++ vede za ručičku a že to každý hned umí. Prasárny povolí i nadále. Je na Tobě, zda použiješ try-except (máš, ale nenutí Tě compiler), stejně tak nezakáže Reset(), atd.
To je tak možná rozdíl mezi Rust a C++(novější). Že Tě Rust ohlídá, i když neumíš psát opatrný kód.
Ty jsi nikdy nedělal na žádném větším projektu, že? Ono je hezké argumentovat tím, že chybu neuděláš, protože to umíš. Máš tam ale dalších 50 programátorů, kteří nejsou tak dobří nebo programují trochu jiným stylem. Pak vznikne chyba velmi snadno.
Rust ti dává garance, které v C++ nikdy mít nebudeš. Hlídá podobné chyby v compile time. Nedostanou se ti do produkčního kódu. Pokud takové garance nechceš, tvoje věc, klidně si mysli, že je nepotřebuješ. Já tvůj názor nesdílím a chci, aby co nejvíc věcí hlídal překladač. Protože překladač je (prakticky) neomylný, člověk dělá chyby.
Dělal, dá se to i ohlídat, ale je to náročné. Např. zakázané funkce, konstrukce, ... (vesměs kontrolováno strojově - ale ne compilerem, ale bokem, pochopitelně)
Jenomže, právě tímto posledním dílem o Rust, mi začalo připadat, že i v Rust můžeš dělat prasárny a také je musíš hlídat bokem. Možná jsem nepochopil přesně (třeba to unsafe). Ono z jednoho seriálu těžko, to chce praxi. Ale to povinné unsafe je snad stále jen obdoba try-except, ale že *mut někde něco přepíše co nemá a zprasí data, tomu nezabrání. Jen, že nepadne do systému s výjimkou/signálem. Je to tak?
Problém je v tom, že C++ nejde spolehlivě zkontrolovat na memory safety ani překladačem, ani externími tooly. Co je to zakázaná konstrukce? Je to přiřažení do smart pointeru? Pokud ne, vymyslím 1000 různých variací, jak v C++ napsat chybný kód:
auto a = std::make_unique< std::vector<int> >();
a->push_back(1);
auto it = a->begin();
a = std::make_unique< std::vector<int> >();
// ...
std::cout << *it << std::endl;
Znovu opakuji, v Rustu taková chyba udělat nelze, chytí ji překladač.
"Zakázané konstrukce" je třeba užití std::vector a unique(C++11) dohromady, protože jsou nekompatibilní (vector je skoro o desítku let starší, tak má problémy). Vector máme vlastní (dokonce ještě z dob pre-vector C++, pochopitelně upravovaný právě kvůli novým věcem). Obdobně, další konstrukce, je-li třeba předat do funkce větší vstupní objem dat a nejde to hodnotou /kvůli té velikosti/, tak zásadně const reference, ani ne const pointer či dokonce jen pointer. A tak podobně.
Čím dál více si uvědomuji, že naše striktně vyžadované postupy jsou asi dost podobné Rust (či jiným low-level safe jazykům) a užívám je i jinde, obecně, že už si ani moc neuvědomuji, jak moc se dá v C++ prasit :-D Takže se omlouvám jak mám názory posunuté a divím se, proč na to třeba další jazyk. Profesionální slepota.
Teď nevím přesně, jestli rozumím, co chcete. Myslíte kód ekvivalentní s tímhle?
auto a = std::make_unique< std::vector<int> >();
a->push_back(1);
auto it = a->begin();
// ...
std::cout << *it << std::endl;
Pokud ano, tak naprosto přesný ekvivalent by mělo být následující (NB: nemám překladač po ruce, takže to nemůžu otestovat):
let a = Box::new(Vec::<isize>::new());
a.push(1);
let it = a.iter();
// ...
writeln!("{}", it.next().unwrap());
Žádné unsafe není potřeba. Ovšem tohle není zrovna idiomaticktý Rustovský kód. Nevím, co vlastně tento příklad má dělat ale v reálném programu by se to téměř určitě udělalo jinak.
Mě šlo o to, že iterátor v C++ se dá chápat jako generalizovaný ukazatel na prvky nějakého kontejneru. K jednomu kontejneru se iterátorů dá vyrobit víc, dají se prostřednictím nich měnit hodnoty prvků kontejneru, dají se předávat jako argumenty různým funkcím (algoritmům), atd. C++ má poměrně složitá pravidla, kdy dochází k invalidaci iterátorů - v tomto příkladě iterátor přestává být validní při destrukci původního vektoru způsobené druhým voláním a=std::make_unique. Rust pokus o takovou chybnou manipulaci s iterátorem zachytí už při překladu. Jenže tato kontrola pravděpodobně nepovolí i spoustu jiných případů, kdy v práci s iterátorem chyba není. Mě by zajímalo, jak často v praxi nastávají situace, že kompilátor Rustu nepovolí nějakou takovou konstrukci, která by v C++ byla v pořádku, a je nutné použít buď unsafe, nebo nějaký méně efektivní algoritmus.
Omezení v Rustu jsou, např. tento kód se nepřeloží, i když v něm není žádná chyba:
let mut vec = vec![1, 2];
let ref1 = &mut vec[0];
let ref2 = &mut vec[1];
Borrow checker neumí pracovat s jednotlivými prvky vektoru, ale jen s celým vektorem:
error[E0499]: cannot borrow `vec` as mutable more than once at a time 3 | let ref1 = &mut vec[0]; | --- first mutable borrow occurs here 4 | let ref2 = &mut vec[1]; | ^^^ second mutable borrow occurs here
Je otázka, jestli to prakticky něčemu vadí. Šlo by to vyřešit pomocí unsafe, ale v Rustu je lepší použít split_at_mut:
let mut vec = vec![1, 2];
let (a, b) = vec.split_at_mut(1);
let ref1 = &mut a[0];
let ref2 = &mut b[0];
Domnívám se, že si nepsal (a ani neviděl) kód který se vykonává před main(). Někdy je to docela dost kódu (a ani nemusí obsahovat jádro OS) a musí se dělat relativně šílené věci s HW aby to šlo vůbec používat. Vlastně je to jedna z věcí, která mě na tomto jazyku zatím chyběla (ale znám ho jen z těchto článků), protože někdy opravdu není jiná možnost jak si popovídat s HW.
Druhá věc, která mi chybí, je podpora architektur s jinou než x8 organizací paměti. Ne všechny mají nejmenší adresovatelnou hodnotu o velikosti 8 byte. Nemám někdo tušení jak a jestli (alespoň teoreticky) jsou podporovány architektury s velikostí charu (pomohu si C výrazem :-) 16, 32 bitů? Jak jsou tam řešeny typy jako i8?
zatim jsou podporovany klasicke 32bit a 64bit architektury + MSP430 (16bit MCU):
https://forge.rust-lang.org/platform-support.html
A predpokladam, ze i na DSPckach a tak by i8 bylo podporovano, neni duvod proc by to neslo
Při vší úctě váš příspěvek svědčí o tom, že problém není ve smyslu Rustu, ale v tom, do jaké míry tomu (ne)rozumíte. V C++ můžu mít unique_ptr, jak ale zaručím, že je ukazatel skutečně jediný? Nijak. Není nejmenší problém mít na tentýž objekt zároveň shared_ptr, takže paměť se mi kdykoli může uvolnit pod rukama. Především je ale úplně jisté, že kromě toho "unique" ukazatele se někde v programu k tomu samému objektu přistupuje taky jako T*. Nemusím snad mluvit o tom, že ukazatele i s celou slavnou STL (a i C++ reference) můžou klidně být NULL, ukazovat do nealokované paměti atd. To všechno jsou jenom berličky, které ale vůbec neřeší podstatu problému.
Obecně k vašemu dojmu ze seriálu se dají říct dvě věci. Věcí, které Rust umí a C++ ne je hodně, ale samozřejmě u takovýchto jednoduchých příkladů se neprojeví. Cokoli tady autor demonstruje se samozřejmě dá udělat i v C++ (nebo v assembleru). Teprve při práci na větším a složitějším projektu se dají ocenit věci jako právě ona rigoróznost a z ní vyplývající garance, algebraické typy, mimořádně pokročilý typový systém, relativně dobrá podpora funkcionálního programování (Rust rozhodně není pravý funkcionální jazyk, ale jde v tomto směru mnohem dále, než jakýkoli jiný mainstream jazyk), vynikající možnosti paralelizace, jaké nemá ani Go (natož C++!), metaprogramování, kterému se snad kromě D nevyrovná žádný jiný statický jazyk, integrovaná podpora testů, které se můžou týkat i dokumentace, nástroj cargo, atd... a to všechno s tím, že výsledný kód typicky běží stejnou rychlostí jako C - kdepak C++ - ale někdy i násobně vyšší, právě díky snadné a bezpečné paralelizaci.
Zadruhé je nutné mít na paměti, proč Rust vůbec existuje. Vyvíjí ho Mozilla spolu s projektem Servo a to na základě jejich dlouholetých zkušeností se správou velkého a složitého programu v C++, totiž Gecka. Rust je přímo navržen tak, aby se poučil z C++ a vyhnul se problémům a vadám typickým pro C++ projekty. Je to situace analogická s tím, jak Ada vznikla na základě neblahých zkušeností DoD s Fortranem. V žádném případě nikdo neříká, že je Rust "dokonalý", ale tvrdit, že nemá smysl, protože C++ atd... je v podstatě stejné, jako tvrdit, k čemu Wayland když máme X11.
Díky za obšírnější vysvětlení. Souhlas, že teprve v praxi bych asi uviděl, co už Rust dělá za nás a co už nemusíme kontrolovat bokem (byť automatizovaně, vesměs).
Jen pozor s tím unique_ptr ... pokud se užívá striktně jen nová logika (auto, unique, shared, make, ...) tak si to fakt ohlídá. Problém je, jakmile se to začne mixovat (get, reset, ...) .. .- asi jako tady to Rustovské *mut (chápu-li dobře).
Bez praxe v Rust asi těžko posoudím, jak moc jsou v něm (zne)užitelné ty obezličky (které obvykle být _musí_ i když člověk nechce, pracuje-li člověk s reálnými aplikacemi či více embedded) a zda se to nakonec nezvrtne na obdobu void* v C++ (přes který lze nakonec přetypovat skoro vše a nedá se ohlídat).
Jen pozor s tím unique_ptr ... pokud se užívá striktně jen nová logika (auto, unique, shared, make, ...) tak si to fakt ohlídá. Problém je, jakmile se to začne mixovat (get, reset, ...) .. .- asi jako tady to Rustovské *mut (chápu-li dobře).
Problém je, že si to neohlídá. Můžete mít unique_ptr nebo nějaký jiný ****ptr, ale hned třeba ifstream.read() má argumenty char* s, streamsize n, takže .get() se použít prostě musí (a navíc je tu možné buffer overflow jak kráva). U rustu je ekvivalentní as_ptr() unsafe kód a normálně se nepoužívá.
Hlavně ale se ale můžu důsledně vyhýbat všemu "nebezpečnému" a přesto to nebude fungovat a program mi bouchne. Příklad:
class foo {
private:
std::unique_ptr<int> &ptr;
public:
foo(std::unique_ptr<int> &p): ptr(p) {}
};
foo bar() {
std::unique_ptr<int> p(new int);
foo f(p);
return f;
}
Tohle by v Rustu překladač nedovolil. Je to zkrátka o tom, že v C++ je snaha (samozřejmě velmi žádoucí) řešit tyto a další problémy tak, že logika, která chybí překladači (neboť ten stále vychází z C a je to i jeden z požadavků), se doimplementovává pomocí knihoven, maker a šablon. To je velmi užitečné ale má to samozřejmě své meze, podobně jako byly generické třídy zpětně doimplementovány do Javy jako jakýsi syntaktický wrapper nad Object.
Jsem fanda Rustu ale zdůrazňuji, že to v žádném případě není žádný všelék. Mezi jeho největší slabiny patří to, že (mj. na rozdíl od C++) je stále v podstatě definovaný implementací a nikoli specifikací. Navíc mám dojem, že Rust pomalu ale jistě získává pověst složitého a obtížného jazyka, podobně jako třeba Ada, a to mu může do budoucna jedině uškodit. Ale jak už bylo řečeno, Rust vznikl jako snaha řešit problémy spojené s C++ a STL, v tomto smyslu je vlastně přímým konkurentem D, a tak není divu, že právě proti C++ má skoro jenom samé výhody.
Když se chcete důsledně vyhýbat všemu "nebezpečnému", tak nemůžete vyrobit třídu, která si pamatuje referenci na nějaký jiný objekt, tedy v podstatě obyčejný pointer.
Rust vznikl jako snaha řešit problémy spojené s C++ a STL, jenže C++ se od té doby dost posunulo (C++11 a novější) a ty samé problémy řeší (a z velké části vyřešilo) také.
Na druhou stranu i já, který si jinak dávám pozor, spoustu věcí řeším referencí (jednorázová volání funkcí, ...). U memberu objektu to bývá problematické (když už vyloženě nezbytné z důvodu náročnosti /embedded/, tak obvykle alespoň zakážeme copy constructory, ale i tak se dá objevit způsob, kde kontrola selže).
Je jasné, že v uvedeném případě je správné vyžadovat shared_ptr (a tam by se to ohlídalo už při deklaraci, v compile time) a ne pointerovou (referenční) logiku.
Ale beru, i díky zdejší diskuzi /@klokan argumentuje celkem rozumně a věcně/, že C++ dovolí nejen prasárny, ale může "chybovat" i tam, kde prostě použiji můj zažitý C styl (a ne novější C++11 +) a že mi to může ujet i když si dávám pozor. Rust to prostě ohlídá, za cenu omezení a za cenu overhead (memory i runtime). Ale, pokud bych jel v C++11+ striktně opatrně, podobný overhead by tam stejně být musel. Takže zbývá jen to omezení. Zda bychom pro low-level věci stejně neměli plný kód unsafe, i kvůli externím věcem i někdy kvůli nutnosti šetření (času i paměti).
Příklad výše vezme každý překladač C++11, což je důkaz, že se nic nevyrešilo. C++11 nanejvýš nabízí nástroje, jak si dát pozor a problémům se VYHNOUT, ale ne je REŠIT, a to je rozdíl. Když používám výhradně oficiální a doporučené jazykové konstrukce a přesto můžu mít kdykoli memory error, tak to není VYŘEŠENÝ problém. Například to totiž znamená, že každá funkce, třída atd. má své nevyřčené předpoklady a invarianty, které znamenají, že nějaká změna JINDE V PROGRAMU je kdykoli může rozbít. A i i kdyby byly nakrásně do posledního vyřčené a dokonce dokumentované (jaká utopie) tak překladač absolutně není schopný je kontrolovat.
Rust vám nikdo nevnucuje a pokud se vám C++11 líbí, klidně zůstaňte u něj, nikdo vám ho nevezme. Ale můžete mi věřit, že jazyky jako Rust nebo Ada nikdo nevyvíjí ani nepoužívá z blbosti nebo masochismu. Většinou jsou to právě lidé, kterým po mnoha letech debugování, ladění, obcházení problémů atd. jednou došla trpělivost.
Tak čirý základní Rust má v zásadě totéž, co Go, přesněji řečeno totéž, co GCCGo, neboť každý "spawn" (ekvivalent syntaxe "go" v Go) běží jako samostatné vlákno. To se liší od překladače gc, který používá model "M:N" a je to dáno tím, že u Rustu je požadavek, aby výsledný kód byl čistě nativní a nepotřeboval žádné runtime prostředí.
První rozdíl je v tom, že v Rustu typový systém hlídá předávání a sdílení objektů mezi jednotlivými vlákny a statická kontrola zaručí, že nedojde k data race condition (které jsou v Go možné).
Tím to ale zdaleka nekončí. Kombinace typového systému, silně funkcionálního nádechu jazyka a pokročilého metaprogramování umožnila vytvořit velmi zajímavé knihovny. Příkladem je framework Tokio a jeho knihovna "futures". Ta nabízí kompletní a velmi pokročilou nativní podporu asynchronních operací, včetně asynchronního volání funkcí, metod a IO, s tím, že správa pracovních vláken atd. je plně automatická. Zajímavým příkladem je jejich testovací program "minihttp". Zdroják je dobrým příkladem, jak elegantní a jednoduché to je a výkon mluví sám za sebe, i když samozřejmě benchmarky je vždycky nutné brát s rezervou. Důležité je i to, že statická kontrola, kterou se Rust vyznačuje, plně funguje i v rámci takovéhoto kódu, takže opět, žádné race condition, memory leaky apod.
Jiným zajímavým příkladem je knihovna Rayon, která sice umí různé věci, ale především obsahuje paralelní iterátory pro základní datové struktury: vektory, stromy atd. Správa vláken je opět automatická, takže u snadno paralelizovatelných problémů dokáže i naprostý začátečník s Rayonem napsat program, který bude třeba dvakrát nebo třikrát rychlejší, než normální kód v céčku.
Je ale nutné po pravdě říct, že Rust má taky jednu velkou nevýhodu. Nejen že na rozdíl od Go nebo třeba D není nijak zvlášť snadný pro programátory zvyklé na C/C++, on vlastně není snadný vůbec. Často se říká, že průměrný céčkař zvládne Go za čtyři dny. Rust naproti tomu předpokládá na jedné straně dobrou znalost C, na druhé stejně dobrou znalost Haskellu nebo některé verze ML, a k tomu navíc má svá vlastní specifika (typicky kontrolu životnosti a s ní související metaproměnné, ale nejen to), takže ty čtyři dny budou v nejlepším případě nejméně čtyři týdny.
Děkuji za podrobnou odpověď. Zrovna ty miniaturní go-rutiny mapované M:N na vlákna mi připadají jako zásadní výhoda go oproti ostatním jazykům. O bezpečnost a rychlost Rustu se přít nebudu, ač je za cenu pohodlí. Na uvedené knihovny se podívám.
S posledním odstavcem naprosto souhlasím, málokterý jazyk je tak neintuitivní. I Scala mi ve srovnání připadá v podstatě jednoduchá.
Pokud vím tak to sice není přímo ve specifikaci, ale autoři Go v zásadě počítají s implementací M:N a GCCGO v je tomto směru vlastně nekonformní. Je to problém, protože překladače pak nejsou zaměnitelné a každý projekt v Go, který používá gorutiny a chce dosáhnout slušného výkonu, musí předpokládat M:N nebo 1:1 a podle toho používat buď gc nebo GCCGO. Na druhou stranu má GCCGO mnohem lepší optimalizace, než gc a vůbec je pro Go dobré, že má dva nezávislé plnohodnotné překladače. V principu je ovšem Go určené především pro síťové a webové služby (aspoň to byl původní účel, i když dneska se ujalo i jinde) a tomu odpovídá i návrh gorutin, které jsou primárně zamýšlené jako zdokonalené korutiny a podpora pro určitou formu asynchronicity, spíš než jako prostředek na paralelizaci výpočtů.
proč je mi pořád předhazováno něco o nebezpečnosti toho jazyka
Protoze ten jazyk je ze sve podstaty nebezpecny, mist, kde jde udelat neco nebezpecneho, je tam spousta. To, ze existuje nejaka podmnozina jazyka, ktera je relativne bezpecna neni prilis uklidnujici. To je jako, kdybys mel vsude doma nechranene vodice. Ty prece dobre vis, kde ty draty jsou, kde to probiji, a kde se toho nemas dotykat. Prijde-li kdokoliv novy, muze to mit fatalni dusledky.
Než aby se naučili pořádně jednu věc
Co to znamena poradne. C++ ma nekolik vrstev, puvodni C, C++ z devadesatych let, moderni C++11. Co z toho se ma clovek naucit poradne? Kolik let to trva, nez se to nauci poradne a muze zacit programovat?
Je za tím snad nějaká snaha prožít si pět minut slávy? Nebo peníze? A nebo mě autor prostě nedokáže přesvědčit že Rust umí něco co C++ ne.
Osobne bych videl problem v tve genialite a neochote si pripustit, ze existuji i programatori, kteri delaji chyby. Rust podobne jako Go (nebo konec koncu i Java) byly navrzeny pragmaticky jako programovaci jazyky, ktere maji usnadnit programatorum praci, aniz by se museli prilis zatezovat technickymi detaily a mohli se soutredit na programovani samotne, za co jsou placeni. Kde to jde, prekladac by mel upozornit na potencialni problem, protoze to usetri x-hodin prace s hledanim chyby. Btw. nedavno jsem po dlouhe dobe neco programoval v C++ a uz jsem zapomnel, jak krasne dlouhe a "velmi" informativni hlasky prekladace dokazi vygenerovat.
Otazka ku kapitole 11:
V uvedenom priklade by malo byt pomerne jednoduche urcit, ze premenna na ktoru pointer ukazuje uz nie je platna (s pointerom sa nerobia ziadne operacie okrem priradenia adresy premennej). Je tak? Problem by to mohol asi byt, pokial sa pouzije pointerova aritmetika. Su aj ine pripady?
Dakujem.
Je to ještě nižší úroveň, než C wrapper, protože pracují přímo s alokátorem paměti. Například Vec v Rustu je jednak obdobou vec z C++, ale zároveň se běžně používá jako obyčejné malloc() v C. Takže unsafe blok je tu zcela legitimní a v podstatě je totéž, jako inline asm v libc a asm a nizkoúrovňové C v STL.
Navíc je nutné mít na paměti dvě věci. Některé z těchto unsafe bloků by se dneska daly odstranit, byly napsány v době, kdy jazyk a standardní knihovna ještě neměly všechny potřebné featury (vnitřně mutační struktury, metody split_****_mut() u Vec atd.) Kromě toho je (jako u všech jazyků) překladač a standardní knihovna záměrně psán v omezené podmnožině jazyka v zájmu usnadnění bootstrapu.
Z těchto důvodů je jasné, že posuzovat výskyt unsafe kódu podle překladače a knihovny nemá smysl. Mnohem zajímavější je aplikační kód. Za svou osobu mám za sebou pár malých ale relativně netriviálních projektů v Rustu (v řádu každý pár tisíc řádků) a unsafe jsem použil všeho všudy třikrát. Poprvé z vlastní blbosti, kdy jsem nevěděl, jak to udělat čistě (a když jsem se to dočetl, tak jsem kód opravil), podruhé abych "na místě" přetypoval vektor u8 na vektor m128, a potřetí jako wrapper nad libjpeg.