Portál AbcLinuxu, 9. května 2025 00:31
Vypadá to dobře.
BTW: Kdy se tak asi dá očekávat, že bude v distribucích jako standardní balíček a nebude se muset instalovat přes curl … | sh
?
Jenže dokud je to jen v komunitních repozitářích, tak je to spíš taková hračka, nic pro produkční nasazení nebo seriózní vývoj. S takovou Javou 8 jsem taky čekal, až bude aspoň v Ubuntu – nebudu nutit uživatele, aby si do systému tahali něco, co není v oficiální distribuci – takže do té doby jsem si s 8 hrál jen u sebe a učil se ji používat.
Aha, tak to je dobrá zpráva. Teď ještě Debian, Ubuntu, Fedora a OpenSUSE
(a Guix a další distribuce, aby se to někoho nedotklo, že jsem ho nejmenoval)
Jedná se o systémový jazyk, jehož specialitou je konkurentní programování bez data raců.Jde hlavně o bezpečný memory management bez GC* založený na vlastnictví, ta bezpečnost vícevláknového programování je toho důsledek. Jinak po vydání 1.0 schytal Rust poměrně dost kritiky/skepse, zejména ze strany těch, co Rust do té doby ignorovali. V některých případech i oprávněně, například pomalá kompilace nebo nižší výkon než C (Rust je aktuálně 1-3× pomalejší, podle případu). *) GC v budoucnu zřejmě přibude jako volitelná komponenta zkrze další typ pointeru.
GC v budoucnu zřejmě přibude jako volitelná komponenta zkrze další typ pointeru.Jako knihovna nebo to bude plně integrováno do jazyka? Dříve to bylo součástí jazyka, ale v roce 2013 to bylo odstraněno. Smutné je, že Rust dnes nebrání memory leakům – GC by pomohl.
Škoda, že zprávička vyšla až včeraNjn, já jsem zprávičku napsal v pátek.
Jako knihovna nebo to bude plně integrováno do jazyka?Pokud vim, tak zatím neexistuje konkrétní plán. Ale pochybuju, že by se vrátil ten dřívější stav.
Smutné je, že Rust dnes nebrání memory leakům – GC by pomohl.Tím asi narážíš na ten problém s Rc/ReffCell objevený u JoinGuradu. Memory leak je nepříjemný efekt, ale problém je hlavně v tom, že se nezavolají destruktory ve správnou chvíli. S tím GC imho nepomůže - to JoinGuard API nenapíšeš pomocí GC. Viděl jsem nějakej návrh, kde to bylo vyřešeno pomocí lambdy, ale v stdlib je pořád to starý API s varováním.
Njn, já jsem zprávičku napsal v pátek.Jj, koukal jsem, že je ve frontě. Asi neměl čas ten, kdo to schvaluje...
Tím asi narážíš na ten problém s Rc/ReffCell objevený u JoinGuradu.Mám na mysli kód, který vytvoří referenční cyklus, s tím GC pomůže. Například:
use std::cell::RefCell; use std::rc::Rc; struct Pokus { jiny: RefCell<Option<Rc<Pokus>>>, } impl Drop for Pokus { fn drop(&mut self) { println!("Zaviram"); } } fn main() { let p: Rc<Pokus> = Rc::new(Pokus { jiny: RefCell::new(None) }); let mut jiny = p.jiny.borrow_mut(); *jiny = Some(p.clone()); }
Mám na mysli kód, který vytvoří referenční cyklus, s tím GC pomůže.Někdy ano, někdy ne, to záleží na tom, jaký prostředky spravuješ tím objektem / jaký máš požadavky na destruktor. S GC typicky nevíš, kdy se uvolní jaký objekt a v jakém pořadí, což nemusí být přijatelné. Pokud bys v tom příkladě spravoval
Pokus
garbage collectorem, nevíš, kdy se zavolá ten println!()
. Bude v té době ještě vůbec možné zapisovat na výstup?
func withOpenFileDo[T](filename: String, action: File -> T): T { val file = fopen(filename) try { action(file) } finally { close(file) } } val contentsOfFile = withOpenFileDo("foo.txt", file -> file.readAll)RAII je tedy poměrně slabý koncept, který se dá triviálně nahradit funkcemi. GC je podstatně silnější koncept, který není možno RAII plně nahradit.
GC by nemělo sloužit ke správě resourců.Souhlas. Pointu zbytku příspěvku nechápu se přiznám
auto withOpenFileDo(alias action)(strinf filename) { auto file = File(filename); scope(exit) file.close(); // tady si delej co chces return action(file); } ...
stream
, případně istream
nebo ostream
, takže už mi to ani nepřijde.
with open(filename) as stream: do_something_with_stream(stream) with open(filename) as stream: for line in stream: do_something_with_line(line)
File(filename).doSomethingWithLine(); File(filename).byLine.doSomethingWithLine();
Co třeba tohle?
Files.lines(Paths.get("/etc/passwd")) .filter(s -> s.endsWith("/bin/bash")) .map(s -> s.split(":", 2)[0]) .forEach(System.out::println);
(Java)
try (Stream<String> řádky = Files.lines(Paths.get("/etc/passwd"))) { řádky .filter(s -> s.endsWith("/bin/bash")) .map(s -> s.split(":", 2)[0]) .map(Java8::throwsException) // nějaká metoda, která vyhodí výjimku .onClose(() -> { // můžeme si přidat vlastní kód, který se provede při zavírání zdroje // (vedle standardního zavření souboru v tomto případě) System.out.println("onClose..."); }) .forEach(System.out::println); } // nakonec se automaticky zavře soubor /etc/passwd
Tak schválně napiš kód se stejným výstupem třeba v tom Pythonu.
.onClose()
v kódu výše než bude vyhozena výjimka? (V Rustu by to tak bylo (resp. tam by se místo vyjímky zpanikařilo), ale jak fungujou ty iterace u Javy, nevim...).
Jinak pokud vim, tak standardní knihovny C++ a Rust ekvivalent .onClose()
nemají, nicméně bylo by triviální tu metodu dodělat nebo udělat totéž jinak, tohle už není až tak o fíčurách jazyka...
Když ta Java8::throwsException
1 vyhodí výjimku, tak se zavolá jednak standardní close()
toho proudu (v tomto případě to zavře soubor) a jednak tebou definovaná onClose
funkce. Výjimka (a na ni navázané případné potlačené výjimky) se pak vyhodí výš, protože tam nemáš blok catch
.
[1] to je moje statická metoda v mojí třídě Java8, nic ze standardní knihovny… asi jsem to měl radši pojmenovat česky, aby to bylo vidět na první pohled
Když taTo by znamenalo, že to Stream API je podobně lazy jako iterátory v Rustu a vyhodnocuje až když se v tom řetězci filtrů objeví nějaký `consumer`. Pokud to tak není, tzn. pokud seJava8::throwsException
1 vyhodí výjimku, tak se zavolá jednak standardníclose()
toho proudu (v tomto případě to zavře soubor) a jednak tebou definovanáonClose
funkce.
map()
et al. vyhodnocují hned, pak bude ta výjimka vyhozena ještě před zavoláním .onClose()
, tudíž tan onClose()
lambda by se nezavolala.
Představ si to prostě jako anonymní vnitřní třídy (jen zapsané jinou syntaxí). Mají prázdný konstruktor a při vytváření jejich instancí nemůže dojít k chybě. Máš tedy vytvořené instance těch filtrů, mapovačů i onClose
operace. A až potom se to celé uvede do pohybu, proud se začne postupně zpracovávat a až tady se může případně objevit nějaká výjimka.
BTW: tohle je právě princip proudů, že je zpracováváš proudově – průběžně – a ne jako kolekce/pole, které bys celé prošel v jednom cyklu (třeba mapování) a až pak jako celek předal do dalšího cyklu (třeba filtrování) a nakonec jako celek předal všechny prvky do cyklu, ve kterém se zpracuje výstup.
for line in BufReader::new(File::open("/etc/passwd").unwrap()).lines()
{
let str = line.as_ref().unwrap();
if str.ends_with("/bin/bash") { println!("{}", str.split(':').next().unwrap()); }
}
Sans error-handling, který by se v praxi doplnil, čímž by kód vzrostl, ale byl by korektnější.
JJ v D je to az na drobne syntax rozdily uplne stejne:
File("/etc/passwd").byLine .filter!((s)=> s.endsWith("/bin/bash")) .map!((s)=>s.splitter(":").front) .each!writeln;
A samozrejme je to rychlejsi jak Java :) asi 1.7x
Tak to by se dalo rict potom i o Jave :D.
Btw, jak se neco podobneho napise v rust. Ja se o to pokusil bohuzel jsme nenasel ani zpusob (funkcni) jak precist soubor radek po radku natozpak neco slozitejsiho.
Tak to by se dalo rict potom i o Jave :DJava má svoje VM.
Btw, jak se neco podobneho napise v rust.
use std::fs::File;
use std::io::{BufReader, BufRead};
fn main()
{
BufReader::new(File::open("/etc/passwd").unwrap()).lines()
.filter(|l| l.as_ref().unwrap().ends_with("/bin/bash"))
.map(|l| println!("{}", l.as_ref().unwrap().split(':').next().unwrap()))
.last();
}
Rust nemá exceptions, proto tam jsou všade ty unwraps.
Tak v D to jde, ale nevim co bych tim ziskal, me to takto prijde mnohem vic fajn a efektivni
Tak to je asi o zvyku, ja drive taky moc tento zapis nemusel, ale nakonec mi prijde prehlednejsi.
open( '/etc/passwd' ).lines ==> map { ( < user shell > Z=> .split(':').[0,*-1] ).hash.item }\ ==> grep *.< shell > eq '/bin/bash' ==> map *.< user >.say ;a s
for
cyklem
for open( '/etc/passwd' ).lines -> $passwd_line { my ( $user, $shell ) = ( split ':', $passwd_line )[ 0, *-1 ]; if $shell eq '/bin/bash' { say $user } };tak si vyber :)
Tak to jde snad všude, ne?
Klasicky bys to zapsal třeba takhle:
try (Scanner sc = new Scanner(new File("/etc/passwd"))) { while (sc.hasNextLine()) { String řádka = sc.nextLine(); if (řádka.endsWith(":/bin/bash")) { System.out.println("Scanner: " + řádka.split(":", 2)[0]); } } }
Není to moc velký rozdíl, pokud se ty funkce píší přímo do kódu v místě, kde se používají. Ale výhodu vidím v tom, že tu funkci můžeš předat zvenku jako parametr dané metody. Případně v tom, že ty proudy se dají zpracovávat paralelně.
Není to moc velký rozdíl, pokud se ty funkce píší přímo do kódu v místě, kde se používají. Ale výhodu vidím v tom, že tu funkci můžeš předat zvenku jako parametr dané metody.To můžeš s for cyklem taky.
For each může být taky, potřebuješ něco, co implementuje rozhraní Iterable
. Případně jakoukoli kolekci nebo pole, ale pak by se to nezpracovávalo proudově.
Diky ja se o to snazil sam ale nejak jsem se ztratil v tech unwrapech a nevedel jsem kde mam pouzit spravne to as_ref :).
filter_map()
, která jak filtruje, tak mapuje.
proc ne?
do_something_*()
je obecná operace, takže ten model je popsaný zcela chybně, ale výše pod ukázkou Javy už je to napraveno, takže není, co řešit.
jo jenze to prave v D lze udelat (UFCS), to ze je do_something obecna operace nicemu nevadi
Ano i ne :). Zjednodusene plati toto. Pokud pouziji teckovou notaci tak se podivam zda dany clen ci metoda existuje na danem objektu. Pokud ano tak ji zavolam. Pokud neexistuje tak se podivam po volne funkci se stejnym nazvem jako ta volana metoda a pokud existuje a jeji prvni parametr je typu meho objektu tak zkusim zavolat ji.
Prvni rada nez se zamyslim je pouzivej uz hotove veci, ktere jsou bud uz ve phobos a nebo pouzij libasync z code.dlang.org. Jinak uplme presne nechapu co potrebujes, ale podle me si nemusis drzet referenci na jednotliva vlakna, ale pouzivej modul std.concurrency na posilani si zprav pomoci Tid.
import core.atomic; shared static ushort i; string* failer = null; m_instanceId = i; // race-condition mezi tímto static if (!EPOLL) g_threadId = new size_t(cast(size_t)m_instanceId); core.atomic.atomicOp!"+="(i, cast(ushort) 1); // a tímto m_evLoop = evl;Pak tvrdí, že je to nothrow. Ale není. V případě debug buildů asserty hází výjimky. V dalších částech, aby byl nothrow, tak výjimky chytá (a né jen Exception, ale Throwable, což by neměl) a ve spoustě případech ji prostě ignoruje a nijak nehlásí chybu. V dalších místech, když dojde k NEFATÁLNÍ runtime chybě, tak prostě hodí assert. (jinak já asserty v D nemám vůbec rád, chcípne jen aktuální vlákno, kdy by bylo lepší nechat chcípnout celý program, protože assert = fatální programátorova chyba, nikoliv ošetřitelná runtime chyba. Potřebuju nějak pořešit svůj Poller pro můj EventLoop, ten epoll, to je hrozný. Přese nebudu volat epoll_wait() na vyzvedávání po jednom eventu, abych předcházel race-condition mezi add a del. Koukni na to. Není to hotový, takže je tam spousta věcí, co by šla lépe
module jardik.eventloop.poller; private import jardik.util.abortif; private import core.stdc.errno; private import core.thread; private import std.exception : ErrnoException; version(linux) { version = POLLER_USE_EPOLL; //version = POLLER_USE_POLL; } else { version = POLLER_USE_POLL; } version(POLLER_USE_EPOLL) { private import core.sys.linux.epoll; // FIXME: this needs fixing up, its haz race condition // when you remove fd, which hasn't been dispatched yet // and then add it again, it will be dispatched with old // ready events ... even worse is that the fd can be totaly // different file public final class Poller { public alias Callback = void delegate(int fd, uint events); public enum : uint { POLLIN = .EPOLLIN, POLLOUT = .EPOLLOUT, POLLPRI = .EPOLLPRI, POLLERR = .EPOLLERR, POLLHUP = .EPOLLHUP, } epoll_event[] events_; // FIXME: indexed by file descriptor value, find better way Callback[] callbacks_; int fd_; uint currentEvent_; uint numFDs_; public this() { currentEvent_ = uint.max; fd_ = epoll_create1(EPOLL_CLOEXEC); if (fd_ == -1) { throw new ErrnoException("Poller: epoll_create1() failed"); } //events_ = new epoll_event[16]; events_ = new epoll_event[1]; // FIXME: make bigger after race condition fixed callbacks_ = new Callback[16]; } void poll(int timeoutMillis) { version(assert) { abortIf(fd_ == -1, "Poller: close() already called"); abortIf(timeoutMillis < 0, "negative timeout"); } // we ensure that events_.length won't go above int.max int ret = epoll_wait(fd_, events_.ptr, cast(int)events_.length, timeoutMillis); if (ret < 0) { int errnoSave = errno; if (errnoSave != EINTR) throw new ErrnoException("Poller: poll() failed"); } else if (ret != 0) { currentEvent_ = ret - 1; } } public bool dispatch() { version (assert) { abortIf(fd_ == -1, "Poller: close() already called"); } bool dispatchedSome = false; // NOTE: current_fd_ can change durring event dispatch in case of recursion for (uint currentEvent = currentEvent_; currentEvent != uint.max; currentEvent = currentEvent_) { const epoll_event ev = events_[currentEvent]; currentEvent_ = currentEvent - 1; // underflows on 0 to uint.max, which is desired // dispatch Callback cb = callbacks_[ev.data.fd]; if (cb !is null) { dispatchedSome = true; cb(ev.data.fd, ev.events); } } return dispatchedSome; } public void add(int fd, uint events, Callback cb) { version(assert) { abortIf(fd_ == -1, "Poller: close() already called"); abortIf(cb is null, "Callback is null"); } prepareAdd(fd); int ret, errnoSave; epoll_event ev; ev.events = events; ev.data.fd = fd; // Add the descriptor for (;;) { ret = epoll_ctl(fd_, EPOLL_CTL_ADD, fd, &ev); if (ret == 0) break; errnoSave = errno; if (errnoSave != EINTR) { throw new ErrnoException("Poller: epoll_ctl(EPOLL_CTL_ADD) failed"); } } // Store callback callbacks_[fd] = cb; // Add to count ++numFDs_; } public void remove(int fd) { version(assert) { abortIf(fd_ == -1, "Poller: close() already called"); abortIf(fd < 0, "Invalid file descriptor"); abortIf(callbacks_.length <= cast(uint)fd, "Descriptor not found"); } int ret, errnoSave; for (;;) { ret = epoll_ctl(fd_, EPOLL_CTL_DEL, fd, null); if (ret == 0) break; errnoSave = errno; if (errnoSave != EINTR) { // Don't treat ENOENT as error iff the descriptor // was "autoremoved" from epoll (happens on its last close()) if (errnoSave == ENOENT && callbacks_[fd] !is null) break; throw new ErrnoException("Poller: epoll_ctl(EPOLL_CTL_DEL) failed"); } } callbacks_[fd] = null; --numFDs_; } private void prepareAdd(int fd) { version(assert) { abortIf(fd < 0, "Invalid file descriptor"); } // Grow callback array if needed size_t cblen = callbacks_.length; if (cblen <= cast(uint)fd) { // this doesn't run often so lets keep it simple do { cblen *= 2; } while (cblen <= cast(uint)fd); // maximum value of valid file descriptor is int.max // so we never need to alocate more than (int.max + 1) elements if (cblen > cast(uint)int.max + 1) cblen = cast(uint)int.max + 1; callbacks_.length = cblen; } } public @property uint numFDs() const { return numFDs_; } public @property bool dispatchIsPending() const { return currentEvent_ != uint.max; } public void close() { // close epoll descriptor import core.sys.posix.unistd : system_close = close; int ret; do { ret = system_close(fd_); } while (ret == -1 && errno == EINTR); // set to invalid state fd_ = -1; events_ = null; callbacks_ = null; numFDs_ = 0; currentEvent_ = uint.max; } } } // version(POLLER_USE_EPOLL) version(POLLER_USE_POLL) { private import core.sys.posix.poll; public class Poller { public alias Callback = void delegate(int fd, uint events); public enum : uint { POLLIN = .POLLIN, POLLOUT = .POLLOUT, POLLPRI = .POLLPRI, POLLERR = .POLLERR, POLLHUP = .POLLHUP, } pollfd[] fds_; Callback[] callbacks_; uint numFDs_; uint currentFD_; public this() { currentFD_ = uint.max; } void poll(int timeoutMillis) { version(assert) { abortIf(timeoutMillis < 0, "negative timeout"); } uint numFDs = numFDs_; int ret; if (numFDs == 0) { // nothing to poll, just sleep Thread.sleep(dur!"msecs"(timeoutMillis)); ret = 0; } else { ret = core.sys.posix.poll.poll(fds_.ptr, numFDs, timeoutMillis); } if (ret < 0) { int errnoSave = errno; if (errnoSave != EINTR) throw new ErrnoException("Poller: poll() failed"); } else if (ret != 0) { currentFD_ = numFDs_-1; } } public bool dispatch() { bool dispatchedSome = false; // NOTE: current_fd_ can change durring event dispatch for (uint currentFD = currentFD_; currentFD != uint.max; currentFD = currentFD_) { const pollfd pfd = fds_[currentFD]; fds_[currentFD].revents = 0; currentFD_ = currentFD - 1; // underflows on 0 to uint.max, which is desired // dispatch if (pfd.revents != 0) { Callback cb = callbacks_[currentFD]; dispatchedSome = true; cb(pfd.fd, pfd.revents); } } return dispatchedSome; } public void add(int fd, uint events, Callback cb) { version(assert) { abortIf(cb is null, "Callback is null"); abortIf(fd < 0, "Invalid file descriptor"); } pollfd pfd = {fd, cast(short)events, 0}; uint numFDs = numFDs_; prepareAdd(); fds_[numFDs] = pfd; callbacks_[numFDs] = cb; numFDs_ = numFDs+1; } // fuck yeah, cool O(N) search!! public void remove(int fd) { uint fdIndex = findFD(fd); version(assert) { abortIf(fdIndex == uint.max, "file descriptor not found"); } uint lastIndex = numFDs_-1; uint currentIndex = currentFD_; if (currentIndex <= lastIndex && fdIndex <= currentIndex) { fds_[fdIndex] = fds_[currentIndex]; callbacks_[fdIndex] = callbacks_[currentIndex]; fdIndex = currentIndex; currentFD_ = currentIndex-1; } fds_[fdIndex] = fds_[lastIndex]; callbacks_[fdIndex] = callbacks_[lastIndex]; fds_[lastIndex] = pollfd.init; callbacks_[lastIndex] = Callback.init; --numFDs_; } public @property uint numFDs() const { return numFDs_; } public @property bool dispatchIsPending() const { return currentFD_ != uint.max; } public void close() { fds_ = null; callbacks_ = null; currentFD_ = uint.max; numFDs_ = 0; } private uint findFD(int fd) @trusted { version(assert) { abortIf(fd < 0, "Invalid file descriptor"); } uint numFDs = numFDs_; pollfd* pfds = fds_.ptr; for (uint i = 0; i < numFDs; ++i) { if (pfds[i].fd == fd) return i; } return uint.max; } private void prepareAdd() { size_t capacity = callbacks_.length; if (numFDs_ == capacity) { version(assert) { abortIf(capacity == cast(uint)int.max + 1, "The fuck ... where did you get so many descriptors??!"); } if (capacity == 0) capacity = 4; else capacity *= 2; // can't have more than int.max+1 descriptors if (capacity > cast(uint)int.max + 1) capacity = cast(uint)int.max + 1; fds_.length = capacity; callbacks_.length = capacity; } } } }
nothrow @trusted @safe pure override inout
a chce se mi zvracet. A pak stejně zjistíte, že fce nothrow není, protože je tam assert().
tak to nothrow rika ze funkce nevyhazuje zadnou vyjimku tridy Exception (pripadne od ni odvozenou). Coz ale pro assert neplati, ten vyhazuje objekt typu AssertError a ten je odvozen od objektu object.Error a ten zase od object.Throwable. Takze je to v poradku.
jinak ano je skoda ze to nepojmenovali spis noexception, to nothrow je dost zavadejici
Jinak na D je hrozná hlavně standardní knihovna,Celkove implementace. Prosel jsem od 2003 pres D1 na D2, prezil kockoviny se standardni knihovnou a zabugovane kompilatory, a stale to neni moc pouzitelne. Co realne sleduji tvurci jazyka D uz netusim, protoze to vypada jak pejsek a kocicka v kuchyni v kombinaci s Patem a Matem.
Tak popravde pouzitelne uz te dnes je, ja osobne uz nenarazim na moc problemu. Spis by to ale chtelo vidat nejakou stabilni verzi. A idealne standardni knuhovnu psat tak aby byla stale kompatibilni stou stabilni verzi kompilatoru. Me dokaze obcas fakt vytocit kdy clovek hleda napriklad regresi a neni schopen bisectem ji najit, protoze druntime i phobos jsou tak provazany s kompilatorem ze napriklad jen par komitu novejsi verze phobos uz nejde skompilovat a opacne.
V C mě štve absence výjimekTo se da castecne osklive resit pomoci setjmp() a longjmp().
nutnost kontrolovat návratové chybové hodnotyPokud se vyjimky pouzivaji pro fatalni selhani a ne pro control flow, jste v podobne situaci i s nimi.
půl megovou binárku pro hello world, se mi taky nezdáWell, soucasna implementace je ma poned rough edges.
JJ presne, ja osobne vyjimky moc nemusim. Driv jsem je mel rad a videl jsem na nich jen to dobre. Dnes uz jsem zkusenejsi a vidim i ty problemy.
Tak me na vyjimkach vadi zejmena to ze se pak spatne propojuji s jinymi jazyky. Jako ve sve aplikaci je klidne pouzivam. Ale u knihovny to je obcas za trest. Clovek kdyz pak dela treba binding pro jiny jazyk, tak ma docela problem.
Option
, Result
, apod.), kde ti z funkce buď vyleze výsledek, který jsi chtěl, nebo None / chybový kód / popis chyby / whatever. Scope-base úklid je zachován. Ale zas to má nevýhodu ve vyšší výřečnosti kódu.
void callMe1() { callMe2(); } void callMe2() { callMe3(); } void callMe3() { callMe4(); } void callMe4() { callMe5(); } void callMe5() { callMe6(); } void callMe6() { callMe7(); } void callMe7() { callMe8(); } void callMe8() { callMe9(); } void callMe9() { callMe10(); } void callMe10() { callMe11(); } void callMe11() { callMe12(); } void callMe12() { callMe13(); } void callMe13() { callMe14(); } void callMe14() { callMe15(); } void callMe15() { callMe16(); } void callMe16() { callMe17(); } void callMe17() { callMe18(); } void callMe18() { callMe19(); } void callMe19() { callMe20(); } void callMe20() { throw "Jardík rulez!"; }
Java je hodně otevřená a ty ty vrstvy jsou vidět. Zvlášť když použiješ nějaký framework nebo knihovnu, tak jich pár bude. Není na tom nic špatného. Jenže když někdo píše v jazyce, kde je ten výkonný kód skrytý v nějakém modulu napsaném třeba v C/C++, tak ho to může zaskočit, protože on obvykle vidí třeba jen dvě tři vrstvy volání svého jednoduchého programu, ale nevidí tu složitost pod tím.
Pokud nechceš tupě kopírovat kód nebo psát duplicitní, tak je celkem normální, že máš hodně menších metod/funkcí a ty se navzájem provolávají. Na vyšší čísla se dostaneš celkem snadno a nevidím v tom problém.
S GC typicky nevíš, kdy se uvolní jaký objekt a v jakém pořadí, což nemusí být přijatelné.Pak je na programatorovi to oserit, necpat klicove veci do destructoru a GC nechat jen dealokaci pameti.
pro systemovy jazyk mi tam chybi [nebezpecna] low-level flexibilita nabizena C/C++.Chybí? Viz.
Rust to bude mit tezke.Souhlasim, mam trochu obavu, aby to nebylo další D. Nicméně D imho trpí od začátku tím, že se snaží mít strašně moc featur a ve velké šíři. Přijde mi to na zvládnutí náročnější než C++ a to už je co říct. Naprosti tomu Go má přesně opačný problém, poskytuje featur naprosté minimum; např. absence generik by mi asi vadila. Imho Go zachraňuje vliv Googlu.
Tak rekl bych ze D nema zas tolik featur, teda minimalne mi prijde lehci na nauceni nez C++, nehlede na to ze nikdo nikoho nenuti pouzivat vse. Spis mi prijde ze lide okolo D se priliz snazi ukazovat vlastnosti jazyka, ktere jsou sice krasne ale pro normalniho programatora treba i nezajimave.
Nicméně autoři o tomhle vědí a Allocator API je jedna z top priorit po 1.0, v plánu je přidat to co nejdříve.Vyborne, jen si tim s ohledem na soucasny design otevrou Pandorinu skrinku.
Naprosti tomu Go má přesně opačný problém, poskytuje featur naprosté minimum;Coz neni vubec spatny pristup: Make things as simple as possible, but not simpler.
absence generik by mi asi vadila.Souhlas.
Imho Go zachraňuje vliv Googlu.Google si navrhnul Go pro sve vlastni potreby a specificke uziti, a dal ho k dispozici. To je trochu jiny pristup, nez "uvedu novy jazyk a uvidim jestli to nekdo zacne pouzivat".
Google si navrhnul Go pro sve vlastni potreby a specificke uziti, a dal ho k dispozici. To je trochu jiny pristup, nez "uvedu novy jazyk a uvidim jestli to nekdo zacne pouzivat".Rust byl navržen pro Servo.
LOL. Myslím, že se tady na AbcTrollingu máme ještě hodně co učit.
Ale kde ten první vezmeš? Můžeš ho leda překládat nějakou starší verzí a tu ještě starší, až nakonec dojdeš k nějaké, která šla ještě přeložit něčím jiným, ne?
Ale nevim, jaky duvod k tomu maji u Rustu - mozna vyuzivani tech safety featur jazykaTo ani ne, spíš hlavně je rustc společně se Servem takový test nasazení jazyka.
Mam nutkani demonstrativne implementovat trusting trust utok na Rust, ktery by byl diky jedinne relevantni implementaci toho jazyka daleko hure odhalitelny, nez stejny utok na Ceckovy kompilator.Přijde mi, že když zaneseš něco do GCC, vyjde to úplně nastejno, resp. získáš ještě mnohem víc, vzhledem k tomu, co všechno je na Linuxu kompilováno GCC...
To ani ne, spíš hlavně je rustc společně se Servem takový test nasazení jazyka.Pokud nekdo testuje jazyk tak, ze jedinna implementace kompilatoru toho jazyka je v tom jazyku, tak bych to opravdu zaradil do kategorie "programatorska onanie".
Přijde mi, že když zaneseš něco do GCC, vyjde to úplně nastejno, resp. získáš ještě mnohem víc, vzhledem k tomu, co všechno je na Linuxu kompilováno GCC...Trusting trust na GCC s velkou pravdepodobnosti odhalim jinym ceckovym kompilatorem. Navic GCC se snad nikde nebuilduje tak, ze by se stahla "oficialni binarka" ze serveru autoru, ale distribuce pouzivaji vlastni binarky, takze se muzou navzajem kontrolovat.
Pokud nekdo testuje jazyk tak, ze jedinna implementace kompilatoru toho jazyka je v tom jazyku, tak bych to opravdu zaradil do kategorie "programatorska onanie".Napsal jsem "test nasazení jazyka"... Ale klidně to do té kategorie zařaď, jsem si jist, že o tom TV Nova natočí reportáž
Trusting trust na GCC s velkou pravdepodobnosti odhalim jinym ceckovym kompilatorem.Jde vůbec GCC skompilovat něčím jiným než GCC? Krom toho, abys skutečně spolehlivně ten útok odhalil, musel bys jít do vygenerovaného kódu, a to už je jedno, jestli udělám s céčkovým programem nebo rustovým... Nicméně (pokus o) trusting trust útok na Rust (nebo jiný kompilátor) by byl určitě zajímavý, takže s chutí do toho...
Tak ona to samozrejme je i trochu onanie, ale na tom nevidim nic spatneho (onanie je zdrava). Ale napriklad kompilator jazyka D bude od pristi verze obsahovat i verzi kompilatoru v D (bavim se o frontendu). Vyhod to ma nekolik.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.