Programowanie w języku Rust - ebook
Programowanie w języku Rust - ebook
Programowanie w języku Rust to oficjalna książka na temat Rusta: języka programowania na licencji open source, który pozwala nam szybciej pisać niezawodne oprogramowanie. Rust daje możliwość kontrolowania szczegółów niskiego poziomu (jak wykorzystanie pamięci) w połączeniu z ergonomią wysokiego poziomu, eliminując kłopoty tradycyjnie związane z językami niskiego poziomu.
W książce Programowanie w języku Rust dwaj członkowie Rust Core Team pokazują, jak w pełni korzystać z właściwości Rusta – od instalacji po tworzenie własnych niezawodnych i skalowalnych programów.
Zaczynamy od podstaw, takich jak tworzenie funkcji, wybieranie typów danych i wiązanie zmiennych i przechodzimy następnie do bardziej zaawansowanych pojęć takich jak:
posiadanie i pożyczanie, czasy życia i cechy bezpieczeństwo pamięci Rusta, które gwarantuje budowanie szybkich, bezpiecznych programów testowanie, obsługa błędów i efektywna refaktoryzacja typy generyczne, inteligentne wskaźniki, wielowątkowość, obiekty cech oraz zaawansowane dopasowywanie wzorców użycie Cargo - wbudowanego w Rust menedżera pakietów, służącego do budowania, testowania i dokumentowania swojego kodu i zarządzania zależnościami używanie zaawansowanego kompilatora Rusta wraz z technikami programowania opartymi na kompilatorze Książka zawiera dużo przykładów kodu, a także trzy rozdziały poświęcone budowaniu gotowych projektów przeznaczonych do sprawdzenia swojej wiedzy: gra w zgadywanie, implementacja narzędzia wiersza poleceń w języku Rust oraz wielowątkowy serwer.
| Kategoria: | Programowanie |
| Zabezpieczenie: |
Watermark
|
| ISBN: | 978-83-01-20639-0 |
| Rozmiar pliku: | 27 MB |
FRAGMENT KSIĄŻKI
O autorach
Steve Klabnik jest szefem zespołu dokumentacji Rusta oraz jednym z najważniejszych deweloperów tego języka. Często zabiera głos i aktywnie uczestniczy w tworzeniu open source. Poprzednio pracował przy takich projektach jak Ruby i Ruby on Rails. Klabnik pracuje w firmie Mozilla.
Carol Nichols jest członkinią podstawowego zespołu Rusta, a także współzałożycielką pierwszej na świecie firmy konsultingu programistycznego skupionego na języku Rust, Integer 32, LLC. Nichols organizuje konferencje Rust Belt Rust Conference.WSTĘP
Nie zawsze było to oczywiste, lecz język programowania Rust ma przede wszystkim związek z upoważnianiem – niezależnie od tego, jaki rodzaj kodu piszecie, Rust daje upoważnienie do sięgania dalej, do programowania z zaufaniem w szerszym zestawie dziedzin, niż to robiliśmy dotychczas.
Weźmy jako przykład pracę „na poziomie systemowym”, która dotyczy szczegółów zarządzania pamięcią na niskim poziomie, reprezentacji danych i współbieżności. Tradycyjnie ten obszar programowania jest postrzegany jako arkana dostępne tylko dla niewielkiej grupy wtajemniczonych, którzy poświęcili lata na naukę unikania wstydliwych wpadek. A nawet osoby, które mają w tym praktykę, robią to ostrożnie, aby ich kod nie był otwarty na eksploity, katastrofy i uszkodzenia.
Rust przełamuje te bariery, eliminując stare pułapki i zapewniając nam przyjazny, elegancki zestaw narzędzi. Programiści, którzy muszą się zagłębić w sterowanie niskiego poziomu, mogą to zrobić w języku Rust, bez typowego ryzyka katastrofy lub luk w zabezpieczeniach, a także bez konieczności nauki szczegółów kapryśnych narzędzi. Co więcej, język jest zaprojektowany tak, aby w naturalny sposób kierować nas do niezawodnego kodu, który jest efektywny w zakresie szybkości i wykorzystania pamięci.
Programiści, którzy pracują już w kodzie niskiego poziomu, mogą użyć Rusta, aby zmierzyć się z czymś nowym. Na przykład wprowadzenie równoległości to w języku Rust działanie dość niskiego ryzyka – kompilator wychwyci za nas klasyczne błędy. Możemy więc wziąć się za bardziej agresywną optymalizację kodu, mając pewność, że nie wprowadzimy przypadkiem awarii ani eksploitów.
Ale Rust nie ogranicza się do programowania niskiego poziomu. Jest wystarczająco wyrazisty i ergonomiczny, aby tworzyć aplikacje wiersza poleceń, serwery sieciowe i wiele innych rodzajów kodu, który jest przyjemny w pisaniu – proste przykłady obu elementów znajdziecie w dalszej części książki. Praca z językiem Rust pozwala budować umiejętności, które można przenosić między dziedzinami. Możemy nauczyć się Rusta, pisząc aplikację sieciową, a potem wykorzystać te umiejętności do celów naszego Raspberry Pi.
Książka ta obejmuje cały potencjał Rusta, jaki daje on swoim użytkownikom. Ma przyjazną i dostępnie napisaną treść, która nie tylko pomaga podnieść poziom wiedzy o języku Rust, lecz także zwiększyć zaufanie do siebie jako programisty. Zagłębmy się więc w lekturze i zacznijmy naukę – witamy w społeczności Rusta!
Nicholas Matsakis i Aaron TuronPODZIĘKOWANIA
Chcielibyśmy podziękować wszystkim, którzy pracowali z nami nad językiem Rust, tworząc wspaniały język warty napisania o nim książki. Jesteśmy wdzięczni każdemu ze społeczności Rusta, że są otwarci i tworzą środowisko, do którego należałoby zaprosić większą liczbę osób.
Szczególnie wdzięczni jesteśmy tym, którzy przeczytali początkowe wersje tej książki online oraz przekazali nam uwagi, raporty o błędach i prośby dotyczące zmian. Specjalne podziękowania kierujemy do Eduarda-Mihaiła Burtescu i Alexa Crichtona za opinię techniczną, a Karen Rustad Tölvy za grafikę na okładce. Dziękujemy naszemu zespołowi z No Starch, w tym Billowi Pollockowi, Liz Chadwick i Janelle Ludowise, za poprawienie tej książki i doprowadzenie jej do druku.
Steve chciałby podziękować Carol za to, że była wspaniałą współautorką. Bez niej ta książka miałaby znacznie gorszą jakość, a jej pisanie zajęłoby znacznie więcej czasu. Ponadto dziękuję Ashley Williams, która udzialała nam ogromnego wsparcia od początku aż do końca pisania książki. Carol chciałaby podziękować Steve’owi za rozbudzenie jej zainteresowania Rustem i za możliwość pracy nad tą książką. Jest wdzięczna swojej rodzinie, zwłaszcza mężowi Jake’owi Gouldingowi i córce Vivian za nieustającą miłość i wsparcie.WPROWADZENIE
Witamy w książce Programowanie w języku Rust będącej wprowadzeniem do tego języka. Rust to język programowania, który pomaga szybciej pisać niezawodne oprogramowanie. Ergonomia wysokiego poziomu i sterowanie na niskim poziomie to często sprzeczności w projektach języków programowania. Rust stawia wyzwanie temu konfliktowi. Dzięki zrównoważeniu silnych możliwości technicznych i dużego doświadczenia deweloperskiego, Rust daje możliwość kontrolowania szczegółów niskiego poziomu (jak wykorzystanie pamięci) bez kłopotów tradycyjnie związanych z taką kontrolą.
Dla kogo jest Rust
Rust jest idealny dla wielu ludzi z różnych powodów. Przejrzyjmy najważniejsze ich grupy.
Zespoły deweloperskie
Rust sprawdza się jako produktywne narzędzie pracy dużych zespołów deweloperów na różnym poziomie wiedzy o programowaniu systemów. Kod niskiego poziomu jest podatny na wiele wyrafinowanych błędów, które w większości języków można wychwycić tylko dzięki intensywnemu testowaniu i uważnemu sprawdzaniu kodu przez doświadczonych deweloperów. W języku Rust kompilator gra rolę bramkarza, odmawiając skompilowania kodu z tymi problematycznymi błędami, w tym z błędami współbieżności. Dzięki pracy z kompilatorem zespół może poświęcić czas na skupienie się na schemacie logiczym programu, a nie na wyszukiwanie błędów.
Rust oferuje także współczesne narzędzia programistyczne do programowania systemowego:
- Cargo, menedżer zależności i narzędzie do tworzenia oprogramowania, sprawia, że dodawanie, kompilowanie i zarządzenia zależnościami jest „bezbolesne” i spójne w całym ekosystemie Rusta;
- Rustfmt zapewnia spójny styl kodowania różnych deweloperów;
- Rust Language Server zasila integrację IDE (Integrated Development Environment) w celu tworzenia kodu i wewnętrznych komunikatów o błędach.
Korzystając z tych i innych narzędzi w ekosystemie Rusta, deweloperzy mogą być wydajni, pisząc na poziomie systemowym.
Studenci
Rust jest przeznaczony dla studentów i tych, którzy są zainteresowani nauką pojęć systemowych. Korzystając z Rusta, wiele osób poznało takie pojęcia, jak tworzenie systemów operacyjnych. Społeczność Rusta chętnie przyjmuje studentów i z radością odpowiada na ich pytania. Dzięki takim przedsięwzięciom jak ta książka zespoły Rusta chcą, aby pojęcia systemowe były bardziej dostępne dla większej liczby osób, zwłaszcza tych rozpoczynających programowanie.
Firmy
Setki firm, małych i dużych, używa Rusta do wielu zadań. Zadania te obejmują narzędzia wiersza poleceń, usługi sieciowe, narzędzia DevOps, wbudowane urządzenia, analizę i transkrypcje audio o wideo, kryptowaluty, bioinformatykę, mechanizmy wyszukiwania, Internet rzeczy, a nawet duże fragmenty przeglądarki Firefox.
Deweloperzy open source
Rust jest dla osób, które chcą budować język programowania, jego społeczność, narzędzia deweloperskie i biblioteki. Chcielibyśmy, aby każdy dokładał swoją cegiełkę do języka Rust.
Ludzie ceniący szybkość i stabilność
Rust jest dla tych, którzy wymagają od języka szybkości i stabilności. Przez szybkość rozumiemy szybkość programów, które możemy tworzyć, i szybkość, z jaką Rust pozwala nam je pisać. Sprawdzenie dokonywane przez kompilator Rusta zapewnia stabilność dzięki dodawaniu funkcji i refaktoryzacji. To kontrastuje z wątłym starszym kodem w językach niemających takiej kontroli, a które deweloperzy często boją się modyfikować. Dzięki dążeniu do abstrakcji o zerowych kosztach, funkcjom wyższego poziomu, które kompilują się do kodu niskiego poziomu, a także szybkiemu kodowi pisanemu ręcznie twórcy Rusta starają się tworzyć bezpieczniejszy kod, który jednocześnie jest szybszy.
Społeczność Rusta chce też wspierać innych użytkowników. Wymienieni tu są po prostu najważniejszymi interesariuszami. Ogólnie największą ambicją twórców Rusta jest wyeliminowanie kompromisów, które programiści akceptowali przez całe dekady, zapewniając bezpieczeństwo i produktywność, szybkość i ergonomię. Wypróbujcie Rusta i sprawdźcie, czy będzie wam odpowiadać.
Dla kogo jest ta książka
W książce założono, że czytelnicy pisali dotyczczas kod w innym języku programowania, ale nie ma to znaczenia, w jakim. Chcieliśmy, aby materiał był szeroko dostępny dla osób z różnym doświadczeniem programistycznym. Nie poświęcamy wiele czasu na tłumaczenie, czym jest programowanie lub jak o nim myśleć. Osoby, które są nowicjuszami w programowaniu, powinny raczej sięgnąć po książkę poświęconą wprowadzeniu do programowania.
Jak korzystać z tej książki
Ogólnie przyjęto, że książka będzie czytana od początku do końca. Kolejne rozdziały są oparte na pojęciach z rozdziałów wcześniejszych, a te wcześniejsze mogą nie zawierać szczegółów danego tematu. Zwykle wracamy do niego w dalszych rozdziałach.
W książce są dwa rodzaje rozdziałów: rozdziały dotyczące pojęć oraz projektów. W rozdziałach dotyczących pojęć uczymy się danego aspektu języka Rust. W rozdziałach z projektami budujemy razem niewielki program, korzystając z tego, czego nauczyliśmy się do tej pory. Rozdziały 1, 12 i 20 to rozdziały zawierające projekty. Pozostałe to rozdziały poświęcone pojęciom.
W rozdziale 1 wyjaśniamy, jak zainstalować Rusta, jak napisać program Hello, world! oraz jak używać Cargo – menedżera pakietów i systemu tworzenia oprogramowania w języku Rust. Rozdział 2 to praktyczne wprowadzenie do języka. Omawiamy tu pojęcia wysokiego poziomu, a kolejne rozdziały zawierają potrzebne szczegóły. Jeśli od razu chcemy zająć się praktyką, rozdział 2 świetnie się do tego nadaje. Z początku można mieć ochotę przeskoczyć rozdział 3, w którym przedstawiamy funkcje Rusta, podobne jak innych języków programowania, i od razu przejść do rozdziału 4, aby poznać system posiadania Rusta. Uczniowie metodyczni, którzy wolą poznać szczegóły zanim przejdą dalej, mogą pominąć rozdział 2 i przejść do rozdziału 3, wracając do rozdziału 2, gdy zechcą pracować nad projektem, stosując poznane szczegóły.
W rozdziale 5 omawiamy struktury i metody, a w rozdziale 6 – wyliczenia, wyrażenia match i konstrukcję sterującą if let. Struktur i wyliczeń będziemy używać do tworzenia niestandardowych typów w języku Rust.
W rozdziale 7 poznamy system modułów Rusta i reguły prywatności do celów organizacji kodu i jego interfejsu API (Application Programming Interface). W rozdziale 8 omawiamy typowe struktury danych kolekcji, które zapewnia standardowa biblioteka, takie jak wektory, łańcuchy oraz mapy skrótów. Rozdział 9 dotyczy filozofii i techniki obsługi błędów.
W rozdziale 10 zagłębiamy się w elementy generyczne, cechy i czas życia, co daje nam możliwość definiowania kodu, który stosuje się do wielu typów. Rozdział 11 dotyczy testowania, co nawet przy gwarancjach bezpieczeństwa Rusta jest konieczne do stwierdzenia, czy kod programu jest poprawny. W rozdziale 12 tworzymy własną implementację podzbioru funkcjonalności z narzędzia wiersza poleceń grep, które wyszukuje tekst w plikach. W tym celu korzystamy z wielu pojęć omawianych w poprzednich rozdziałach.
Rozdział 13 zawiera analizę zamknięć i iteratorów – funkcji Rusta, które wywodzą się z funkcyjnych języków programowania. W rozdziale 14 analizujemy dokładniej Cargo i omawiamy najlepsze praktyki współdzielenia naszych bibliotek z innymi. W rozdziale 15 przedstawiamy inteligentne wskaźniki, których dostarcza standardowa biblioteka, oraz cechy, które umożliwiają ich funkcjonalność.
W rozdziale 16 analizujemy różne modele programowania współbieżnego i mówimy o tym, jak Rust pomaga nam bez obaw programować wiele wątków. W rozdziale 17 porównujemy idiomy Rusta z zasadami programowania obiektowego, które możecie znać.
Treść rozdziału 18 odwołuje się do wzorców i do porównywania z nimi, co stanowi dający wiele możliwości sposób wyrażania idei w programach języka Rust. Rozdział 19 zawiera duży wybór zaawansowanych tematów, w tym niebezpieczny (unsafe) Rust i więcej informacji o czasach życia, cechach, typach, funkcjach i zamknięciach.
W rozdziale 20 tworzymy projekt, w którym implementujemy na niskim poziomie wielowątkowy serwer sieciowy!
Wreszcie dodatki zawierają użyteczne informacje o języku w postaci zestawień. W dodatku A omawiamy słowa kluczowe Rusta, w dodatku B – operatory i symbole, w dodatku C – wyprowadzane cechy dostępne w bibliotece standardowej, a dodatek D dotyczy makr.
Nie ma złego sposobu czytania tej książki – jeśli chcecie przeskoczyć do przodu, proszę bardzo! Może będziecie musieli wrócić do wcześniejszych rozdziałów, jeśli coś będzie niejasne. Róbcie to, co jest dla was najlepsze.
Ważną częścią nauki języka Rust jest wiedza o tym, jak czytać komunikaty o błędach wyświetlane przez kompilator – one prowadzą nas do działającego kodu. Dlatego podajemy wiele przykładów kodu, który nie kompiluje się wraz z komunikatem o błędzie pokazywanym przez kompilator w każdej sytuacji. Trzeba wiedzieć, że jeśli napiszemy i uruchomimy przypadkowy przykład, może się on nie skompilować! Trzeba przeczytać sąsiadujący tekst, aby sprawdzić, czy przykład, który próbujemy uruchomić, może prowadzić do błędu. W większości sytuacji doprowadzimy was do poprawnej wersji każdego kodu, który się nie kompiluje.
Źródła i jak uczestniczyć w tworzeniu tej książki
Książka ta jest open source. Jeśli znajdziecie błąd, nie wahajcie się go opisać lub wysłać żądanie uzupełnienia na GitHub pod adresem https://github.com/rust-lang/book/. Sprawdźcie CONTRIBUTING.md na stronie https://github.com/rust-lang/book/blob/master/CONTRIBUTING.md, aby poznać tam więcej szczegółów.
Kod źródłowy do przykładów z tej książki, errata i inne informacje są dostępne pod adresem https://www.nostarch.com/Rust/.1
ROZPOCZYNAMY
Zacznijmy naszą podróż po języku Rust! Mamy wiele do nauki, ale każda podróż ma swój początek. W tym rozdziale omówimy:
• instalację języka Rust w systemach Linux, macOS i Windows;
• pisanie programu, który wyświetla nam Hello, world!;
• wykorzystanie Cargo – menedżera pakietu i systemu tworzenia oprogramowania w języku Rust.
Instalacja
Pierwszym krokiem jest instalacja Rusta. Pobierzemy go przez rustup, narzędzie wiersza poleceń do zarządzania wersjami Rusta i powiązanymi z nim narzędziami. Pobranie wymaga połączenia z Internetem.
UWAGA
Jeśli z jakiegoś powodu nie chcecie korzystać z rustup, należy zajrzeć na stronę instalacyjną Rusta, pod adresem https://www.rust-lang.org/install.html, gdzie można znaleźć inne opcje.
Podane niżej kroki pozwalają zainstalować stabilną wersję kompilatora Rust. Wszystkie przykłady i wyniki pokazane w książce są oparte na stabilnej wersji Rust 1.21.0. Stabilność Rusta gwarantuje, że wszystkie przykłady z książki dadzą się skompilować także w nowszych wersjach języka. Wyniki mogą się nieco różnić, zależnie od wersji, gdyż Rust często ma wprowadzane poprawki komunikatów o błędach i ostrzeżenia. Innymi słowy, każda nowsza, stabilna wersja Rusta, którą zainstalujemy, korzystając z podanych tu kroków, powinna działać zgodnie z oczekiwaniami przedstawionymi w książce.
Notacja wiersza poleceń
W tym rozdziale oraz w całej książce pokażemy niektóre polecenia używane z poziomu terminalu. Wiersze poleceń, które wprowadzamy z terminalu, muszą się zaczynać od znaku $. Nie musimy go wpisywać, gdyż wskazuje on początek każdego polecenia. Wiersze, które nie rozpoczynają się od znaku $, pokazują zwykle wyniki poprzedniego polecenia. Ponadto przykłady specyficzne dla Power- Shell będą używać znaku > zamiast znaku $.
Instalacja rustup w systemach Linux i macOS
Jeśli używamy systemów Linux lub macOS, musimy otworzyć terminal i wprowadzić poniższe polecenie:
Polecenie pobiera skrypt i rozpoczyna instalację narzędzia rustup, które instaluje najnowszą stabilną wersję Rusta. Możemy zostać poproszeni o hasło. Jeśli instalacja się powiedzie, pojawi się pokazany niżej wiersz:
(Rust został zainstalowany. Wspaniale!)
Jeśli chcemy, możemy pobrać skrypt i sprawdzić go przed uruchomieniem.
Przy kolejnym logowaniu skrypt instalacyjny automatycznie dodaje Rust do naszej systemowej ścieżki PATH. Jeśli chcemy zacząć korzystać z Rusta od razu, bez restartowania terminalu, w naszej powłoce musimy uruchomić poniższe polecenie, aby ręcznie dodać Rust do naszej ścieżki systemowej PATH:
Alternatywnie możemy dodać następujący wiersz do naszego ~/.bash_profile:
Ponadto potrzebny nam będzie jakiegoś rodzaju konsolidator. Być może macie już jakiś zainstalowany, ale przy próbie kompilacji programu w języku Rust mogą się pojawić błędy mówiące, że konsolidator nie może działać, co będzie oznaczać brak odpowiedniej instalacji, i wtedy trzeba zainstalować go ręcznie. Kompilatory języka C są zwykle dostarczane z poprawnym konsolidatorem. Należy sprawdzić w dokumentacji, jak zainstalować kompilator języka C. Ponadto niektóre popularne pakiety języka Rust są oparte na kodzie C i kompilator C będzie nam potrzebny. Dlatego warto zainstalować go od razu.
Instalacja rustup w systemie Windows
W systemie Windows należy przejść do witryny https://www.rust-lang.org/install.html i postępować zgodnie z instrukcjami instalacji języka Rust. W jakimś punkcie instrukcji otrzymamy komunikat informujący, że potrzebne są nam także narzędzia instalacyjne C++ dla Visual Studio 2013 lub w wersji nowszej. Najprostszym sposobem pozyskania narzędzi instalacyjnych jest zainstalowanie Build Tools for Visual Studio 2017 z witryny https://www.visualstudio.com/downloads/. Narzędzia te są dostępne w części Other Tools and Frameworks (Inne narzędzia i schematy). W dalszej części książki są używane polecenia, które działają zarówno z cmd.exe, jak i z PowerShell. Jeśli wystąpią jakieś różnice, wyjaśnimy, której wersji używać.
Aktualizacja i odinstalowywanie
Po zainstalowaniu Rusta za pośrednictwem rustup aktualizacja do najnowszej wersji jest łatwa. Z naszej powłoki uruchamiamy następujący skrypt aktualizacyjny:
Aby odinstalować Rusta i rustup, uruchamiamy w naszej powłoce poniższy skrypt dezinstalacyjny:
Usuwanie błędów
Aby sprawdzić, czy Rust został poprawnie zainstalowany, otwieramy powłokę i wpisujemy wiersz polecenia:
Powinniśmy zobaczyć numer wersji, skrót wydania oraz datę wydania ostatniej stabilnej wersji. Format komunikatu jest następujący:
Jeśli zobaczymy taką informację, to znaczy, że Rust został poprawnie zainstalowany. Jeśli informacja się nie pojawi, a jesteśmy w systemie Windows, musimy sprawdzić, czy Rust znajduje się w naszej zmiennej systemowej %PATH%. Jeśli wszystko jest tu poprawne, a Rust nadal nie działa, to pomoc można znaleźć w kilku miejscach. Najprostsze jest skorzystanie z kanału IRC #rust na witrynie irc.mozilla.org, do której można dotrzeć przez Mibbit pod adresem http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust/. Pod tym adresem można pogawędzić z Rustarianami (to przydomek, jaki sobie nadaliśmy), którzy pomogą nam rozwiązać problem. Innym świetnym zasobem jest forum Users pod adresem https://users.rust-lang.org/ oraz Stack Overflow pod adresem http://stackoverflow.com/questions/tagged/rust/.
Lokalna dokumentacja
Instalator obejmuje także lokalną kopię dokumentacji, więc można ją czytać w trybie offline. Aby otworzyć lokalną dokumentację, musimy w naszej przeglądarce uruchomić rustup doc.
Za każdym razem, gdy typ lub funkcja są dostarczone przez standardową bibliotekę, a my nie mamy pewności, co ona robi lub jak z niej korzystać, możemy użyć dokumentacji interfejsu programowania aplikacji (API), aby się tego dowiedzieć.
Hello, world!
Gdy już mamy zainstalowany Rust, pora na napisanie pierwszego programu. Tradycją przy nauce nowego języka jest napisanie małego programu, który wyświetla na ekranie tekst Hello, world!.
UWAGA
W książce przyjęto, że czytelnicy umieją posługiwać się wierszem poleceń. Rust nie ma szczególnych wymagań dotyczących edycji, narzędzi ani miejsca położenia kodu, więc można też bez przeszkód korzystać z zintegrowanego środowiska deweloperskiego (integrated development environment, IDE) zamiast z wiersza poleceń. Wiele IDE zawiera obecnie elementy obsługi Rusta. Szczegóły można sprawdzić w dokumentacji. Obecnie zespół Rusta skupia się na umożliwieniu szerszej obsługi w IDE i w tym obszarze następuje szybki postęp.
Tworzenie katalogu projektu
Zaczniemy od utworzenia katalogu, w którym będzie przechowywany nasz kod program. Dla języka Rust nie ma znaczenia, gdzie znajduje się kod, ale na potrzeby ćwiczeń i projektów zawartych w tej książce zalecamy utworzenie katalogu projects w podstawowym katalogu i przechowywanie tam wszystkich naszych projektów.
Aby utworzyć katalog projects oraz katalog dla projektu Hello, world! w obrębie tego katalogu, otwieramy terminal i wprowadzamy podane niżej polecenia. Dla systemów Linux i macOS wprowadzamy:
Dla wiersza poleceń systemu Windows polecenia mają postać:
Z kolei w Windows PowerShell wprowadzamy:
Pisanie i uruchamianie programu w języku Rust
Teraz utworzymy nowy plik źródłowy i nazwiemy go main.rs. Pliki w języku Rust zawsze mają rozszerzenie .rs. Jeśli w nazwie pliku używamy więcej niż jednego słowa, oddzielamy je znakiem podkreślenia. Na przykład piszemy hello_world.rs, a nie helloworld.rs.
Otwórzmy teraz utworzony przed chwilą plik hello_world.rs i wprowadźmy do niego kod pokazany na listingu 1.1.
Listing 1.1. Program, który wyświetla tekst Hello, world!
Zapisujemy plik i wracamy do okna terminalu. Aby skompilować i uruchomić program w systemach Linux lub macOS, wprowadzamy następujące polecenia:
W systemie Windows wprowadzamy polecenie .\main.exe zamiast ./main:
Niezależnie od używanego systemu operacyjnego, ciąg znaków Hello, world! powinien wyświetlić się na terminalu. Jeśli nie widzimy takiego wyniku, musimy odwołać się do punktu „Usuwanie błędów” w rozdz. 1, aby znaleźć sposób rozwiązania problemu.
Jeśli tekst Hello, world! się wyświetlił, to świetnie. Można oficjalnie stwierdzić, że napisaliśmy pierwszy program w języku Rust, czyli staliście się programistami Rusta – witamy!
Anatomia programu w języku Rust
Przyjrzyjmy się szczegółowo, co się dzieje w naszym programie Hello, world!. Oto pierwszy kawałek puzzla:
Te wiersze definiują funkcję w języku Rust. Funkcja main jest funkcją specjalną – zawsze stanowi pierwszy element kodu, który jest uruchamiany w wykonywalnym programie Rusta. Pierwszy wiersz deklaruje funkcję o nazwie main, która nie ma żadnego parametru i niczego nie zwraca. Gdyby miała ona parametry, byłyby one umieszczone w nawiasach ( ).
Zauważmy też, że treść funkcji znajduje się w nawiasach klamrowych { }. Rust wymaga, aby takie nawiasy otaczały treści funkcji. W dobrym stylu jest umieszczenie otwierającego nawiasu klamrowego w tym samym wierszu, w którym jest deklaracja funkcji, z odstępem między nimi.
W czasie pisania tej książki tworzone było automatyczne narzędzie formatujące o nazwie rustfmt. Jeśli chcemy trzymać się standardowego stylu pisania w projektach Rusta, rustfmt sformatuje nasz kod w określonym stylu. Zespól Rusta planuje ostatecznie dołączyć to narzędzie do standardowej dystrybucji języka, takiej jak rustc. Tak więc zależnie od tego, kiedy czytacie tę książkę, być może macie już to zainstalowane na swoim komputerze! Więcej szczegółów można znaleźć online.
Wewnątrz funkcji main znajduje się następujący kod:
Ten wiersz wykonuje całe działanie tego małego programu – wyświetla tekst na ekranie. Warto zwrócić uwagę na cztery niewielkie szczegóły. Po pierwsze, styl Rusta polega na robieniu wcięć za pomocą czterech odstępów, a nie tabulatora.
Po drugie, println! wywołuje makro Rusta. Gdyby było to wywołanie funkcji, podalibyśmy samą nazwę w postaci println (bez znaku !). Makra języka Rust omawiamy bardziej szczegółowo w dodatku D. Teraz wystarczy wiedzieć, że użycie znaku ! oznacza, że wywołujemy makro, a nie zwykłą funkcję.
Po trzecie, widzimy łańcuch tekstowy "Hello, world!". Przekazujemy go jako argument do println!, a wtedy tekst jest wyświetlany na ekranie.
Po czwarte, kończymy wiersz znakiem średnika (;), który wskazuje, że wyrażenie jest zakończone i można zacząć pisać następne. Większość wierszy w kodzie języka Rust kończy się średnikiem.
Kompilacja i uruchomienie to oddzielne kroki
Właśnie uruchomiliśmy nowo utworzony program. Przeanalizujmy więc wszystkie kroki tego procesu.
Przed uruchomieniem programu w języku Rust musimy go skompilować za pomocą kompilatora Rusta, co robimy, korzystając z polecenia rustc, po którym wpisujemy nazwę naszego pliku źródłowego, co wygląda tak:
Osoby, które umieją programować w C lub C++, zauważą, że przypomina to gcc lub clang. Po udanej kompilacji Rust daje na wyjściu binarny plik wykonywalny. W przypadku systemów Linux, MacOS i PowerShell w Windows zobaczymy plik wykonywalny, wykonując polecenie ls w naszej powłoce:
W przypadku CMD w systemie Windows musimy podać poniższe polecenie:
Pokazuje to plik z kodem źródłowym o rozszerzeniu .rs, plik wykonywalny (w systemie Windows main.exe, a main w pozostałych platformach) oraz, jeśli używamy CMD, plik zawierający informacje debugera o rozszerzeniu .pdb. W tym miejscu uruchamiamy plik main.exe lub main w następujący sposób:
Jeśli nasz plik main.rs zawierał nasz program Hello, world!, to na terminalu pojawi się taki właśnie napis.
Osoby, które znają dynamiczny język jak Ruby, Python lub JavaScript, mogą nie być przyzwyczajone do tego, że kompilacja i uruchamianie programu to dwa oddzielne kroki. Rust jest kompilowanym językiem programowania. Oznacza to, że możemy skompilować program i przekazać komuś wersję wykonywalną, aby osoba ta mogła uruchomić program bez instalacji języka Rust. Jeśli damy komuś plik .rb, .py lub .js, to musi on mieć zainstalowaną implementację języka Ruby, Python lub JavaScript. W tych językach potrzebujemy tylko jednego polecenia, aby skompilować i uruchomić nasz program. Wszystko jest kompromisem w projekcie języka.
Sama kompilacja za pomocą rustc wystarcza dla prostszych programów, ale w miarę jak nasz projekt się rozrasta, będziemy chcieli zarządzać wszystkimi opcjami i łatwo dzielić się kodem. Omówimy teraz narzędzie Cargo, które pomoże nam pisać prawdziwe programy w języku Rust.
Witaj Cargo!
Cargo to wbudowany w Rust menedżer systemu i pakietów. Większość Rustarian używa tego narzędzia, aby zarządzać swoimi projektami w języku Rust, gdyż Cargo obsługuje za nas wiele zadań, takich jak tworzenie kodu, pobieranie bibliotek, od których nasz kod jest zależny, oraz tworzenie tych bibliotek. (Biblioteki, których potrzebuje nasz kod, nazywamy zależnościami).
Najprostsze programy w języku Rust, jak ten, który już napisaliśmy, nie mają żadnych zależności. Tak więc, gdybyśmy utworzyli nasz projekt Hello, world! za pomocą Cargo, obsługuje tworzenie kodu. W miarę pisania coraz bardziej skomplikowanych programów w języku Rust będziemy dodawać zależności, a jeśli zaczniemy projekt przy użyciu Cargo, dodawanie zależności stanie się dużo łatwiejsze.
Ponieważ większość projektów w Rust wykorzystuje Cargo, w książce także go używamy. Cargo jest instalowane razem z Rust, więc ma go każdy, kto użył oficjalnych instalatorów omówionych w podrozdziale „Instalacja” w rozdz. 1. Jeśli instalowaliśmy Rusta za pomocą innych środków, trzeba sprawdzić, czy Cargo jest zainstalowane, wprowadzając z terminalu następujące polecenie:
Jeśli zobaczymy numer wersji, to go mamy! Jeśli pojawi się błąd, jak command not found (nie znaleziono polecenia), to musimy znaleźć w dokumentacji sposób oddzielnej instalacji Cargo.
Tworzenie projektu za pomocą Cargo
Utwórzmy nowy projekt, korzystając z Cargo, i popatrzmy, czym się różni od naszego oryginalnego projektu Hello, world!. Cofnijmy się do naszego katalogu projects (lub innego, w którym zdecydowaliśmy się przechowywać nasz kod). Następnie w dowolnym systemie operacyjnym wykonajmy następujące polecenia:
Pierwsze polecenie tworzy nowy wykonywalny plik binarny o nazwie hello_cargo. Argument --bin przekazany do cargo new tworzy nową wykonywalną aplikację (często nazywaną po prostu binarną), w przeciwieństwie do biblioteki. Nasz projekt nazwaliśmy hello_cargo, a Cargo tworzy swoje pliki katalogu o tej samej nazwie.
Wejdźmy do katalogu hello_cargo i pokażmy listę plików. Zobaczymy, że Cargo wygenerował dwa pliki i jeden katalog: plik Cargo.toml oraz katalog src zawierający wewnątrz plik main.rs. Zainicjowane zostało także nowe repozytorium Git wraz z plikiem gitignore.
UWAGA
Git to popularny system kontroli wersji. Możemy zmienić cargo new tak, aby używało innego systemu kontroli wersji za pomocą flagi --vcs. Uruchommy cargo new --help tak, aby zobaczyć dostępne opcje.
W naszym edytorze tekstów otwieramy Cargo.toml. Plik powinien wyglądać podobnie jak kod na listingu 1.2.
Listing 1.2. Zawartość pliku Cargo.toml wygenerowana przez polecenie cargo new
Plik ten jest zapisany w formacie TOML (Tom’s Obvious, Minimal Language), który jest formatem konfiguracyjnym Cargo.
Pierwszy wiersz, , to nagłówek części, który wskazuje, że kolejne instrukcje stanowią pakiet konfiguracyjny. W miarę jak będziemy dodawać więcej informacji do tego pliku, pojawią się inne części.
Kolejne trzy wiersze określają informacje konfiguracyjne potrzebne Cargo do skompilowania naszego programu: nazwę, wersję i autora. Cargo otrzymuje nasze nazwisko (nazwę) oraz e-mail z naszego środowiska pracy, więc jeśli podane informacje są niepoprawne, to musimy je w tym momencie poprawić, a następnie zapisać plik.
Ostatni wiersz, , to początek części, w której podajemy listę ewentualnych elementów zależnych naszego projektu. W języku Rust pakiety kodu są określane jako skrzynki (crates). W tym projekcie nie potrzebujemy żadnych dodatkowych skrzynek, ale w pierwszym projekcie z rozdziału 2 wykorzystamy część dependencies. Otwórzmy teraz plik src/main.rs i przyjrzyjmy mu się:
Cargo wygenerował nam program Hello, world! taki jak ten, który napisaliśmy w listingu 1.1. Jak dotąd różnica między poprzednim projektem a projektem generowanym przez Cargo polega na tym, że Cargo umieścił kod w katalogu src oraz że w katalogu głównym mamy plik konfiguracyjny Cargo.toml. Cargo oczekuje, że nasze pliki źródłowe będą się znajdowały w katalogu src. Katalog na najwyższym poziomie projektu jest przeznaczony tylko na pliki README, informacje o licencji, pliki konfiguracyjne i wszystkie inne elementy, które nie są związane z naszym kodem. Cargo pomaga w organizowaniu naszych projektów. Wszystko ma tu swoje miejsce i my mamy miejsce na wszystko.
Jeśli rozpoczęliśmy projekt, który nie używa Cargo, jak to zrobiliśmy przy naszym projekcie Hello, world!, możemy go przekształcić na projekt z użyciem Cargo. Kod projektu przenosimy do katalogu src i tworzymy odpowiedni plik Cargo.toml.
Tworzenie i uruchamianie projektu w Cargo
Popatrzmy teraz, jakie będą różnice po utworzeniu i uruchomieniu programu Hello, world! za pomocą Cargo! Z naszego katalogu hello_cargo budujemy nasz projekt, wprowadzając następujące polecenie:
Polecenie to tworzy plik wykonywalny w target/debug/hello_cargo (lub w target\debug\hello_cargo.exe w systemie Windows) zamiast korzystania z naszego bieżącego katalogu. Plik wykonywalny możemy uruchomić za pomocą następującego polecenia:
Jeśli wszystko pójdzie dobrze, Hello, world! powinien wyświetlić tekst na terminalu. Pierwsze uruchomienie programu Cargo spowoduje też, że Cargo utworzy nowy plik Cargo.lock w katalogu na najwyższym poziomie. Plik ten przechowuje dokładne wersje zależności z naszego projektu. Ten projekt nie ma zależności, więc plik jest prawie pusty. Nie musimy zmieniać go ręcznie, gdyż Cargo zarządza jego zawartością.
My tworzymy tylko projekt za pomocą cargo build i uruchamiamy za pomocą ./target/debug/hello_cargo, ale możemy też użyć cargo run, aby skompilować kod, a następnie uruchomić wynikowy plik wykonywalny w jednym poleceniu:
Tym razem nie widzimy wyników wskazujących, że w kompilacji hello_cargo pomaga Cargo. Cargo określił, że pliki nie uległy zmianie, więc uruchomił wersję binarną. Gdybyśmy zmodyfikowali nasz kod źródłowy, Cargo przed uruchomieniem przebudowałby projekt, a wtedy wyniki miałyby postać jak niżej:
Cargo zapewnia także polecenie o nazwie cargo check. Polecenie to szybko sprawdza nasz kod, aby się upewnić, że można go skompilować, ale nie wykonuje pliku wykonywalnego:
Dlaczego nie tworzyć pliku wykonywalnego? cargo check często jest znacznie szybsze od cargo build, gdyż pomija krok tworzenia pliku wykonywalnego. Jeśli podczas pisania kodu stale sprawdzamy swoją pracę, korzystanie z cargo check przyspieszy ten proces! Dlatego wielu Rustarian co jakiś czas uruchamia cargo check podczas pisania programu, aby mieć pewność, że uda się go skompilować. Uruchamiają cargo build dopiero wtedy, gdy są gotowi do użycia wersji wykonywalnej.
Podsumujmy więc, czego dowiedzieliśmy się o Cargo:
- możemy tworzyć projekt za pomocą cargo build lub cargo check;
- możemy utworzyć i uruchomić projekt w jednym kroku za pomocą cargo run;
- zamiast zapisywania wyników kompilacji w tym samym katalogu co nasz kod, Cargo przechowuje go w katalogu target/debug.
Dodatkową zaletą korzystania z Cargo jest fakt, że polecenia są takie same, niezależnie od tego, w jakim systemie operacyjnym pracujemy. Dlatego nie będą już podawane instrukcje specyficzne dla systemów Linux i macOS oraz dla systemu Windows.
Tworzenie gotowej wersji
Gdy nasz projekt jest wreszcie gotowy, możemy użyć polecenia cargo build --release, aby skompilować go z optymalizacją. To polecenie utworzy plik wynikowy w katalogu target/release zamiast target/debug. Optymalizacja powoduje, że kod w języku Rust działa szybciej, ale włączenie jej wydłuża czas potrzebny na skompilowanie programu. Dlatego mamy dwa różne profile: jeden na potrzeby tworzenia programu, gdy chcemy szybko ponownie budować program, i często drugi do tworzenia końcowej wersji programu, który przekażemy użytkownikowi i który nie będzie wielokrotnie przebudowywany, a za to będzie działał tak szybko jak to tylko możliwe. Jeśli robimy badania porównawcze (benchmarking) czasu działania naszego kodu, koniecznie musimy wykorzystywać cargo build --release i zrobić badanie porównawcze dla pliku wykonywalnego z katalogu target/release.
Cargo jako konwencja
Przy prostych projektach Cargo nie daje wielkich korzyści w stosunku do korzystania z rustc, ale będzie się sprawdzał w miarę jak nasze programy staną się bardziej skomplikowane. Przy projektach złożonych z wielu modułów znacznie prostsze jest zrzucenie na Cargo koordynacji budowy wyniku.
Wprawdzie projekt hello_cargo jest prosty, wykorzystuje jednak wiele narzędzi, z których będziemy korzystać w dalszej części naszej kariery z językiem Rust. W istocie praca przy jakimkolwiek istniejącym projekcie pozwala na korzystanie z podanych niżej poleceń, aby sprawdzić kod za pomocą Git, zmienić katalog zawierający projekt oraz na budowę wyniku:
Więcej informacji na temat Cargo można znaleźć w dokumentacji pod adresem https://doc.rust-lang.org/cargo/.
Podsumowanie
Świetnie rozpoczęliśmy naszą podróż z językiem Rust! W tym rozdziale nauczyliśmy się, jak:
- instalować najnowszą stabilną wersję języka Rust za pomocą rustup;
- aktualizować Rust do nowszej wersji;
- otwierać zainstalowaną lokalnie dokumentację;
- pisać i uruchamiać program Hello, world! bezpośrednio za pomocą rustc;
- tworzyć i uruchamiać nowy projekt, wykorzystując konwencje Cargo.
To dobry moment na budowę bardziej konkretnego programu, aby przyzwyczaić się do czytania i zapisu w kodzie języka Rust. Dlatego w rozdziale 2 zbudujemy program zgadywankę. Jeśli ktoś od razu chce przejść do opisu działania popularnych pojęć programowania w języku Rust, może najpierw przeczytać rozdział 3, a potem wrócić do rozdziału 2.