Rust oczami programisty JavaScript
Jakiś czas temu próbowałem znaleźć coś ciekawego do poczytania w sieci. Wtedy wpadłem na Rusta. Rust to:
„Język umożliwiający każdemu tworzenie niezawodnego i wydajnego oprogramowania”.
Krótko mówiąc, to język do programowania systemów. Zadziwiająco szybki. W wielu testach porównawczych, przeprowadzanych przez The Computer Language Benchmarks Game, prędkością prześciga nawet C ++ (głównie w przypadku złożonych zadań algorytmicznych, takich jak drzewa binarne itp.).
Zanim jednak zacząłem zagłębiać się w sam język, sprawdzałem, co ludzie o nim piszą. Rust powstał w 2011 roku i jest wspierany przez Mozillę (non-profit). Ma za sobą nieco niszową grupę, składającą się głównie z byłych programistów C i C++, którzy chcieli oderwać się od NPE, niezdefiniowanych zachowań, złożoności języka, zwariowanych makr itp.
Mimo że Rust wciąż jest bardzo młodym językiem, który wciąż się rozwija, ma już w sieci swoją stronę poświęconą programowaniu gier. Niektóre z gier stworzonych w tym języku są bardzo popularne, a nawet zostały umieszczone w App Store i Google Play Store, np. A Snake's Tale. Rust ma również skutecznego menedżera pakietów - crates.io, który jest porównywalny z npm. Pobrano go już prawie 1,5 miliarda razy.
Po zobaczeniu tego wszystkiego zdecydowałem, że muszę zaspokoić swoją ciekawość i nauczyć się składni Rusta. Zacząłem od przeczytania The Book - kompletnego przewodnika po składni, idiomach i strukturze programów w Rust.
Ponieważ znam wiele języków podobnych do C, składnia była dla mnie prosta i łatwa do zrozumienia. Jeśli jesteś jedną z osób, które używają const, gdy tylko jest to możliwe, i gardzisz tymi, którzy tego nie robią - pokochasz Rusta. Domyślna forma przypisywania zmiennych czyni je niemutowalnymi.
// Below is an immutable variable
let a = 5;// Below is a mutable variable
let mut b = 5;// General form for variables:
let var_name: type = value;
Typy danych w Rust są proste. Liczby całkowite składają się z 8 do 128-bitowych liczb z i bez znaku.
Liczby całkowite
Istnieją również niemutowalne typy tablicowe, znaki, &str
(referencję do listy znaków w pamięci), krotki, wyliczenia,
struktury, kolekcje itp. Funkcje deklarowane są za pomocą słowa kluczowego fn
:
fn main() {
println!("Hello, world");
}
Możesz określić zwracane wartości za pomocą symbolu ->
, a jeśli zwracasz je w ostatnim wierszu funkcji, to nie ma potrzeby dołączania słowa kluczowego return
.
// returns the 32-bit integer 5
fn my_function() -> u32 {
5
}
Wszystko szło gładko, dopóki nie wpadłem na pojęcie własności (ownership). Wszystkie programy w trakcie działania muszą zarządzać sposobem korzystania z pamięci komputera. Ponieważ Rust nie odśmieca pamięci, a programiści nie muszą ręcznie przydzielać i zwalniać pamięci, pamięcią zarządza system własności.
Oto zasady własności w Rust (bezpośrednio z The Book):
- Każda wartość w Rust ma zmienną, która nazywa się jej właścicielem.
- Jednocześnie może istnieć tylko jeden właściciel.
- Gdy właściciel wypadnie z zasięgu, wartość zostanie usunięta.
Spójrzmy na przykład:
{ // s is not valid here, it’s not yet declared
let s = "hello"; // s is valid from this point forward
// do stuff with s
} // this scope is now over, and s is no longer valid
Tak przyblokowałem się, pierwszy raz używając Rusta.
let a = String::from("Hello, world!"); // a is the owner
let b = a; // ownership has changed to b. What is a?
println!("{}", a); // error[E0382]: borrow of moved value: `a`
Ponieważ jednocześnie może istnieć tylko jeden właściciel, nie mogę przypisać wartości zmiennej innemu, używając nazwy pierwszej zmiennej, jeśli została ona przydzielona w stercie, tak jak String
w Rust. Teraz, jeśli typ zmiennej jest prymitywny (int, double, bool itp.), ponowne przypisanie po prostu skopiuje wartość i NIE zmieni właściciela.
Odwołując się do zmiennej, używasz symbolu &
jak w C i C++. Referencje pozwalają programiście pobrać wartość bez przejmowania własności. W jednym czasie może istnieć tyle niemutowalnych referencji ile chcesz, ale tylko JEDNA mutowalna referencja.
let a = String::from("Dude, Rust is sick.");
{
let b = &a; // ok
let c = &a; // ok
}
{
let b = &mut a; // mutable reference. ok.
*b = String::from("Different!"); // change value. ok
}
{
let b = &mut a; // ok
let c = &mut a; // ERROR. Only one mut reference at a time!
}
Jeszcze trudniejszy do zrozumienia jest dla mnie czas życia (lifetime), czyli koncept, dzięki któremu Rust nie musi używać garbage collectora. Jest to również rozwiązanie znanego problemu C++ - zwisających wskaźników (dangling pointers), gdy zmienna jest „używana po zwolnieniu”. Nie będę nawet próbował tu wyjaśniać tej koncepcji. Więcej znajdziesz tutaj.
Ogólnie rzecz biorąc, moje doświadczenie z Rustem było satysfakcjonujące. Z łatwością zrozumiałem jego składnię i wytyczne dotyczące stylu. Muszę przyznać, że niektóre koncepcje były początkowo trudne do zrozumienia. Nie wiem, czy to tylko moje spostrzeżenie, ale nawet kod napisany w Ruście wygląda elegancko.
Rust wykorzystuje funkcyjne techniki programowania, powszechnie stosowane w JavaScript (filtrowanie, mapowanie itp.), a słowa kluczowe składni są krótkie i konkretne (fn
, enum
, let
, mut
, i32
i str
).
Niektórzy programiści nawołują, aby Rust był kolejnym C albo C++. Nie wiem, czy dojdzie do tego w najbliższym czasie, ale jedno jest pewne - Rust będzie rósł i dojrzewał jako język. W końcu ma wsparcie Mozilli i „kult” odziedziczony po programistach C++, którzy mogli by za niego zabić.
Oryginał tekstu w języku angielskim przeczytasz tutaj.