( Kiskübi | 2021. 03. 24., sze – 21:58 )

Nézzük sorra.

1.) Lehet, hogy "konkurrens" elérés helyett "fizikailag egy időben"-re gondolsz?  Föltételezek Rólad annyi hátteret, hogy tudod: "fizikailag egy időben" nem lehet, a RAM-nak is egy címbusza, egy adatbusza, kizárólagos vezérlőjelei vannak (gondolom nem dual-port RAM-ról akarsz beszélni, az kissé speciális állat).  Én végig data race condition elkerüléséről írtam.

Ha a konkurrens elérésről a korábbi magyarázataim még részletezésre szorulnak, kérlek, mutass rá a kérdéses mondatomra.  Elképzelhető, hogy Rustban kezdők számára túl tömören írtam.

2.) Az ellenjavallt és az ördögtől való, az két különböző dolog.

3.) A goto-ról: részint ízlése is válogatja, hogy kik mire szoktak jutni, de azt gondolom, hogy a szakmai álláspont:

  • (főleg ANSI) C-ben cleanup-ra pont jól használható (de gcc-ben már van __cleanup__ attribútum)
  • máskülönben ellenjavallt (a kevesebb meglepetést könnyebben megértik a kollégák, kisebb komplexitás => kisebb költség, nagyobb megbízhatóság).

Én biztos nem fogom Neked megtiltani. :)

4.) A globális változóról: ezt sem tilos.  Amikor kell, akkor kell, csak ismerni kell a hátrányait:

  • "lasagne" kódot készítünk, ami a spagettinél egy fokkal rosszabb, mert minden függ mindentől.  Nyerő, ha a komponensek belső konzisztenciája erős, kifele egymástól minél kevésbé függnek, mert annál könnyebb tesztelni és módosítani.
  • data race kockázata ott van, emiatt mint mondtam, Rustban hasonló szabályok vonatkoznak rá, mint referenciákkal megosztott lokális adatra.  Azaz:
    • vagy inicializálás után csak olvasható
    • vagy runtime szinkronizációs módszer kell, de elfelejteni nem tudjuk, kiköveteli a fordító, és ez elég fontos nyeremény
    • vagy minden egyes elérése unsafe blokkos, amit ugye alapvetően nem teszünk, ha a biztonságban kicsit is motiváltak vagyunk.
  • Inicializálás és drop (destruktor):
    • inicializálás csak konstanssal, vagy konstans függvénnyel történhet.  Ugyanis még a main() előtt kell inicializálni, különben egy darabig inicializálatlan lenne, és beleolvashatna akárki (akár egy interrupt handler is, ezt nincs mód ellenőrizni).
    • drop nincs, hasonló okokból, ezzel éljünk együtt.

Nem tiltja a Rust a globális (azaz "static") változó használatát, ha pl. Mutex-be teszed, vagy más szinkronizációs metódusba, esetleg ha csak olvasható (de az ritkán kell).

Főleg akkor vagyunk előrébb a main()-ben (vagy akárhol) lokális, és róla referenciát kérős adattal,

  • cselekedni kell élete végén (Rustban: kell rá drop())
  • vagy dinamikusan kell inicializálni, vagy több globális inicializálása egymástól függ;
  • vagy ha tervezési szempontból kezd aggasztó lenni, hogy mindenki hozzáfér;
  • illetve akkor is, ha nem feltétlenül akarjuk Mutex-be dugni csak azért, mert írni akarjuk.

Az inicializálási problémát a lazy_static!() könyvtári makró is meg tudja oldani (a drop()-ot talán nem oldja meg, nem emlékszem).

Egy bosszantó dolog lehet talán: egyszálú környezetben sem engedi Mutex vagy egyéb szinkronizálás nélkül írni.  Azt hiszem, ez feloldható egy egyszer leírt unsafe trait-tel, de akkor

  • ez ne lib legyen, amit az alkalmazás használhat konkurrensen is,
  • a fejlődése során később se váljon többszálúvá.

A safe módok static változóra tehát elérésenként el fognak füstölni pár órajelet - a szinkronizálás, és opcionálisan a lazy_static miatt.  Ha az 1 MHz-es kontrollert ki kell hajtani padlóig, akkor ne ezt válaszd. :)

A lokális static változókra ugyanez a szigor jut, talán mert egy másik szál is ráláthat arra a függvényre.  És talán ugyanezért hívják static-nak attól függetlenül, hogy globális-e vagy sem.