Vzhledem k tomu ze to vypadá jako kopie jazyka Erlang, dovolim jsi odpovědět.
Jedna se o defaultni větev. Když pravidla před tim nezaberou, tak udělej tohle.
Pokud takova větev v rozhodování není a nic nezabere, v Erlangu se vyvolá výjimka. Tady nevím.
Ty funkce co se s nimi muže pracovat jako s daty jako by z oka vypadly funs z Erlangu.
Však se v článku píše: "Ve skutečnosti překladač kontroluje, zda je _ použit, protože je vyžadováno, aby větve v konstrukci match pokryly všechny možné hodnoty testovaného výrazu"
a hned pod tím je ukázka. Takže to vypadá, že Rust si to kontroluje, Erlang ne (vůbec ne?) a C na to kašle a jen někdy vypíše warning, když se dobře nastaví přepínače. Těžko říct, co je nejlepší.
Rust kontroluje jestli jsou pokryty všechny možnosti a že všechny větve vrací stejný datový typ. Jde to docela ruku v ruce. Je to někdy docela otrava když vím, že nějaká možnost nemůže nastat (třeba x%2 je jen 0 nebo 1, ale match vyžaduje i vše ostatní). Tam se dá použít macro třeba unreachable!() nebo panic!() které obě mají univerzální návratovou hodnotu a schodí program pokud na ně dojde.
Co se '_' týče, je třeba to brát jako pattern. '_' nevytváří žádnou dočasnou proměnnou. Stejně tak by se pro vyčerpání všech možností dala proměnná pojmenovat (nebo všechny hodnoty pokrýt ručně). Patterny se pak vyskytují i u deklarací proměnných. Docela dobré když funkce vrací enum s několika hodnotami a já chci jen jednu. Nebo pro for cylkus když nijak nepotřebuju i.
Pokud vím, tak konkrétně Julia si nevede vůbec špatně a snaží se ty low level konstrukce optimalizovat, viz třeba http://julialang.org/benchmarks/
Tudíž by mě zajímalo, jestli to někomu přijde jako taková tragédie, aby lovil zrovna ve vodách Rustu, který je podle mě z hlediska zápisu pro tyto účely míň vhodný, než třeba Python+Cython, Julia nebo já nevím - třeba Nim.
> Pokud vím, tak konkrétně Julia si nevede vůbec špatně
Otázkou je, jak jsou optimalizace v Julii robustní - aby člověk nestrávil hodiny hledáním důvodů, proč se určitý kousek kódu nezoptimalizoval, jak měl - viz třeba http://zverovich.net/2016/05/13/giving-up-on-julia.html nebo https://www.reddit.com/r/Julia/comments/3x6tg9/why_is_this_loop_in_julia_slower_than_the_python/
Jasně, je to interpretovaný jazyk s JIT, jeho JIT je možná v něčem horší než JIT pro Python z odkazu, má pomalejší rozjezd než třeba Python, Julia je ve verzi 0.5 a na nedokonalost má nárok. Otázka je, co z toho komu vadí. Docela bych souhlasil s pohledem, který zde psali jiní - na prototypování (třeba) Python, na velkou aplikaci, která pravidelně chroustá spoustu dat, (třeba) Rust.
Nějaká nuimerické a algebraické knihovny tu jsou, ale je to dost fragmentované:
num - další numerické typy (bigint, complex, ...)
nalgebra - dobře optimalizovaná lineární algebra pro nízké dimenze
ndarray - N-dimenzionální matice a metody na nich
Letos na RustConf byli nějací chlapíci s machine learningem a docela shrnuli co od toho očekávat:
https://www.youtube.com/watch?v=lY10kTcM8ek
Není to moc pro prototypování protože to není dynamickty typovaný jazyk, není interpretovaný a nemá moc vizualizační nástroje. Ale je rychlý, dobře se paralelizuje, dobrý networking, error handling atd.
Kasli na rust a pouzivej jazyk D :D.
http://mir.rocks
http://blog.mir.dlang.io/glas/benchmark/openblas/2016/09/23/glas-gemm-benchmark.html
Muzou tam byt navic treba ruzne run-time testy, napr. meze poli, ukazatele, ...
Kdyz jsem kdysi cetl o vypujcenych ukazatelich v Rustu tak me treba pripadalo ze nektere veci budou muset testovat v run-time. Ale treba jsou chytri a vyresili to jinak... :-)
Potom podle semantiky jazyka nemusi nektere veci jit implementovat tak efektivne, treba dynamic dispatch. Nerikam ze tohle je pripad Rustu.
Dalsi co me napada je treba pattern matching. Kdybys to v C++ delal "rucne" tak tam treba vyuzijes nejakou znalost problemu a udelas to lip...
Nebo naopak růstu (třeba časem) vymyslí nějakou mikrooptimalizaci a udělá to lépe než vývojář v C++. Podobně jako dnes GCC někdy poráží vývojáře v assembleru (pokud nejsou extrémně zkušení), protože kompilátory jsou v některých mikrooptimalizacích dobré (resp. vývojáři kompilátorů jsou dobří) a optimalizovat to ručně by si vyžádalo tunu zkušeností a času.
"Muzou tam byt navic treba ruzne run-time testy, napr. meze poli, ukazatele, ..." " ... tak me treba pripadalo ze nektere veci budou muset testovat v run-time... " " ... Potom podle semantiky jazyka nemusi nektere veci jit implementovat tak efektivne,... " " ... Dalsi co me napada je treba pattern matching. ..."
Typická ukázka předčasné optimalizace založené na dojmech. Víc k těm nesmyslům asi nemá cenu dodávat.
a jeje, nekomu jsem narusil belief system, aby me ted nezacal kamenovat ;-)
Jinak to co jsem psal neni mysleno jako kritika Rustu, spis me slo o to ukazat ze kdyz 2 jazyky maji stejny backend tak to neznamena ze budou stejne efektivni... ;-)
Kdyz vezmu napr tu kontrolu mezi poli - vubec nevim jestli to rust dela nebo ne - ale jazyk ktery to dela nebude tak efektivni jako jazyk ktery to nedela, i kdyz bude mit stejny backend.
You have completely missed the point. Jde o princip, že stejný backend nemusí zajistit stejný výkon, ne o konkrétní případ polí.
Stejně tak Java, Groovy a Scala nad stejným JVM mohou mít jiný výkon, a to někdy dost zásadně. Ano, některé výkonové charakteristiky a optimalizace budou mít podobné, ale dá se – i mezi Javou a Scalou – najít spousta rozdilů, někdy i velkých.
Tak jsem udelal maly testik, aby to nebylo mlaceni prazdne slamy, jednoducha smycka, sum square pro i od 0 do milionu, rust vs c, rustc vs clang, bez optimalizace c o rad rychlejsi, s optimalizaci O3 taky c rychlejsi, ale nechce se mi hrabat v rust deklaracich, abych zjistil presnejsi rozdily pro O3. Jen tak pro info, size rust exe codu 240kB, size c exe codu 8kB.
Kdyz mi das mail, tak ti to poslu, rust dnes vidim asi tak po treti, takze jestli je to zcela identicke, to fakt nevim, vysledky jsou kazdopadne stejne, mozna deklarace promennych neni identicka. Kazdopadne zaklad pro rust jsem vzal v tomto clanku, napsat c code je easy. Kompilovano pod Fedorou 24, rustc i clang jsou defaultni verze z Fedory. V nejhorsim to sem muzu pastnout, zdrojaky jsou kratke.
zkus optimalizace, Rust schvalne by default neoptimalizuje vubec, viz FAQ:
Why is my program slow?
The Rust compiler doesn’t compile with optimizations unless asked to, as optimizations slow down compilation and are usually undesirable during development.:
Ale jinak z takto kratkeho programu se moc nezjisti, protoze 6 nebo 10 ms je tak nizka hodnota, ze se do toho nutne zapocita start runtime (to je problem vetsiny mikrobenchmarku). Zacal bych s necim slozitejsim, co psali lidi, kteri ten ktery jazyk znaji:
Moje testy:
Kod D:
import std.range: iota;
import std.stdio : writeln;
import std.algorithm : fold;
enum MAX = 1_000_000UL;
void main()
{
iota(0, MAX).fold!"a + b^^2".writeln;
}
D (ldc bez optimalizace)
13ms
D (ldc optimalizace)
3ms
C (clang bez optimalizace)
13ms
C (clang optimalizace)
2ms
rust (rustc bez optimalizace)
48ms
rust (rustc optimalizace)
5ms
Rust má podstatně větší standardní knihovnu než C a další věci co v C nejsou (např. panic! macro) a v základu linkuje staticky. Pokud opravdu potřebuješ minimální velikost, musíš si s tím vyhrát trochu víc (hledej bare metal rust). Jinak těžko říct cos tam proved, protože imho jenom smyčka by vyšla zhruba stejně rychlá i v Javě.
Co se velikosti binárka týče, https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html je pěkný čtení.
TL;DR: Rozebírá se tam velikost "Hello World" kde Rust má 650kB a C jen 8,5kB. Je tam něco o debugovacích symbolech (moc nevim co to je) - obsahuje jemalloc pro dynamické alokace ikdyž nejsou v programu, backtrace a jeho stringy ikdyž v programu není panic. Po odstranění dohodle je to na 113kB. Taky knihovny jsou linknutý staticky a ne dynamicky jako u C. Rust má nějaký závislosti na standardní C knihovně, linkuje je staticky, jakýmsi příkazem kompilátoru se dá linknout dynamicky a pak je Rust stejně velký jako C.
Debug symboly je možné odstranit příkazem "strip", stejně jako u jiných binárek, třeba u céčka přeloženého s přepínačem -g). Tím se velikost sníží na cca 360kB (v tom článku to zmiňují). Nevýhodou je, že při ladění přes gdb (lze volat i rust-gdb) nebudou jednoduše k dispozici názvy funkcí atd. gdb pouze lakonicky vypíše:
Reading symbols from hello_world...(no debugging symbols found)...done.
A i tak základní příkazy jako "break main" už nebudou funkční:
(gdb) break main
Function "main" not defined.
Jde to sice obejít, pro distribuci aplikace je možné použít externí symboly (distra to tak běžně dělají, protože většina lidí asi debug symboly nevyužije, prakticky jen pro ABRT, takže proč zaplňovat disk objemnými soubory), ale pro vývoj to asi nemá cenu řešit.
Tezko rict kam az ten optimalizator "vidi". "Moderni" C++ ma tu vyhodu, ze vsechno jsou jen sablony, ktere obsahuji pouze inline metody. Spojenim takovych sablon pak vznikne velice dlouhy kod, na kterem ze muze opimalizator vyradit. Dneska se ale daji podobnym zpusobem optimalizovat i non-inline funkce. Za urcitych okolnosti optimalizotor vi, ze po zavolani nejake jednoduche funkce zustanou registry nezmenene.
No, když Rust, C++, D a další budou kompilovat přes LLVM, pak se lišit moc nebudou a důležité pro volbu jazyka bude to, jak se v něm dělá. To je jednak syntax (každému vyhovuje jiná, někdo se nevzdá té céčkovské, někomu se líbí Rust) a pak (asi hlavně) práce s knihovnama.
A Rust je v tomhle dobrý (poučený z Javy, Pythonu, Nodejs, ....) a přes cargo nabízí snadnou práci s knihovnami (nevím proč tomu říkají crates).
V C/C++ už jsem nějaký rok nedělal, ale s knohovnama byl někdy problém. Hlavně s různými verzemi knihoven.
Taky mě trochu hlava nebere. Rust se srovnává s C, přitom třeba má vlastnosti hodně podobné C++, včetně všelijakých kontrol. Pramení to snad s dodnes opakované a nepravdivé Linusovi mantry o tom, že C++ se pro vývoj kernelu nehodí? Nikdy by nikdo nebyl schopen odpovědět na otázku proč.
BTW: C++ používám pro psaní software pro Arduino. Bez problémů (jen mi chybí výjimky, ale chápu, proč jsou problém)
No to nevim. Zacinal jsem na wiringu ale pak jsem zjistil, ze je to postaveny nad c++ takze jsem to zacal psat ve visual studiu naplno v c++ a wiring pouzil jako knihovnu k desce. Pouzivam sablony, alokace objektu (destruktory to vola), i virtualni funkce (akorat tam v knihovne chybi nejake funkce ktere se predpokladaji ze jsou v libc, da se obejit) ... a prekladac odmitne prelozit kod obsahujici throw.
Pokud to člověk chce apriori považovat za neplatné, tak stejně žádnému důvodu neuvěří. Podle mě je C++ dost strašný jazyk, který vynucuje nešikovný objektový model a přitom nenabízí nic směrem k odolnosti vůči chybám, spíš naopak. Oproti tomu je C jednoduchý a dobře standardizovaný jazyk, který lépe odpovídá tomu, co se děje ve výsledné binárce, lépe se v něm řeší binární i zdrojová kompatibilita, nevynucuje jeden nepříliš dobrý objektový model.
Ale jak říkám, člověk, který se jednou zahrabal v C++ a považuje to za nejlepší věc na světě tohle asi nepobere.
OLOL OLOL
Pane, napsal jsem ve standardním C hru. Pak jsem zkusil C++ a pak jsem se učil Javu a OOP a pak jsem se vrátil zpět k C++ a vzpoměl jsem si, jaká trápení jsem během psaní té hry zažil,jak jsem si vyvíjel systémy, které řešili
- stringy
- virtuální funkce
- lambda funkce (ono známé, předání pointeru na funkci a kontext)
- zapouzdření dat (AI nestvůr v té hře by se realizovala mnohem lépe pomocí objektů)
čisté C? Nikdi více!
A co jako? Mám přesně opačnou zkušenost. Jestli má někdo potřebu se po naučení Javy vracet k C++, tak to ten jeho kód musí být teda pěkná "lahůdka". Raději bych to snad ani nechtěl vidět, natož tam muset něco modifikovat. A soudě dle vašich různých názorů zde v diskusích mám z vás pocit, že přestože "jste napsal hru", tak bych si k vám pro rady ohledně technik programování a softwarového inženýrství rozhodně nechodil.
A to je podle me i odpoved na to, proc se rika, ze Rust ma ambici nahradit C. C++ je C s pridanou hodnotou (a hromadou balastu) a +- se v nem porad da psat normalni C. Rust se snazi predefinovat "nastupce C" s minimem balastu a znalostmi z okolniho sveta, ktere autori maji.
Myslim, ze Linus to vysvetloval tim, ze C++ skryva pred programatorem alokaci pameti, coz muze byt v kernelu kontraproduktivni. Napr. diky implicitnim datovym konverzim zavolate metodu a pred jejim faktickym zavolanim si vytvorite hromadu docasnych objektu na stacku.
C a C++ maji "problem" v tom ze nerozlisuji mezi poiterem na stack a pointerem na heap. Snadno muzete na heapu vytvorit instanci, ktera ukazuje na stack vaseho vlakna a vsechno bude krasne fungovat dokud vase vlakno neumre. Takove chyby, jsou jen velice tezko odhalitelne. (I kdyz v C++ se daji udelat smart pointery, ktere neco takoveho nedovoli)
Pokud je to pravda, tak Linus nezná C++, jinak by to netvrdil ... ale dost možná to spíš neřekl nebo to řekl jinak.
C++ nedělá žádné skryté alokace. Možná je dělá knihovna STL. Ta je sice součástí standardu, ale není povinnost ji používat. Objekty lze v C++ nadefinovat tak, že se chovají přesně tak, jak tvůrce vyžaduje, včetně toho, že má pod kontrolou přesný postup co se volá při vytvoření a při destrukci (což taky znamená, že se nemusí dít vůbec nic).
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj". Já z toho důvodu používám objekt, který nazývám StringView, který nic nealokuje. Mimochodem, něco podobného (jako std::string_view) se chystá do C++17. Ale to nebrání tvůrce kernelů si něco podobného vyvinout už od C++98.
Ano, co pro C++ chybí je knihovna pro programování kernelů..., kde by byly do objektů zabalené věci na míru implementované potřebám kernelu.
C++ nedělá žádné skryté alokace.
V originale Linus psal:
any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel.
Nemluvi tedy o skrytych alokacich, ale o jazycich, ktere rady skryvaji veci jako je alokace, coz ma trochu jiny vyznam. Staci se zamyslet nad tim, proc vznikaji vyssi jazyky. Je to prave proto, aby skryvaly jednotlive nudne casti programu a nezatezovaly tim programatora. Kdyz tedy C++ nic neskryva, k cemu je dobre?
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj".
Nejak na tom radku postradam explicitni alokaci pameti. Nebude to tim, ze ji C++ skryva?
Vidím tam viditelné volání konkrétní funkce (strdup) s konkrétními parametry. Takže, když už volám nějakou (cizí) funkci a nevím co dělá, tak šup do dokumentace. Třeba:
http://man7.org/linux/man-pages/man3/strdup.3.html
... a vida. Je tam jak informace i že volá malloc (a ne nic jiného, třeba new) a je tam i info o free.
Nikde není zamlčeno, že tam (existuje) "mezilehlý" návratový alokovaný prostor, není zamlčeno kde že se vytvoří (heap x stack x ...) a jaké funkce k tomu použil (malloc x new x static ...). A je jasně dáno (tedy, je to dáno na mne, programátora) kdy to místo bude uvolněno, respektive sám určím kdy uvolním.
Vím moc dobře, jak to tam je a vím (pracuji na low-level, embedded) a že tam s tím dokáži udělat pěkné prasárny. Stačí to toho zapojit pár online a optimalizaci a mírně se to zamotá děděním. Plus problém se systémy, kde např. konstanty (vč. statických stringů) mohou být chápány různě v závislosti na nastavení překladače (a pak jeho následné optimalizaci).
Navíc, sakra záleží na obou stranách přiřazení. Nejjednodušší pak bývá jít se podívat do assembleru, co že mi tam reálně udělal. Někdy to bývá docela překvápko a jsou to 3-4 instrukce a hotovo. Někdy (stačí drobná změna) je z toho alokace na stacku, volání konstruktoru a destruktoru hned za sebou (který nic nutného neudělá, žádné volatile) a pak přiřazení něčeho úplně mimo.
No, rozepisoval bych se moc, zkrátím to. Vracet objekt je věc, která může nadělat dost škody i ve chvíli, kdy člověk myslí, že zná dokonale, jak mu překladač na daný systém funguje.
... a to je to, čím může být c++ nebezpečné. Jak některé věci může skrývat před očima programátora, tedy dělat implicitně i alokace na stacku (minimálně), vlastně jen dle uvážení (doslova, někdy ano někdy ne, dle optimalizace). Je to fajn, dokud funguje a je dost místa a runtime času a nesnaží se optimalizovat - pak obvykle nezkrachuje. Obvykle. Ale jakmile se zajde do hloubky ...
Mel jsem na mysli neco takoveho:
#include <stdio.h> class Foo { public: Foo(int x) {}; }; class Bar { public: Bar(Foo const &_f) : f(_f) {}; Foo const& f; void print() { const void *p = &f; printf(" pointer: %p \n", p); } }; int main() { Bar *pf1 = new Bar(Foo(42)); Bar *pf2 = new Bar(42); pf1->print(); pf2->print(); return 0; }
Zavolam "new Bar(42);" a vubec si nevsimnu, se vlastne vykona "new Bar(Foo(42));" a mam na heapu instanci, ktera ukazuje na muj zasobnik.
PS: uz jsem v C++ dlouho nedelal, takze jestli je to blbost, tak me opravte.
Popravdě jsem si myslel, že s deklarací konstruktoru `Bar(Foo const &_f)` se kvůli ampersandu výraz `new Bar(Foo(42))` (popř. `new Bar(42)`) ani nezkompiluje, protože to není left-side-expression. Ale ono se to fakt zkompiluje: http://codepad.org/5Bc1s0Az
Zřejmě se to skutečně alokuje na stacku.
Na svou obranu dodám, že C++ mě neživí a beru to spíše jako součást všeobecného rozhledu.
Nikdo netvrdi ze C++ je bezpecny jazyk ale lze v nem psat bezpecne. Vse je o lidech a o stabni kulture. Tak napriklad vyse uvedeny priklad se prelozi, protoze const T & se da aplikovat i na docasne objekty a nepredpoklada se, ze se budou drzet jako const reference, jejich platnost by mela byt zajistena do navratu z funkce (constructoru). Pokud uz to ale nekdo takhle napise (i ja to tak nekdy delam) tak se tam dava klicove slovo explicit, cimz se zabrani automaticke konverzi (ale nezabrani to kdyz to nekdo udela manualne, holt je to v lidech)
Proto by konstruktor s jedním argumentem (když to není copy/move) měl být deklarovaný jako explicit, pokud opravdu nechci definovat typovou konverzi. Ale úplně stejně špatně je i new Bar(Foo(42)) v inicializaci pf1. Když si třída Bar drží referenci na argument konstruktoru, tak by tam asi měla být jako pojistka ještě jedna deklarace konstruktoru: Bar(Foo &&_f) = delete
pouziti explicit, nebo vymazani rvalue copy constructoru resi ten problem jenom castecne.
"Vychytraly" programator muze snadno udelat treba:
Foo f;
Bar *b = new Bar(f);
Hlavni problem je ze kdyz si nekde ukladam ukazatel na objekt, tak ten objekt by nemel mit kratsi zivotnost nez ten ukazatel.
V dokumentaci ke tride bar by melo byt jasne napsane - uklada si ukazatel na parametr, je potreba zajistit zivotnost... a programator by se tim mel ridit :-)
Např. v libc++ std::string x = "ahoj" nic navíc nealokuje, protože krátké řetězce (do cca 20 znaků) se vejdou přímo do objektu std::string. Navíc pro třídy z STL je možné definovat vlastní alokátory a tím libovolně změnit způsob, jak se dynamická paměť alokuje a dealokuje.
I v kódu kernelů psaných v C se spousta věcí skrývá ve funkcích, v horším případě v makrech, v nejhorším případě v kódu generovaném pomocí ad-hoc skriptů při buildu. Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější. Nebo naopak programátor musí vědět, že má zavolat přesně definovanou posloupnost operací. A když to udělá, tak musí přidat do každé následující větve kódu jiné operace. Typicky takto vypadá použití zámků. Třídy a RAII v C++ v takových situacích poskytují lepší řešení.
Ja si treba myslim, ze C je na kernel lepsi z hlediska code-review a kontroly spravnosti kodu. C je 'jednodussi' jazyk - clovek si vystaci s par klicovymi slovy nekolika malo pravidly a jedna z nejslozitejsich veci je priorita operatoru. Naproti tomu v C++ clovek musi umet ruzne konstruktory, musi vedet ze jeden zapis (treba Foo a = b) muze volat ruzne konstruktory, v zavisloti na typu vyrazu b a tak podobne. Za mne je C++ sice casto lepsi nez C, ale je to bastl, takze na to aby clovek mohl _cist_ program se toho musi naucit mnohem vic nez v pripade C. Jine jazyky vyssi urovne se tomu vetsinou vyhly, protoze mely vyrazne konzistentnejsi vyvoj (ale existuji vyjimky jako treba PHP, to je imho jeste horsi bastl).
Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější.
To je hezke pri normalnim vyvoji, v pripade jadra to muze byt zasadni nevyhoda, protoze na jednu stranu ziskate sice srozumitelnejsi kod, ale zase ztracite predstavu o tom, jak dany kod bude vypadat po prelozeni do assembleru/strojoveho kodu.