Nie wiem czy wspominałem to już na blogu, ale od czasu do czasu lubię trochę zmienić stack technologiczny. Przez te kilka lat komercyjnego programowania pracowałem w wielu językach: PHP, C#, JavaScript, TypeScript etc. Z niektórymi zostałem na dłużej, czasem z własnej woli, czasem z przymusu. Z innymi łączył mnie krótki romans (jak np. z Elm-em o którym wspominałem już w czwartym artykule na tym blogu: Link). Ostatnimi czasy moje zainteresowania powędrowały w kierunku języka Rust. Co ciekawe, podobnie jak w przypadku Elm-a, pierwszą styczność z nim miałem na konferecji PolyConf jeszcze w 2016 roku. Dopiero ostatnio zacząłem się interesować jak mogę go wykorzystać w tworzonym przeze mnie aplikacjach. W dzisiejszym artykule chciałbym wam przedstawić w jaki sposób skonfigurować sobie środowisko pod development w Rust-cie.
Nowy język? Po Co?
Zanim przejdziemy do instalowania oprogramowania / plugin-ów chciałbym odnieść się do słonia znajdującego się w pokoju. Nie jeden pewnie zastanawia się: co nam daje nauka nowego języka programowania, kiedy w swoim toolbox-ie mamy już teoretycznie wszystko, co będzie nam potrzebne.
Inne spojrzenie na programowanie
Pierwszą zaletą jest możliwość poznania innych wzorców programistycznych. Przelot rakietą ze świata obiektowości do krainy języków funkcyjnych pozwala na obcowanie np. z funkcyjnym paradygmatem programowania. Tak jest także w przypadku Rust-a, który pomimo iż jest językiem wieloparadygmatowym to czerpie garściami właśnie z funkcyjnych paradygmatów.
Trochę inne możliwości
To na pewno nie przekona każdego, ale ja jako programista lubię schodzić w głębiny tego, jak działa komputer. Chętnie poznaje tajniki języków pozwalających na nowe sposoby zarządzania pamięcią czy po prostu na większą władzę człowieka nad maszyną. I tak, wiem, że w C# też się da to zrobić, ale powiedzcie szczerze: ile razy w aplikacjach enterprise użyliście takich słów kluczowych jak unsafe
lub volatile
? Jeżeli ktoś wypłynął na te niegościnne wody to podzielcie się w komentarzach jak wyglądał proces code review w takim przypadku.
Inny Ekosystem
Poznanie innego ekosystemu pozwala nam na poszerzenie perspektywy. Mamy szansę poznać narzędzia, których być może nie ma w obecnym ekosystemie.
Czym jest Rust?
Rust, zgodnie z definicją zamieszczoną na wikipedii, jest językiem wieloparadygmatowym wysokie poziomu. Jego głównymi założeniami są:
- Silne Typowanie
- Wydajność
- Wielowątkowość
Dzięki dość unikalnemu podejściu do zarządzania pamięcią Rust nie potrzebuje Garbage Collector-a aby zapewnić bezpieczeństwo pamięci. Mechanizm ten został nazwany Ownership
i jest to zestaw reguł, które program musi spełnić na poziomie kompilacji kodu.
Jako, że zdecydowana większość takich reguł jest sprawdzana na poziomie kompilatora, Rust ma naprawdę minimalny narzut wydajności związany z zarządzaniem pamięcią. Jest to bardzo dobrze zobrazowane przez ilość zasobów jakich potrzebuje do wykonania programu. Współczynniki Rust-a są bardzo zbliżone do kodu języka C.
Dodatkowo, kolejny rok z rzędu, Rust jest najbardziej lubianym językiem według badań platformy Stack Overflow
To jak zacząć?
Jak każda wielka przygoda, gdzieś musi być początek. W przypadku Rust-a początek drogi jest definiowany przez system operacyjny na którym pracujemy. Jeżeli korzystasz z Linux-a lub Mac OS X-a to najprostszym sposobem instalacji jest uruchomienie poniższej komendy w terminalu:
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh
Oprócz tego będziemy potrzebowali jeszcze specjalnego programu nazwanego Linkerem
. Jest to program, którego zadaniem jest łączenie skompilowanych plików w jeden plik wykonawczy lub bibliotekę.
Aby upewnić się na platformie Mac OS X, że go zainstalowaliśmy, możemy wykonać komendę:
xcode-select --install
Jeżeli korzystasz z Linux-a to są bardzo duże szanse, że posiadasz kompilator języka C, jak GCC lub Clang.
W przypadku windows-a możemy po prostu wejść na stronę: https://www.rust-lang.org/tools/install i przejść instalator.
Aby sprawdzić czy wszystko działa poprawnie, możemy w terminalu wykonać komendę:
$ rustc --version
rustc 1.67.0 (fc594f156 2023-01-24)
Tak oto zainstalowaliśmy kompilator Rust-a na naszej maszynie.
System paczek
Żaden nowoczesny język nie może się obejść bez systemu paczek. Node ma node_modules, .NET ma nuget-a, PHP ma composer-a a Rust ma Cargo, gdzie pojedyncza paczka nosi nazwę Crate
. Cargo jest instalowane na naszej maszynie w momencie instalacji Rust-a, więc o ile prześlijcie poprzedni krok to wystarczy w konsoli wpisać poniższe zapytanie, aby potwierdzić że wszystko działa poprawnie.
$ cargo --version
cargo 1.67.0 (8ecd4f20a 2023-01-10)
Cargo w ekosystemie rust-a pełni też dość specyficzną funkcję, mianowicie jest narzędziem upraszczającym kompilowanie, uruchamianie, tworzenie czy też zarządzanie projektami rustowymi.
Utworzenie nowego projektu jest proste, wystarczy wpisać
$ cargo new hello_rust
Created binary (application) `hello_rust` package
Powstanie następująca struktura: :
.
├── Cargo.toml
└── src
└── main.rs
Widzimy dwa główne pliki. Pierwszy, na głównym poziomie naszego projektu, to Cargo.toml
. Jest to odpowiednik package.json
z node-a lub pliku *.csproj
.NET-a. To tutaj definiujemy z jakich paczek korzystamy w naszym programie jak i metadane projektu. Wszystko jest definiowane za pomoca składni TOML.
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
Nie chcę się zbytnio zagłębiać w możliwości tego pliku, ponieważ pragnę by czytanie dzisiejszego artykułu nie zajęło więcej niż 15 minut, a jeszcze mamy sporo do omówienia. Dlatego przejdźmy od razu do drugiego pliku main.rs
. Jest to główny plik naszego programu. To właśnie on (a mówiąc bardziej dokładnie zawarta w nim funkcja main) będzie służył za punkt startowy.
Sam plik wygląda następująco:
fn main() {
println!("Hello, world!");
}
Mamy w nim deklarację głównej funkcji naszego programu i wykorzystanie makra println
do wypisania tekstu "Hello, world!" na konsolę.
Następnie możemy zbudować projekt za pomocą polecenia:
$ cargo build
Compiling hello_rust v0.1.0 (/Users/kamilkielbasa/Workspace/__nauka/__rust/hello_rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
Jak i też uruchomić go aby zobaczyć czy wszystko działa tak jak należy:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/hello_rust`
Hello, world!
Debugowanie programu
Od strony rust-a już jesteśmy gotowi aby tworzyć i uruchamiać programy. Wiemy już jak rozpocząć naszą przygodę, jednak co w przypadku, gdy coś pójdzie nie tak? Pozostaje ostatnia kwestia... debugowanie. Według mnie, szczególnie w przypadku nauki nowych technologii, języka programowanie etc. możliwość zatrzymania wykonywania programu i podejrzenia co się kryje pod zmiennymi jest bezcenna. Dlatego od razu przejdźmy do setup-u w dwóch IDE: Visual Studio Code i JetBrains CLion (jeżeli chodzi o IDE z rodziny JetBrains to taki sam setup będzie też w InteliJ jak i Rider, ja osobiście wolę Rider-a trzymać do developmentu .NET a Clion do developmentu Rust. Zmiana środowiska programistycznego pozwala mi szybciej przestawić się na pisanie w odpowiednim języku, ale to moja subiektywna opinia, zawsze starajcie się aby dopasować środowisko do własnych potrzeb :) )
Żeby mieć co debugować wykorzystajmy kod, który służy do wyjaśniania podstaw języka Rust. Czyli Guessing Game
z księgi rust-a. W tym artykule nie będę się skupiać na składni, artykuł wyjaśnia co trzeba. Natomiast, jeżeli chodzi o działanie poniższego programu, to jest to bardzo prosta gra, która najpierw losuje liczbę od 1 do 100, a następnie sprawdza jaką liczbę podał użytkownik w stdin. Jeżeli wprowadzi inną od wylosowanej to na konsoli wyświetli mu się odpowiedni komunikat z podpowiedzią czy podał zbyt małą czy zbyt dużą liczbę.
Aby program poniżej zadziałał to musimy dodać pierwszy crate: rand. Wystarczy w pliku Cargo.toml,i w sekcji dependencies
dodać rand = "0.8.5"
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"
Następnie możemy podmienić treść pliku main.rs na następującą:
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("Please input your guess.");
loop {
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please type a number!");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
},
}
}
}
Visual Studio Code
Aby mieć możliwość debugowania programu Rust-a w Visual Studio Code będziemy potrzebować tak naprawdę dwóch pluginów:
Oprócz tego konieczna jest też konfiguracja pluginu CodeLLDB, tak aby współdziałał z zintegrowanym terminalem.Wchodzimy w ustawienia pluginu:
Następnie przechodzimy przez ustawienia uruchamiania, do sekcji Lldb > Launch: Terminal
(Na chwilę pisania tego artykułu znajduje się ona na samym dole) i z select-a wybrać opcję integrated
Po wykonaniu konfiguracji możemy już na spokojnie stawiać breakpoint-y w naszym kodzie :)
Otrzymujemy też dostęp do wszystkich zmiennych w obecnym scope
-ie, jak i też stos wywołań funkcji, które doprowadziły nas do obecnego miejsca w kodzie.
JetBrains CLion
W przypadku produktów z stajni JetBrains, sprawa wygląda nieco prościej. Wystarczy nam instalacja tylko jednego pluginu:
Po instalacji jesteśmy gotowi do debugowania programu napisanego w Rust-cie.
Tak więc, jak widzicie tutaj setup jest nieco łatwiejszny :)
Podsumowanie
Myślę, że tyle wystarczy na "pierwsze starcie" z nowym językiem / ekosystemem. W tym artykule przeszliśmy przez generowanie projektu, wykorzystania Crago do budowania, uruchamiania projektu jak i konfiguracji środowiska programistycznego aby umożliwić debugowanie kodu stworzonego w Rust.
Jak zwykle mam nadzieje że artykuł był przydatny!
Do Następnego!
Cześć :)