Report is mostly done now, fine tuning left

This commit is contained in:
Marko Korhonen 2020-05-08 09:35:47 +03:00
parent fb5aa5ee5a
commit 06b71b8688
No known key found for this signature in database
GPG key ID: 911B85FBC6003FE5
21 changed files with 533 additions and 225 deletions

View file

@ -8,13 +8,40 @@
lastchecked = {5. Maaliskuuta, 2020} lastchecked = {5. Maaliskuuta, 2020}
} }
@article{wiki:dom,
title ={Document Object Model},
url = {https://en.wikipedia.org/wiki/Document_Object_Model},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {4. toukokuuta 2020},
}
@article{wiki:spa,
title ={Single-page application},
url = {https://en.wikipedia.org/wiki/Single-page_application},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {4. toukokuuta 2020},
}
@article{wiki:rest,
title ={Representational state transfer},
url = {https://en.wikipedia.org/wiki/Representational_state_transfer},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {4. toukokuuta 2020},
}
@article{wiki:actor, @article{wiki:actor,
title = {Actor model}, title = {Actor model},
journal = {}, journal = {},
author = {Wikipedia contributors}, author = {Wikipedia contributors},
year = 2020, year = 2020,
url = {https://en.wikipedia.org/wiki/Actor_model}, url = {https://en.wikipedia.org/wiki/Actor_model},
lastchecked = {16. Huhtikuuta, 2020} lastchecked = {16. huhtikuuta, 2020}
} }
@article{rust:lang, @article{rust:lang,
@ -23,7 +50,7 @@
author = {Rust Team}, author = {Rust Team},
year = 2020, year = 2020,
url = {https://www.rust-lang.org}, url = {https://www.rust-lang.org},
lastchecked = {24. Huhtikuuta, 2020} lastchecked = {24. huhtikuuta, 2020}
} }
@article{rust:ownership, @article{rust:ownership,
@ -32,7 +59,16 @@
author = {Rust Team}, author = {Rust Team},
year = 2020, year = 2020,
url = {https://doc.rust-lang.org/stable/book/ch04-01-what-is-ownership.html}, url = {https://doc.rust-lang.org/stable/book/ch04-01-what-is-ownership.html},
lastchecked = {24. Huhtikuuta, 2020} lastchecked = {24. huhtikuuta, 2020}
}
@article{rust:fearless-concurrency,
title = {What is Ownership - Rust book},
journal = {Rust Blog},
author = {Aaron Turon},
year = 2015,
url = {https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html},
lastchecked = {7. toukokuuta, 2020}
} }
@article{rust:println, @article{rust:println,
@ -41,7 +77,7 @@
author = {Rust Team}, author = {Rust Team},
year = 2020, year = 2020,
url = {https://doc.rust-lang.org/std/macro.println.html}, url = {https://doc.rust-lang.org/std/macro.println.html},
lastchecked = {3. Toukokuuta, 2020} lastchecked = {3. toukokuuta, 2020}
} }
@article{wiki:garbagecollection, @article{wiki:garbagecollection,
@ -50,7 +86,7 @@
author = {Wikipedian kirjoittajat}, author = {Wikipedian kirjoittajat},
year = 2020, year = 2020,
url = {https://fi.wikipedia.org/wiki/Automaattinen_roskienker%C3%A4ys}, url = {https://fi.wikipedia.org/wiki/Automaattinen_roskienker%C3%A4ys},
lastchecked = {24. Huhtikuuta, 2020} lastchecked = {24. huhtikuuta, 2020}
} }
@misc{rust:cargo, @misc{rust:cargo,
@ -59,7 +95,16 @@
publisher = {}, publisher = {},
author = {Rust Team}, author = {Rust Team},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
}
@misc{rust:install,
title = {Rust - Getting started},
url = {https://www.rust-lang.org/learn/get-started},
publisher = {},
author = {Rust Team},
year = 2020,
lastchecked = {1. toukokuuta 2020},
} }
@misc{rust:macro-lisp, @misc{rust:macro-lisp,
@ -68,7 +113,7 @@
publisher = {}, publisher = {},
author = {JunSuzukiJapan}, author = {JunSuzukiJapan},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
} }
@misc{rust:cratesio, @misc{rust:cratesio,
@ -77,7 +122,7 @@
publisher = {}, publisher = {},
author = {Rust Team}, author = {Rust Team},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
} }
@misc{jwt:home, @misc{jwt:home,
@ -86,7 +131,7 @@
publisher = {}, publisher = {},
author = {Auth0}, author = {Auth0},
year = 2020, year = 2020,
lastchecked = {29. Huhtikuuta 2020}, lastchecked = {29. huhtikuuta 2020},
} }
@misc{yew:home, @misc{yew:home,
@ -95,7 +140,7 @@
publisher = {}, publisher = {},
author = {Yew Team}, author = {Yew Team},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
} }
@misc{yew:router-problem, @misc{yew:router-problem,
@ -104,7 +149,7 @@
publisher = {}, publisher = {},
author = {Yew Team}, author = {Yew Team},
year = 2020, year = 2020,
lastchecked = {3. Toukokuuta 2020}, lastchecked = {3. toukokuuta 2020},
} }
@misc{webassembly:stdweb, @misc{webassembly:stdweb,
@ -113,7 +158,7 @@
publisher = {}, publisher = {},
author = {Rust team}, author = {Rust team},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
} }
@misc{webassembly:websys, @misc{webassembly:websys,
@ -122,7 +167,7 @@
publisher = {}, publisher = {},
author = {Rust team}, author = {Rust team},
year = 2020, year = 2020,
lastchecked = {1. Toukokuuta 2020}, lastchecked = {1. toukokuuta 2020},
} }
@misc{uikit, @misc{uikit,
@ -131,7 +176,7 @@
publisher = {}, publisher = {},
author = {}, author = {},
year = 2020, year = 2020,
lastchecked = {2. Toukokuuta 2020}, lastchecked = {2. toukokuuta 2020},
} }
@misc{webassembly:home, @misc{webassembly:home,
@ -140,16 +185,24 @@
publisher = {}, publisher = {},
author = {}, author = {},
year = 2020, year = 2020,
lastchecked = {2. Toukokuuta 2020}, lastchecked = {2. toukokuuta 2020},
} }
@misc{webassembly:text, @misc{webassembly:text,
title ={Understanding WebAssembly text format}, title ={Understanding WebAssembly text format},
url = {https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format}, url = {https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format},
publisher = {}, publisher = {Mozilla},
author = {Mozilla}, author = {MDN contributors},
year = 2019, year = 2019,
lastchecked = {2. Toukokuuta 2020}, lastchecked = {2. toukokuuta 2020},
}
@article{s-expression,
title ={S-expression},
url = {https://en.wikipedia.org/wiki/S-expression},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {7. toukokuuta 2020},
} }
@article{wiki:metaprogramming, @article{wiki:metaprogramming,
@ -158,6 +211,68 @@
journal = {}, journal = {},
author = {Wikipedia contributors}, author = {Wikipedia contributors},
year = 2020, year = 2020,
lastchecked = {3. Toukokuuta 2020}, lastchecked = {3. toukokuuta 2020},
} }
@article{wiki:runtime-library,
title ={Runtime library},
url = {https://en.wikipedia.org/wiki/Runtime_library},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {7. toukokuuta 2020},
}
@article{wiki:thread-safety,
title = {Thread safety},
url = {https://en.wikipedia.org/wiki/Thread_safety},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {7. toukokuuta 2020},
}
@misc{typescript,
title ={TypeScript: Typed JavaScript at Any Scale},
url = {https://www.typescriptlang.org},
publisher = {Microsoft},
author = {TypeScript team},
year = 2020,
lastchecked = {3. toukokuuta 2020},
}
@misc{javascript-wa,
title ={Using the WebAssembly JavaScript API},
url = {https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API},
publisher = {Mozilla},
author = {MDN contributors},
year = 2019,
lastchecked = {3. toukokuuta 2020},
}
@article{wiki:sop,
title ={Same-origin-policy},
url = {https://en.wikipedia.org/wiki/Same-origin_policy},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {4. toukokuuta 2020},
}
@article{wiki:cors,
title ={Cross-origin resource sharing},
url = {https://en.wikipedia.org/wiki/Cross-origin_resource_sharing},
journal = {},
author = {Wikipedia contributors},
year = 2020,
lastchecked = {4. toukokuuta 2020},
}
@misc{sourcecode,
title ={Insinöörityön lähdekoodi},
url = {https://gitlab.com/ReekyMarko/thesis},
publisher = {},
author = {Marko Korhonen},
year = 2020,
lastchecked = {8. toukokuuta 2020},
}

View file

@ -3,14 +3,40 @@
% Acronyms, abbreviations, etc. % Acronyms, abbreviations, etc.
\newacronym{html}{HTML}{HyperText Markup Language} \newacronym{html}{HTML}{HyperText Markup Language. Verkossa käytettävä tiedostomuoto.}
\newacronym{php}{PHP}{Hypertext Preprocessor} \newacronym{http}{HTTP}{
\newacronym{orm}{ORM}{Object-relational mapping} Hypertext Transfer Protocol. Alunperin HTML-dokumenttien välittämiseen kehitetty protokolla. Nykyään käytetään paljon muuhunkin
\newacronym{json}{JSON}{JavaScript Object Notation} }
\newacronym{jwt}{JWT}{JSON Web Token} \newacronym{php}{PHP}{
\newacronym{rest}{REST}{Representational state transfer} Hypertext Preprocessor. HTML-dokumenttien muokkaamiseen palvelimella käytetty ohjelmointikieli
\newacronym{api}{API}{Application Programming Interface} }
\newacronym{css}{CSS}{Cascading Style Sheets} \newacronym{orm}{ORM}{
Object-relational mapping. Ohjelman objektien sovittamista relaatiotietokannan tauluihin ja tietokannasta takaisin ohjelmaan
}
\newacronym{javascript}{JavaScript}{
Web-kehityksessä suosittu ohjelmointikieli
}
\newacronym{json}{JSON}{
JavaScript Object Notation. Avoimen standardin tiedostomuoto, joka muistuttaa JavaScript-objekteja
}
\newacronym{jwt}{JWT}{
JSON Web Token. Avoin standardi käyttöoikeuksien hallinnoimiseen
}
\newacronym{rest}{REST}{
Representational state transfer. Rajapintojen rakentamiseen tehty malli
}
\newacronym{api}{API}{
Application Programming Interface. Ohjelmointirajapinta
}
\newacronym{css}{CSS}{
Cascading Style Sheets. HTML-dokumenttien ulkonäön määrittämiseen tarkoitetty kieli
}
\newacronym{sop}{SOP}{
Same-origin policy. Turvallisuusmalli, missä asiakaspuolen ohjelman ei anneta noutaa resursseja muaalta kuin samalta palvelimelta mistä ohjelma on noudettu
}
\newacronym{cors}{CORS}{
Cross-Origin Resource Sharing. Mekanismi resurssien noutamiselle eri lähteistä
}
% Glossary entries % Glossary entries
@ -26,9 +52,4 @@
description={Ohjelman palvelimella suoritettava osa, johon asiakaspuolen ohjelmisto yhdistyy} description={Ohjelman palvelimella suoritettava osa, johon asiakaspuolen ohjelmisto yhdistyy}
} }
\newglossaryentry{Object-relational mapping}{
name={ORM},
description={Ohjelman objektien sovittamista relaatiotietokannan tauluihin ja tietokannasta takaisin ohjelmaan}
}
\glsaddall \glsaddall

View file

@ -30,7 +30,11 @@
\multicolumn{2}{|p{15cm}|}{\vspace{-22pt} \multicolumn{2}{|p{15cm}|}{\vspace{-22pt}
The purpose of this thesis was to evaluate the suitability of the Rust programming language for web programming.\newline The purpose of this thesis was to evaluate the suitability of the Rust programming language for web programming.\newline
During the thesis, a project was carried out, in which both the frontend and backend was written in Rust. Over the course of the report, both the basics of Rust and web programming are reviewed. The reader is expected to know only basic things, as all of the relevant concepts are explained in great detail.\newline
During the thesis, a project was carried out, in which both the frontend and backend was written in Rust. Based on this project, the suitability of Rust as a web programming language was analyzed. The setup of the development environment needed in developing Rust is also thoroughly explained. \newline
The practical knownledge acquired during the development of the project is applied to assessing the suitability of Rust as a web programming language. This is done separately concerning client side and server side. In the conclusion, the point of view is focused in the future and how this conclusion might change in the next few years. There's also practical tips on how one could apply Rust to new and existing web programming projects.
} \\[14cm] \hline } \\[14cm] \hline
Keywords & \metropoliakeywords Keywords & \metropoliakeywords
\\ \hline \\ \hline

View file

@ -28,9 +28,14 @@
Ohjaaja(t) & \ohjaajat Ohjaaja(t) & \ohjaajat
\\ \hline \\ \hline
\multicolumn{2}{|p{15cm}|}{\vspace{-22pt} \multicolumn{2}{|p{15cm}|}{\vspace{-22pt}
Tämän opinnäytetyön tavoitteena on selvittää Rust-ohjelmointikielen soveltuvuutta web-ohjelmointiin.\newline Tämän insinöörityön tavoitteena oli selvittää Rust-ohjelmointikielen soveltuvuutta web-ohjelmointiin.\newline
Insinöörityössä käydään läpi web-ohjelmoinnin perusteet, sekä Rustin pääominaisuudet. Lukijalta oletetaan hyvin vähän tietämystä ja kaikki olennaiset käsitteet käydään läpi perusteista alkaen.\newline
Insinöörityön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin molemmat Rustilla. Tämän pohjalta arvioitiin kielen soveltuvuutta web-ojelmointiin. Projektin kaikki osa-alueet ja kaikki käytetyt riippuvaisuudet, sekä syyt niiden valitsemiseen on käyty läpi perusteellisesti. Myös kehitysympäristön asentaminen ja projektin aloittaminen on käyty läpi alusta alkaen.\newline
Projektista kerätyn käytännön tiedon avulla on arvioitu Rustin soveltuvuutta web-ohjelmointiin erikseen sekä asiakas- että palvelinpuolella. Lopuksi on siirretty katsetta hieman tulevaan ja arvioitu, miten soveltuvuus tulee todennäköisesti muuttumaan tulevaisuudessa. Lopussa on myös suosituksia Rustin sisällyttämisestä uusiin ja olemassa oleviin web-ohjelmointiprojekteihin.
Opinnäytetyön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin molemmat Rustilla. Tämän pohjalta arvioitiin kielen soveltuvuutta web-ojelmointiin.
} \\[14cm] \hline } \\[14cm] \hline
Avainsanat & \avainsanat Avainsanat & \avainsanat
\\ \hline \\ \hline

View file

@ -16,12 +16,12 @@
%Finnish section, for title/abstract %Finnish section, for title/abstract
\def\otsikko{Rust web-ohjelmointikielenä} \def\otsikko{Rust web-ohjelmointikielenä}
\def\tutkinto{Insinööri (AMK)} % change to your needs, e.g. "YAMK", etc. \def\tutkinto{Insinööri (AMK)} % change to your needs, e.g. "YAMK", etc.
\def\kohjelma{Tieto\textendash ja viestintätekniikka} \def\kohjelma{Tieto- ja tietoviestintätekniikka}
\def\suuntautumis{Ohjelmistotuotanto} \def\suuntautumis{Ohjelmistotuotanto}
\def\ohjaajat{ \def\ohjaajat{
Simo Silander Simo Silander, tutkintovastaava
} }
\def\avainsanat{avainsanat} \def\avainsanat{Rust, web, ohjelmointi, fullstack}
\def\pvm{\specialdate\today} \def\pvm{\specialdate\today}
%English section, for title/abstract %English section, for title/abstract

View file

@ -0,0 +1,22 @@
\chapter{Web-ohjelmointi}
Web-ohjelmoinnilla tarkoitetaan nettiselaimen välityksellä käytettävien ohjelmien ohjelmoimista. Web-ohjelma jaetaan useimmiten kahteen osaan: asiakaspuoleen (eng. client side) ja palvelinpuoleen (eng. server side).
\section{Asiakaspuoli}
\label{sect:asiakaspuoli}
Asiakaspuoli koostuu käyttäjän nettiselaimesta, missä käyttöliittymän rakenne määritetään HTML-dokumentissa. HTML-dokumenttia voi muokata käyttämällä DOM:ia \cite{wiki:dom}. DOM, eli dokumenttioliomalli on tapa kuvata HTML-dokumenttia puurakenteena, joka mahdollistaa dokumentin elementtien lukemisen ja muokkaamisen. Muokkaaminen suoritetaan asiakaspuolen web-ohjelmointikielellä, johon on tällä hetkellä kaksi vaihtoehtoa: JavaScript sekä WebAssembly, mitä tarkastellaan tässä opinnäytetyössä tarkemmi.
Käyttöliittymän ulkonäköä ja toiminnallisuutta voi myös muokata palvelinpuolella. Tätä kutsutaan palvelinpuolen renderöinniksi ja siihen useimmiten käytetty ohjelmointikieli on PHP. Palvelinpuolen renderöinti on kuitenkin jäänyt taka-alalle yhden sivun ohjelmien yleistyessä.
\subsection{Yhden sivun ohjelmat}
\label{sect:spa}
Yhden sivun ohjelmat \cite{wiki:spa} ovat tapa toteuttaa asiakaspuolen ohjelmia, missä selain lataa yhden ohjelman ja HTML-dokumentin, minkä jälkeen sivujen uudelleenlatauksia ei enää tapahdu. HTML-dokumentin DOM:ia muutetaan niin sanotusti lennosta.
Ohjelman tilaa hallinnoidaan usein lukemalla ja muuttamalla asiakkaan nettiselaimen osoitekenttää. Tätä kutsutaan asiakaspuolen reititykseksi. Reititys mahdollistaa myös tiettyyn ohjelman tilaan siirtymiseen suoraan, mikä on hyödyllistä esimerkiksi silloin, kun sovelluksen käyttäjillä on tarve jakaa linkkejä ohjelmaan.
\section{Palvelinpuoli}
Palvelinpuolella tarkoitetaan ohjelman osaa, mihin asiakaspuoli yhdistyy noutaakseen tai tallettaakseen tietoa. Palvelinpuolen ohjelmointikielissä on huomattavasti enemmän vaihtoehtoja, sillä palvelin kommunikoi asiakkaan kanssa käyttämällä HTTP-protokollaa, johon löytyy kirjastoja lähes jokaiselle ohjelmointikielelle.
\subsection{REST}
Palvelinpuoleen viitataan usein myös rajapintana (eng. Application Programming Interface, API), sillä tämänkaltaisia HTTP-protokollalla ohjattavia ohjelmia voi käyttää myös muuallakin kuin asiakaspuolen web-ohjelmissa. Tämänkaltaiset rajapinnat voivat myös yhdistyä toisiinsa internetin välityksellä ja näin jakaa tietoja eri palveluiden välillä.
Web-rajapintojen yleistyessä on kehitetty standardeja, minkä mukaan rajapintoja voi rakentaa. Näin rajapinnoista tulee helpompia käyttää kaikille. Yksi näistä standardeista on Representational state transfer (REST) \cite{wiki:rest}, jota pyrin käyttämään tämän opinnäytetyön projektia ohjelmoidessa.

View file

@ -1,7 +1,7 @@
\chapter{Kielet} \chapter{Kielet}
\section{Rust} \section{Rust}
Rust \cite{rust:lang} on Mozillan 2010 julkaisema ohjelmointikieli. Se on hyvin suorituskykyinen järjestelmätason ohjelmointikieli, muistuttaen monilta osin C- ja C++ -kieliä. Rustin tarkoituksena on säilyttää näiden vanhojen kielien suorituskyky, mutta kuitenkin tarjota samalla muun muassa vahva tyypitys ja taattu turvallinen rinnakaisajo. Lisäksi tyypilliset C-kielien muistinhallintaongelmat on pyritty ratkaisemaan käytännöillä, jotka ovat samalla tehokkaita suorituskyvyn näkökulmasta, mutta myös helppoja käyttää ohjelmoijalle. Rust \cite{rust:lang} on Mozillan 2010 julkaisema ohjelmointikieli. Se on hyvin suorituskykyinen järjestelmätason ohjelmointikieli, muistuttaen monilta osin C- ja C++ -kieliä. Rustin tarkoituksena on säilyttää näiden vanhojen kielien suorituskyky, mutta kuitenkin tarjota samalla muun muassa vahva tyypitys ja taattu turvallinen rinnakaisajo. Lisäksi tyypilliset C-kielen muistinhallintaongelmat on pyritty ratkaisemaan käytännöillä, jotka ovat samalla tehokkaita suorituskyvyn näkökulmasta, mutta myös helppoja käyttää ohjelmoijalle.
\subsection{Muistinhallinta} \subsection{Muistinhallinta}
@ -11,8 +11,6 @@ Automaattiselle roskienkeruulle on aikaisemmin ollut vaihtoehtona vain manuaalin
Rustin yhtenä pääominaisuutena on mainostettu sen uudenlaista näkökulmaa muistinhallintaan: omistajuutta \cite{rust:ownership}. Siinä jokaisella arvolla on omistaja, ja kun omistaja menee näkyvyysalueen ulkopuolelle, niin menevät myös sen omistamat arvotkin, eli ne vapautetaan muistista. Arvojen omistajuutta voi siirtää joko pysyvästi tai väliaikaisesti lainaamalla. Rustin yhtenä pääominaisuutena on mainostettu sen uudenlaista näkökulmaa muistinhallintaan: omistajuutta \cite{rust:ownership}. Siinä jokaisella arvolla on omistaja, ja kun omistaja menee näkyvyysalueen ulkopuolelle, niin menevät myös sen omistamat arvotkin, eli ne vapautetaan muistista. Arvojen omistajuutta voi siirtää joko pysyvästi tai väliaikaisesti lainaamalla.
\clearpage
\begin{code} \begin{code}
\inputminted{Rust}{code/ownership.rs} \inputminted{Rust}{code/ownership.rs}
\captionof{listing}{Omistajuus Rustissa} \captionof{listing}{Omistajuus Rustissa}
@ -21,6 +19,8 @@ Rustin yhtenä pääominaisuutena on mainostettu sen uudenlaista näkökulmaa mu
Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Funktion suorittamisen jälkeen se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella: Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Funktion suorittamisen jälkeen se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella:
\clearpage
\begin{code} \begin{code}
\inputminted{Rust}{code/borrow.rs} \inputminted{Rust}{code/borrow.rs}
\captionof{listing}{Lainaus} \captionof{listing}{Lainaus}
@ -29,10 +29,8 @@ Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name'' -arvon tu
Koodiesimerkissä \ref{code:rust:borrow} omistajuuden siirtäminen on korvattu lainauksella. Tämä muutos pitää tehdä sekä funktion parametrien määritykseen riville 1, että funktion kutsuun riville 7. Lainauksessa arvon omistajuus säilyy nykyisellään ja lainaaja antaa itse arvon sijasta viitteen (eng. reference). Viite on osoitin, joka osoittaa samaan muistipaikkaan kuin missä alkuperäinen arvo on. Lainaaminen tehdään käyttämällä merkkiä ''\char`&''. Koodiesimerkissä \ref{code:rust:borrow} omistajuuden siirtäminen on korvattu lainauksella. Tämä muutos pitää tehdä sekä funktion parametrien määritykseen riville 1, että funktion kutsuun riville 7. Lainauksessa arvon omistajuus säilyy nykyisellään ja lainaaja antaa itse arvon sijasta viitteen (eng. reference). Viite on osoitin, joka osoittaa samaan muistipaikkaan kuin missä alkuperäinen arvo on. Lainaaminen tehdään käyttämällä merkkiä ''\char`&''.
\clearpage
\subsection{Vahva tyypitys} \subsection{Vahva tyypitys}
Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvojen tyypit pitää olla tiedossa ohjelman kokoamisen aikana. Tähän sisältyy myös funktioiden parametrit ja paluuarvot. Usein Rustin kääntäjä osaa päätellä (eng. inference) arvojen tyypit itse, varsinkin yksinkertaisissa tapauksissa: Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvojen tyypit pitää olla tiedossa ohjelman kääntämisen aikana. Tähän sisältyy myös funktioiden parametrit ja paluuarvot. Usein Rustin kääntäjä osaa päätellä (eng. inference) arvojen tyypit itse, varsinkin yksinkertaisissa tapauksissa:
\begin{code} \begin{code}
\inputminted{Rust}{code/type-inference.rs} \inputminted{Rust}{code/type-inference.rs}
@ -40,48 +38,51 @@ Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvoje
\label{code:rust:inference} \label{code:rust:inference}
\end{code} \end{code}
Niissä tapauksissa joissa tyypille voi olla useita vaihtoehtoja, tai silloin jos arvon määrityksen yhteydessä tapahtuu konversio, ohjelmoijan tulee määrittää tyyppi. Tyypin voi määrittää näin: Niissä tapauksissa, joissa tyypille voi olla useita vaihtoehtoja, tai silloin jos arvon määrityksen yhteydessä tapahtuu konversio, ohjelmoijan tulee määrittää tyyppi. Tyypin voi määrittää näin:
\begin{code} \begin{code}
\inputminted{Rust}{code/type-notation.rs} \inputminted{Rust}{code/type-notation.rs}
\captionof{listing}{Tyypin merkintä} \captionof{listing}{Tyypin merkintä}
\label{code:rust:type-notation} \label{code:rust:type-notation}
\end{code} \end{code}
Valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi myös karkeasti rajata funktion parametrejen arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota joka ottaa parametrina henkilön iän, u8 on hyvä valinta koska ihmisen ikä ei voi olla negatiivinen ja ihmiset myös harvoin elävät yli 255 vuotta. \clearpage
Edellä mainittu tyyppi u8 on niin kutsuttu allekirjoittamaton kokonaisluku (eng. \textbf{u}nsigned). Allekirjoitetuissa kokonaisluvuissa (eng. signed) käytetään yksi bitti merkkaamaan sitä, onko luku positiivinen vai negatiivinen. Allekirjoittamattamattomissa luvuissa tätä ei tehdä, joten luku voi olla hieman isompi kuin allekirjoitettu luku, mutta se ei voi olla negatiivinen. Valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi myös karkeasti rajata funktion parametrejen arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota joka ottaa parametrina henkilön iän, u8 on hyvä valinta koska ihmisen ikä ei voi olla negatiivinen ja yksikään ihminen tuskin elää yli 255 vuotta.
Edellä mainittu tyyppi u8 on niin kutsuttu etumerkitön kokonaisluku (eng. \textbf{u}nsigned integer). Etumerkillisissä kokonaisluvuissa (eng. signed integer) käytetään yksi bitti merkkaamaan sitä, onko luku positiivinen vai negatiivinen. Etumerkittömissä luvuissa tätä ei tehdä, joten luku voi olla hieman isompi kuin etumerkillinen luku, mutta se ei voi olla negatiivinen.
\def\2^#1{\hbox to1.6em{$2^{#1}$\hss}}
\begin{table}[h!] \begin{table}[h!]
\label{tab:table1} \label{tab:integers}
\begin{center} \begin{center}
\begin{minipage}{2.5in} \begin{minipage}{2.5in}
\begin{tabular}{|c|c|c|} \begin{tabular}{|l|c|r|}
\hline \hline
\multicolumn{3}{|c|}{\textbf{Allekirjoittamaton}} \\ \multicolumn{3}{|c|}{\textbf{Etumerkitön}} \\
\hline \hline
Tyyppi & Minimi & Maksimi \\ Tyyppi & Minimi & Maksimi \\
\hline \hline
u8 & 0 & $ 2^{8}-1 $\\ u8 & 0 & $ \2^{8} -1 $\\
u16 & 0 & $ 2^{16}-1 $\\ u16 & 0 & $ \2^{16}-1 $\\
u32 & 0 & $ 2^{32}-1 $\\ u32 & 0 & $ \2^{32}-1 $\\
u64 & 0 & $ 2^{64}-1 $\\ u64 & 0 & $ \2^{64}-1 $\\
u128 & 0 & $ 2^{128}-1 $\\ u128 & 0 & $ \2^{128}-1 $\\
\hline \hline
\end{tabular} \end{tabular}
\end{minipage} \end{minipage}
\begin{minipage}{2in} \begin{minipage}{2.5in}
\begin{tabular}{|c|c|c|} \begin{tabular}{|l|c|r|}
\hline \hline
\multicolumn{3}{|c|}{\textbf{Allekirjoitettu}} \\ \multicolumn{3}{|c|}{\textbf{Etumerkillinen}} \\
\hline \hline
Tyyppi & Minimi & Maksimi \\ Tyyppi & Minimi & Maksimi \\
\hline \hline
i8 & $ -2^{7} $ & $ 2^{7}-1 $\\ i8 & $ -\2^{7} $ & $ \2^{7}-1 $\\
i16 & $ -2^{15} $ & $ 2^{15}-1 $\\ i16 & $ -\2^{15} $ & $ \2^{15}-1 $\\
i32 & $ -2^{31} $ & $ 2^{31}-1 $\\ i32 & $ -\2^{31} $ & $ \2^{31}-1 $\\
i64 & $ -2^{64} $ & $ 2^{63}-1 $\\ i64 & $ -\2^{63} $ & $ \2^{63}-1 $\\
i128 & $ -2^{127} $ & $ 2^{127}-1 $\\ i128 & $ -\2^{127} $ & $ \2^{127}-1 $\\
\hline \hline
\end{tabular} \end{tabular}
\end{minipage} \end{minipage}
@ -89,38 +90,51 @@ Edellä mainittu tyyppi u8 on niin kutsuttu allekirjoittamaton kokonaisluku (eng
\caption{Rustin kokonaislukutyypit ja niiden vaihteluvälit} \caption{Rustin kokonaislukutyypit ja niiden vaihteluvälit}
\end{table} \end{table}
\clearpage \bigskip
Toisin kuin C- ja C++ -kielissä, Rustissa on oletuksena etumerkittömien kokonaislukujen ylivuoto pois päältä. Tämä tarkoittaa sitä, että jos esimerkiksi 8-bittisen etumerkittömän kokonaisluvun (u8) arvo on 256, siitä tulee 0. Rustin kääntäjä siis ei anna tällaisen tapahtua vaan kääntämisen yhteydessä tulee virheviesti, jonka voi nähdä koodiesimerkissä \ref{code:rust:integer-overflow}.
\begin{code} \begin{code}
\inputminted{shell}{code/integer-overflow} \inputminted[linenos=false]{shell}{code/integer-overflow}
\captionof{listing}{Allekirjoittamattoman kokonaisluvun ylivuoto} \captionof{listing}{Etumerkittömän kokonaisluvun ylivuoto}
\label{code:rust:integer-overflow} \label{code:rust:integer-overflow}
\end{code} \end{code}
Toisin kuin C- ja C++ -kielissä, Rustissa on oletuksena allekirjoittamattomien kokonaislukujen ylivuoto pois päältä. Tämä tarkoittaa sitä, että jos 8-bittisen allekirjoittamattoman kokonaisluvun (u8) arvo on esimerkiksi 256, siitä tulee 0. Rustin kääntäjä siis ei anna tällaisen tapahtua vaan kääntämisen yhteydessä tulee virheviesti, jonka voi nähdä koodiesimerkissä \ref{code:rust:integer-overflow}.
\subsection{Muuttumaton data} \subsection{Muuttumaton data}
Rustissa kaikki arvot ovat oletuksena muuttumattomia (engl.\ immutable). Jos muuttumattoman datan sijasta tarvitsee muuttujia (engl. mutable), voi käyttää avainsanaa ''mut'', esimerkiksi \mintinline{Rust}{let mut name = "Marko"}. Myös lainaukset suoritetaan oletuksena muuttumattomasti ja muutettavan lainauksen voi tehdä samalla avainsanalla, esimerkiksi \mintinline{Rust}{say_hello(&mut name)} Rustissa kaikki arvot ovat oletuksena muuttumattomia (engl.\ immutable). Jos muuttumattoman datan sijasta tarvitsee muuttujia (engl. mutable), voi käyttää avainsanaa ''mut'', esimerkiksi \mintinline{Rust}{let mut name = "Marko"}. Myös lainaukset suoritetaan oletuksena muuttumattomasti ja muutettavan lainauksen voi tehdä samalla avainsanalla, esimerkiksi \mintinline{Rust}{say_hello(&mut name)}
Yleinen konsensus ohjelmoinnin maailmassa on se, että kaikki arvot, joita ei tarvitse muuttaa, pitäisi nimenomaan määrittää muuttumattomana. Tämä on tuttua kaikille, jotka ovat tutustuneet funktionaalisiin ohjelmointikieliin. Muuttumaton data on myös todella tärkeää rinnakkaisajossa, missä useampi prosessi suorittaa samoja funktioita ja käsittelee samoja arvoja samaan aikaan. Yleinen konsensus ohjelmoinnin maailmassa on se, että kaikki arvot, joita ei tarvitse muuttaa, pitäisi nimenomaan määrittää muuttumattomana. Tämä on tuttua kaikille, jotka ovat tutustuneet funktionaalisiin ohjelmointikieliin. Muuttumaton data on myös todella tärkeää rinnakkaisajossa, missä useampi prosessi suorittaa samoja funktioita ja käsittelee samoja arvoja samaan aikaan.
\subsection{Luotettavuus} \subsection{Luotettavuus}
Rustia kehitettäessä on aina ollut tavoitteena luotettavuus. Tämä tarkoittaa sitä, että ohjelman virheet huomataan jo koontivaiheessa, eikä vasta suorituksen aikana ohjelman tietyssä tilassa. Tämän mahdollistavat edellä mainitut omistajuusmalli ja vahva tyypitys. Omistajuusmalli varmistaa sen, että ohjelmoija joutuu koodia kirjoittaessaan miettimään arvojen eliniän, joka tekee muistivuodoista harvinaisia. Vahva tyypitys varmistaa taas sen, että data on kaikkialla ohjelmassa yhteensopivaa. \label{sect:luotettavuus}
Rustia kehitettäessä on aina ollut tavoitteena luotettavuus. Tämä tarkoittaa sitä, että ohjelman virheet huomataan jo kääntämisen yhteydessä, eikä vasta suorituksen aikana ohjelman tietyssä tilassa. Tämän mahdollistavat edellä mainitut omistajuusmalli ja vahva tyypitys. Omistajuusmalli varmistaa sen, että ohjelmoija joutuu koodia kirjoittaessaan miettimään arvojen eliniän, joka tekee muistivuodoista harvinaisia. Vahva tyypitys varmistaa taas sen, että data on kaikkialla ohjelmassa yhteensopivaa.
Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on todella hyvä työkalu ohjelmoijalle. Se alleviivaa ongelmakohdat ja selittää lyhyesti, mistä ongelma johtuu. Jossain tapauksissa kääntäjä jopa antaa pieniä koodin pätkiä mistä voi olla apua ongelman ratkaisemisessa. Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on todella hyvä työkalu ohjelmoijalle. Se alleviivaa ongelmakohdat ja selittää lyhyesti, mistä ongelma johtuu. Jossain tapauksissa kääntäjä jopa antaa pieniä koodin pätkiä, mistä voi olla apua ongelman ratkaisemisessa:
\subsection{Paketinhallinta} \clearpage
Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io \cite{rust:cratesio} pakettirekisterissä. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml tiedostossa.
\begin{code} \begin{code}
\inputminted{TOML}{code/Cargo.toml} \inputminted[linenos=false]{shell}{code/compiler-help}
\captionof{listing}{Projektin palvelinpuolen Cargo.toml} \captionof{listing}{Rustin kääntäjä auttaa unohtuneen importin lisäämisessä}
\label{code:rust:cargo-toml} \label{code:rust:help}
\end{code} \end{code}
Cargoon on saatavilla myös useita liitännäisiä, esimerkiksi cargo-watch, joka suorittaa halutun toiminnon aina kun projektin sisällä tapahtuu muutoksia ja tässäkin projektissa käytetty cargo-web, joka helpottaa WebAssembly-ohjelmien kehittämistä. \subsection{Suorituskyky}
Monista luotettavuuteen liittyvistä ominaisuuksista johtuen voisi luulla että kaikki nämä ominaisuudet vaikuttaisivat negatiivisesti suorituskykyyn. Monissa kielissä onkin runtime-kirjasto \cite{wiki:runtime-library}. Se on kääntäjän jatke, joka lisää käännettyyn binääritiedostoon ajon aikana suoritettavia käskyjä, jotka liittyvät juuri tällaisten luotettavuusominaisuuksien takaamiseen. Nämä käskyt suoritetaan siis itse ohjelman suorituksen yhteydessä, joten ne käyttävät prosessoriaikaa ja muistia aina kun ohjelma on käynnissä.
Rustissa ei ole runtime-kirjastoa. Kääntäjä tekee kaikki runtime-kirjaston tehtävät kääntämisen yhteydessä. Jos jokin tarkistus ei mene läpi, esimerkiksi omistajuusmallia on käytetty väärin, ohjelman kääntäminen keskeytetään ja kehittäjälle näytetään virheviesti.
Runtime-kirjaston puuttuminen vapauttaa prosessointiaikaa ja muistia itse ohjelman käyttöön, mikä parantaa suorituskykyä.
\subsection{Turvallinen rinnakkaisajo}
Rinnakkaisajo tarkoittaa sitä, että jotakin ohjelman osaa suoritetaan saman aikaan useassa prosessorin säikeessä. Rinnakkaisajolle sopivissa tehtävissä kuorman jakamisella useammalle säikeelle saavutetaan parempaa suorituskykyä.
Turvallisella rinnakkaisajolla \cite{wiki:thread-safety} tarkoitetaan sitä, että jos rinnakkaisajossa käsitellään tietorakenteita, käsittely suoritetaan turvallisesti. Turvallisuus tarkoittaa sitä, että saavutetaan haluttu tulos eikä ilmene sivuvaikutuksia.
Rust takaa turvallisen rinnakkaisajon luvussa \ref{sect:luotettavuus} mainituilla luotettavuusominaisuuksilla. Tämän asian perinpohjaiseen selittämiseen en ryhdy tässä insinöörityössä. Asiasta kiinnostuneet voivat lukea asiasta lisää Rust Blogista \cite{rust:fearless-concurrency}.
\subsection{Makrot} \subsection{Makrot}
\label{chap:macros}
Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnalisuudeltaan samankaltaisia funktioita. Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnalisuudeltaan samankaltaisia funktioita.
\begin{code} \begin{code}
@ -129,10 +143,10 @@ Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisu
\label{code:rust:macro} \label{code:rust:macro}
\end{code} \end{code}
Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin \mintinline{Rust}{match} -lauseeseen. Kun makron syöte vastaa jotakin näistä lauseista, hakasulkujen sisällä oleva koodi generoidaan. Makrossa käytetty \mintinline{Rust}{println!()} on myös itsessään makro, joka tulee Rustin ''std'' -kirjaston mukana.
\clearpage \clearpage
Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin \mintinline{Rust}{match} -lauseeseen. Kun makron syöte vastaa jotakin näistä lauseista, hakasulkujen sisällä oleva koodi generoidaan. Makrossa käytetty \mintinline{Rust}{println!()} on myös itsessään makro, joka tulee Rustin ''std'' -kirjaston mukana.
\begin{code} \begin{code}
\inputminted{Rust}{code/println.rs} \inputminted{Rust}{code/println.rs}
\captionof{listing}{Rustin sisäänrakennettu println!() makro \cite{rust:println}} \captionof{listing}{Rustin sisäänrakennettu println!() makro \cite{rust:println}}
@ -141,6 +155,18 @@ Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin \mintinline{R
Metaohjelmointi avaa aivan uudenlaisia mahdollisuuksia sille, mitä ohjelmointikielellä voi tehdä. Makroilla voi toteuttaa vaikka kokonaisen ohjelmointikielen \cite{rust:macro-lisp}. Metaohjelmointi avaa aivan uudenlaisia mahdollisuuksia sille, mitä ohjelmointikielellä voi tehdä. Makroilla voi toteuttaa vaikka kokonaisen ohjelmointikielen \cite{rust:macro-lisp}.
\subsection{Paketinhallinta}
\label{sect:paketinhallinta}
Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io \cite{rust:cratesio} pakettirekisterissä. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml-tiedostossa.
\begin{code}
\inputminted{TOML}{code/Cargo.toml}
\captionof{listing}{Projektin palvelinpuolen Cargo.toml}
\label{code:rust:cargo-toml}
\end{code}
Cargoon on saatavilla myös useita liitännäisiä, esimerkiksi cargo-watch, joka suorittaa halutun toiminnon aina, kun projektin sisällä tapahtuu muutoksia sekä tässäkin projektissa käytetty cargo-web, joka helpottaa WebAssembly-ohjelmien kehittämistä.
\subsection{Dokumentaatio ja yhteisö} \subsection{Dokumentaatio ja yhteisö}
Rust on tunnettu todella laajasta dokumentaatiostaan ja vahvasta yhteisöstään. Molemmista on paljon apua varsinkin aloittejioille. Rust on tunnettu todella laajasta dokumentaatiostaan ja vahvasta yhteisöstään. Molemmista on paljon apua varsinkin aloittejioille.
@ -151,7 +177,7 @@ WebAssembly \cite{webassembly:home} on kehitteillä oleva asiakaspuolen ohjelmoi
Kehittäjän ei ole tarkoitus kirjoittaa WebAssemblya itse, vaan käyttää työkaluja, joilla olemassa olevia ohjelmointikieliä voi kääntää WebAssemblyksi. Rust on tästä hyvä esimerkki, sillä WebAssembly on yksi sen kääntäjän natiiveista ''targeteista'', samalla tavalla kuin vaikka x86-prosessorit. Kehittäjän ei ole tarkoitus kirjoittaa WebAssemblya itse, vaan käyttää työkaluja, joilla olemassa olevia ohjelmointikieliä voi kääntää WebAssemblyksi. Rust on tästä hyvä esimerkki, sillä WebAssembly on yksi sen kääntäjän natiiveista ''targeteista'', samalla tavalla kuin vaikka x86-prosessorit.
WebAssembly on siis ensisijaisesti binääriformaatti, mutta sen voi muuntaa myös tekstiformaatiksi, jonka nimi on WebAssembly text \cite{webassembly:text}. WebAssembly text käyttää S-lauseita, joten se muistuttaa syntaksiltaan hyvin paljon Lisp-ohjelmointikieltä. WebAssembly on siis ensisijaisesti binääriformaatti, mutta sen voi muuntaa myös tekstiformaatiksi, jonka nimi on WebAssembly text \cite{webassembly:text}. WebAssembly text käyttää syntaksissaan S-lausekkeita \cite{s-expression}. Se on notaatio puurakenteiselle datalle, joka on kehitetty Lisp-ohjelmointikieltä varten, joten WebAssembly text muistuttaa syntaksiltaan hyvin paljon Lispiä. WebAssembly textiä käytetään tilanteissa, joissa ihmisen täytyy ymmärtää mitä koodissa tapahtuu, esimerkiksi WebAssemblyn sisäisessä kehityksessä ja web-ohjelmistojen debuggereissa.
\clearpage \clearpage
@ -160,11 +186,10 @@ WebAssembly on siis ensisijaisesti binääriformaatti, mutta sen voi muuntaa my
\captionof{listing}{Lukujen yhteenlaskufunktio WebAssembly text -formaatissa} \captionof{listing}{Lukujen yhteenlaskufunktio WebAssembly text -formaatissa}
\label{code:webassembly:plus} \label{code:webassembly:plus}
\end{code} \end{code}
\vspace{\baselineskip}
\begin{code} \begin{code}
\inputminted{Rust}{code/plus.rs} \inputminted{Rust}{code/plus.rs}
\captionof{listing}{Lukujen yhteenlaskufunktio Rustilla} \captionof{listing}{Lukujen yhteenlaskufunktio Rustilla}
\label{code:rust:plus} \label{code:rust:plus}
\end{code} \end{code}
WebAssembly textiä käytetään esimerkiksi debuggereissa.

View file

@ -1,98 +0,0 @@
\chapter{Projekti}
Tein opinnäytetyön yhteydessä fullstack-projektin, missä sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. Tarkoituksena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, vaan puhtaasti arvioida Rustin soveltuvuutta web-ohjelmointiin.
\section{Palvelinpuoli}
\subsection{Kehys}
Palvelinpuolen kehykseksi valikoitui Actix web. Se on käytännössä vastine JavaScript-maailman Express.js:lle, eli se hoitaa HTTP-palvelimen työtä ja reitittää GET ja POST pyynnöt ohjelman oikeille funktioille.
Actix web on puolestaan rakennettu hyödyntämällä Actix -sovelluskehystä, mikä on rakennettu löyhästi actor-mallin pohjalta. Actor-malli\cite{wiki:actor} on Carl Hewittin vuonna 1973 luoma matemaattinen ja tietotekninen malli rinnakkaisajosta. Tämän ansiosta Actix web on hyvin suorituskykyinen ja helposti skaalautuva ratkaisu rajapintoja rakennettaessa.
\clearpage
Actix web on myös hyvin integroitu Rustin vahvaan tyypitykseen. Erityisen vaikuttavaa oli se, että esimerkiksi tietyn rajapinnan päätepisteen POST-pyyntöön pystyy määrittämään parametriksi tietyn rakenteen (eng. structure).
\begin{code}
\inputminted{Rust}{code/registeruser.rs}
\captionof{listing}{Rakenne POST-pyynnön parametrinä}
\label{code:rust:registeruser}
\end{code}
Tällöin varmistutaan automaattisesti siitä, että kun asiakkaan POST-pyyntö saapuu tähän funktioon, kaikki data on oikeanlaista tyyppiä ja kaikki tarvittava data on pyynnössä mukana. Verrattuna JavaScriptiin, jossa ei ole tyyppejä, kaikki vastaanotettava data pitäisi tarkastaa käsin.
\begin{code}
\inputminted{JSON}{code/registeruser.json}
\captionof{listing}{Rakenteeseen sovitettava JavaScript-objekti}
\label{code:json:registeruser}
\end{code}
Kun koodiesimerkin \ref{code:rust:registeruser} funktioon ''register'' saapuu koodiesimerkin \ref{code:json:registeruser} mukainen JSON objekti, Actix huomaa väärän tyypin ja vastaa statuskoodilla ''400 Bad Request'', koska objektin parametri \mintinline{JSON}{"admin"} ei ole tyyppiä \mintinline{Rust}{bool}.
\subsection{Todentaminen}
Actix webin modulaarisuus mahdollisti myös helposti tarvittavien väliohjelmistojen sisällyttämisen ohjelman toimintaan. Actix identity -paketista löytyi tarvittavat palikat joilla sain lisättyä itse kirjoittaman käyttäjän todentamistoiminnallisuuden suojaamaan haluttuja reittejä.
Todentamiseen päätin käyttää JSON Web Tokeneita. JSON Web Tokenit ovat standardoitu (RFC 7519\cite{jwt:home}) tunnistautumistapa verkossa. Tokenit ovat merkkijonoja, jotka sisältävät JavaScript-objektin tekstimuodossa (JSON).
\begin{code}
\inputminted{Rust}{code/jwt.json}
\captionof{listing}{Yhden JWT-tokenin sisältö tästä projektista}
\label{code:json:jwt}
\end{code}
Tämä tokenin sisältö on kaikkien sen hallussapitäjien nähtävissä. Tokenin turvallisuus tulee siitä, että token on allekirjoitettu palvelinpuolella vain palvelimen tiedossa olevalla salasanalla niin, että jos tokenin sisältö muuttuu yhtään, palvelin näkee että se ei ole enää validi.
Eli esimerkiksi jos joku tämän sovelluksen käyttäjä, joka ei ole ylläpitäjä, koittaa tehdä itsestään ylläpitäjän muuttamalla tokenista parametrin \mintinline{JSON}{"admin": false} arvoksi \mintinline{JSON}{true}, palvelimella suoritettava JWT validointifunktio, joka tietää oikean salasanan, näkee että tämä tunniste ei ole enää validi.
Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty, esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää tiedon siitä, että onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana.
Päätin tallettaa edellä mainitun JSON Web Tokenin keksiin (eng. cookie), joka on standardi tapa tallettaa juuri tällaisia todentamiseen käytettäviä tietoja selaimiin. Keksien käyttämisen etu on se, että selain huolehtii sen tallettamisesta automaattisesti ilman lisätoimia kehittäjältä. Lisäksi selain sisällyttää sen seuraaviin kutsuihin automaattisesti.
\subsection{Tietokanta}
Tietokannaksi valikoitui itselleni tuttu MySQL. Relaatiotietokannan sai helposti yhdistettyä Rust-koodiini Diesel ORM:llä. Diesel on tähän mennessä käyttämistäni ORM-kirjastoista selkeästi mukavin käyttää.
Käytännöllisimmät ominaisuudet kehittäjän näkökulmasta olivat Dieselin mukana tuleva komentorivikäyttöliittymä ja migraatiot. Jokaiselle taululle luodaan uusi migraatio, esimerkiksi \mintinline{shell}{diesel migration generate users}, jonka jälkeen Dieselin luomaan hakemistoon kirjoitetaan up.sql- ja down.sql-tiedostot, eli ohjeet siitä, miten tämä taulu luodaan ja poistetaan. Taulu viedään tietokantaan komennolla \mintinline{shell}{diesel migration run} ja taulun voi poistaa ja luoda uudelleen komennolla \mintinline{shell}{diesel migration redo}. Tämä mahdollistaa myös samalla sen, että versiohallintaan voi tallentaa useita versioita samasta taulusta ja palata helposti takaisin vanhempaan versioon, jos uudemman kanssa ilmenee ongelmia.
Edellä mainitut työkalut helpottivat tietokannan kehitystä huomattavasti. Usein varsinkin projektin alkuvaiheilla tietokanta muuttuu jatkuvasti ja usein tulee tarve poistaa ja luoda tietokanta uudelleen. Monet kehittäjät pitävät juurikin tällaista Dieselin up.sql kaltaista tiedostoa versiohallinnassa ja tarpeen mukaan poistavat tietokannan käsin ja liittävät komennon tiedostosta tietokannan komentorivikäyttöliittymään. Dieseliä käytettäessä tämä tulee tehtyä automaattisesti ja se tuntuu todella luontevalta.
\section{Asiakaspuoli}
\subsection{Asennus}
Projektin asiakaspuolen aloittaminen vaati aika paljon tutkimustyötä. Rust-koodi pitää kääntää WebAssemblyksi, johon on olemassa useita eri työkaluja. Lisäksi selaimien rajapintojen käyttämiseen tarvitsee jonkinlaisen kirjaston, joita Rust-maailmassa on tällä hetkellä kaksi: stdweb\cite{webassembly:stdweb} ja web\char`_sys\cite{webassembly:websys}. Näistä stdweb on vanhempi, mutta myös tuetumpi. web\char`_sys on uudempi tulokas ja näyttää siltä että se tulee korvaamaan stdweb:n tulevaisuudessa.
Valitsemani sovelluskehys tukee molempia kirjastoja, mutta päädyin kuitenkin valitsemaan stdweb:n. Suurimpana syynä oli projektin aloittamisen helppous, joka tehdään Cargon liitännäisellä cargo-web. Toisena syynä oli että WebAssembly-ohjelman paketointi web\char`_sys:n kanssa on tätä kirjoittaessani on riippuvainen NodeJS:stä, kun taas stdweb ei vaadi NodeJS:n asennusta kehittäjän koneelle ollenkaan.
WebAssembly-koodin suorittamiseksi selain tarvitsee pienen pätkän JavaScriptiä, joka tekee tarvittavat toimet WebAssembly-ohjelman käynnistämiseksi. Tämän koodin generoi puolestani cargo-web. Lisäksi JavaScriptin suorittamiseen tarvitaan yksi HTML-tiedosto, minne voi myös sisällyttää metadataa, kuten sivuston otsikon, joka näkyy selaimen välilehdessä. Tähän tiedostoon linkitetään myös kaikki muu staattinen data, kuten tyylimääritykset. Nämä tiedostot laitoin käytännön mukaisesti static-nimiseen hakemistoon asiakaspuolen projektin juureen. Tämän hakemiston linkitin symbolisella linkillä palvelinpuolen projektiin, jonka HTTP-serveri voi sitten lähettää HTML-dokumentin, JavaScript-tiedoston ja WebAssembly-binäärin käyttäjän selaimelle.
\subsection{Kehys}
Asiakaspuolen sovelluskehykseksi valitsin Yew:n\cite{yew:home}. Yew muistuttaa hyvin paljon JavaScript-maailmassa suosittua Reactia, eli sen on komponenttipohjainen. Tämä tarkoittaa sitä että kaikkien ohjelman osien, joilla halutaan näyttää jotakin käyttöliittyän osaa, täytyy implementoida Yew:n Component-rajapintaa, jossa on funktiot create, change, update ja view. Näiden funktioiden avulla Yew pystyy orkestroimaan kullakin hetkellä näytettävien komponenttien tilaa.
\subsection{Ulkonäkö}
Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein liitännäinen, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista liitännäistä Yew:lle. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti.
\begin{figure}[h]
\centering
\includegraphics[width=\linewidth]{illustration/login.png}
\caption{Login-komponentin ulkonäkö}
\label{fig:login}
\end{figure}
\subsection{Reititys}
Reititykseen käytin Yew:n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (eng. single page application), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahtu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu.
Asiakaspuolen reititys vaatii myös palvelinpuolelta sen, että kaikki mahdolliset asiakaspuolen reitit palauttavat ohjelman. Toinen edellytys on se, että palvelin ei palauta uudelleenohjausta (HTTP 302), koska silloin myös osoitepalkissa oleva osoite muuttuu eikä sitä voida välittää asiakaspuolen ohjelmalle.
Yritin pitkään toteuttaa tällaista logiikkaa palvelinpuolelle siinä onnistumatta, mutta onnekseni yew\char`_routerin esimerkeistä löytyi esimerkkikoodia tämän saavuttamiseksi käyttämäni palvelinkehyksen kanssa. Päädyin laittamaan kaikki palvelimen omat reitit polun \mintinline{shell}{/api/} alle, ja asiakaspuolen ohjelman juureen (\mintinline{shell}{/}). Sitten määritin ns. ''catch-all'' reitin, joka palauttaa asiakaspuolen ohjelman ilman uudelleenohjausta. Tämä on Actixissa nimeltään \mintinline{shell}{default_service}.
\clearpage
Myös yew\char`_routerin kanssa oli omat haasteensa. Alkuun en saanut sitä toimimaan ollenkaan, vaan kaikki kutsut ohjattiin ensimmäisenä määritettyyn reittiin, mikä oli tässä tapauksessa ''/''. Ongelman syyksi paljastui yew\char`_routerin Switch-komponentin kokoava ''to'' makro. Ratkaisuna oli reitin ''/'' makron \mintinline{Rust}{#[to = "/"]} siirtäminen viimeiseksi listassa. Tarkempaa tietoa tästä ongelmasta voi lukea Yew:n dokumentaatiosta \cite{yew:router-problem}.
\section{Ongelmat}
Projektin loppuvaiheilla ilmeni muutamia ongelmia, mitä en ehtinyt enää ajan puutteen takia korjaamaan.
Palvelinpuolella keksin kokoava kirjasto, Actixin liitännäinen actix\char`_identity, kirjoittaa oletuksena keksiin parametrin \mintinline{JSON}{"HttpOnly": true}, mikä tarkoittaa sitä, että selain ei anna sen sisällä suoritettaville ohjelmille pääsyä tähän keksiin. Tämä on todella hyvä turvallisuusominaisuus, mikä estää haitallisia ohjelmia varastamasta käyttäjän kirjautumistietoja.
Suunnittelmana oli käyttää keksin tietoja asiakaspuolella reititykseen. Esimerkiksi jos keksiä ei ole olemassa, käyttäjä tulee reitittää sisäänkirjautumiskomponenttiin, missä keksi voidaan noutaa palvelimelta. Koska keksissä on tämä HttpOnly-parametri, tähän ei ole mahdollisuutta. Tämän parametrin voisi tietysti laittaa pois päältä, mutta actix\char`_identity ei tarjoa tähän mitään mahdollisuutta.
Yhtenä ratkaisuna tähän ongelmaan olisi käyttää JSON Web Tokenin tallettamiseen keksien sijasta selaimen LocalStoragea. Tämä vaatisi lisää työtä sekä asiakaspuolella että palvelinpuolella ja valitettavasti tähän ei jäänyt enää aikaa.

143
tex/chapters/4-projekti.tex Normal file
View file

@ -0,0 +1,143 @@
\chapter{Projekti}
Tein opinnäytetyön yhteydessä fullstack-projektin, jossa sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. Tarkoituksena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, vaan puhtaasti arvioida Rustin soveltuvuutta web-ohjelmointiin.
\section{Kehitysympäristön asennus}
Rust-projektin aloittamiseksi kehittäjä tarvitsee koneelleen Rustin paketinhallintatyökalun, Cargon (katso luku \ref{sect:paketinhallinta}). Olen itse Linux-käyttäjä, joten sain asennettua Cargon Linux-jakeluni ohjelmavarastosta. Windows-käyttäjille suosittelen käyttämään Rustup-asennusohjelmaa. Lisää tietoa Rustin asentamisesta saa Rustin kotisivuilta \cite{rust:install}.
Projektin saa initialisoitua komennolla \mintinline{shell}{cargo init projektinnimi}. Tämä komento luo hakemiston ''projektinnimi'', minkä sisällä on Cargon konfiguraatiotiedosto Cargo.toml, jossa voi määrittää projektin tiedot ja riippuvuudet.
Lähdekoodi sijaitsee hakemistossa ''src''. Cargo kirjoittaa hakemistoon valmiiksi ''main.rs'' -tiedoston, jossa on ''Hello world!'' esimerkkikoodi. ''main.rs'' on aina Rust-ohjelman ensimmäiseksi suoritettava tiedosto, eli niin kutsuttu entrypoint. ''main.rs'' tiedoston sisällä pitää olla ''main()''-funktio, josta ohjelman suoritus alkaa. Projektin voi suorittaa komennolla \mintinline{shell}{cargo run}.
Suosittelen asentamaan myös muutaman Cargon liitännäisen helpottamaan kehitystä. cargo-watchin avulla voi ajaa komennon aina, kun lähdekoodi muuttuu. Esimerkiksi komennolla \mintinline{shell}{cargo watch -x run} voi kääntää ja käynnistää projektin aina uudelleen, kun lähdekoodi muuttuu. cargo-add -liitännäisellä voi helposti lisätä uusia riippuvuuksia projektiinsa. Esimerkiksi Actix webin saa lisättyä komennolla \mintinline{shell}{cargo add actix-web}.
Näillä ohjeilla pääsee alkuun palvelinpuolen kehityksessä. Asiakaspuolen kehityksen käynnistäminen vaati vielä muutaman lisävaiheen, siitä lisää luvussa \ref{sect:asiakaspuoli:asennus}.
\section{Palvelinpuoli}
\subsection{Kehys}
Palvelinpuolen kehykseksi valikoitui Actix web. Se on käytännössä vastine JavaScript-maailman Express.js:lle, eli se hoitaa HTTP-palvelimen työtä ja reitittää GET- ja POST-pyynnöt ohjelman oikeille funktioille.
Actix web on puolestaan rakennettu hyödyntämällä Actix-sovelluskehystä, mikä on rakennettu löyhästi actor-mallin pohjalta. Actor-malli \cite{wiki:actor} on Carl Hewittin vuonna 1973 luoma matemaattinen ja tietotekninen malli rinnakkaisajosta. Tämän ansiosta Actix web on hyvin suorituskykyinen ja helposti skaalautuva ratkaisu rajapintoja rakennettaessa.
Actix web on myös hyvin integroitu Rustin vahvaan tyypitykseen. Erityisen vaikuttavaa oli se, että esimerkiksi tietyn rajapinnan päätepisteen POST-pyyntöön pystyy määrittämään parametriksi tietyn rakenteen (eng. structure) [koodiesimerkki \ref{code:rust:registeruser}].
\clearpage
\begin{code}
\inputminted{Rust}{code/registeruser.rs}
\captionof{listing}{Rakenne POST-pyynnön parametrinä}
\label{code:rust:registeruser}
\end{code}
Tällöin varmistutaan automaattisesti siitä, että kun asiakkaan POST-pyyntö saapuu tähän funktioon, kaikki data on oikeanlaista tyyppiä ja kaikki tarvittava data on pyynnössä mukana. Verrattuna JavaScriptiin, jossa ei ole tyyppejä, kaikki vastaanotettava data pitäisi tarkastaa käsin.
\begin{code}
\inputminted{JSON}{code/registeruser.json}
\captionof{listing}{Rakenteeseen sovitettava JavaScript-objekti}
\label{code:json:registeruser}
\end{code}
Kun koodiesimerkin \ref{code:rust:registeruser} funktioon ''register'' saapuu koodiesimerkin \ref{code:json:registeruser} mukainen JSON objekti, Actix huomaa väärän tyypin ja vastaa statuskoodilla ''400 Bad Request'', koska objektin parametri \mintinline{JSON}{"admin"} ei ole tyyppiä \mintinline{Rust}{bool}.
\subsection{Todentaminen}
Actix webin modulaarisuus mahdollisti myös helposti tarvittavien väliohjelmistojen sisällyttämisen ohjelman toimintaan. Actix identity -paketista löytyi tarvittavat palikat joilla sain lisättyä itse kirjoittamani käyttäjän todentamistoiminnallisuuden suojaamaan haluttuja reittejä.
\clearpage
Todentamiseen päätin käyttää JSON Web Tokeneita. Ne ovat standardoitu (RFC 7519\cite{jwt:home}) tunnistautumistapa verkossa. Tokenit ovat merkkijonoja, jotka sisältävät JavaScript-objektin tekstimuodossa (JSON).
\begin{code}
\inputminted{Rust}{code/jwt.json}
\captionof{listing}{Yhden JSON Web Tokenin sisältö tästä projektista}
\label{code:json:jwt}
\end{code}
Tämä tokenin sisältö on kaikkien sen hallussapitäjien nähtävissä. Tokenin turvallisuus tulee siitä, että token on allekirjoitettu palvelinpuolella vain palvelimen tiedossa olevalla salasanalla niin, että jos tokenin sisältö muuttuu yhtään, palvelin näkee että se ei ole enää validi.
Otetaan esimerkkinä tämän sovelluksen käyttäjä Pasi. Pasi ei ole ylläpitäjä, mutta hän koittaa tehdä itsestään ylläpitäjän muuttamalla omasta tokenistaan parametrin \mintinline{JavaScript}{"admin": false} arvoksi \mintinline{JavaScript}{true}. Kun Pasi lähettää tämän muokatun tokenin palvelimelle, missä se viedään JWT-validointifunktioon. Palvelin huomaa että token ei ole enää validi, koska sen sisältöä ei ole allekirjoitettu palvelimen salasanalla.
Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty, esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää tiedon siitä, että onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä, että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana.
Päätin tallettaa edellä mainitun JSON Web Tokenin evästeeseen (eng. cookie), joka on standardi tapa tallettaa juuri tällaisia todentamiseen käytettäviä tietoja selaimiin. Evästeiden käyttämisen etu on se, että selain huolehtii sen tallettamisesta automaattisesti ilman lisätoimia kehittäjältä. Lisäksi selain sisällyttää sen seuraaviin kutsuihin automaattisesti.
\subsection{CORS}
Lisäsin palvelimelle myös Cross-Origin Resource Sharing (CORS) \cite{wiki:cors} -toiminnallisuuden. Oletuksena selaimen lataama ohjelma saa ladata resursseja vain samasta osoitteesta, kuin mistä itse ohjelma on ladattu. Tämä on turvallisuuskäytäntö, joka kulkee nimellä Same-origin policy (SOP) \cite{wiki:sop}. Näihin resursseihin sisältyvät muunmuassa CSS-tyylimääritykset, kuvat ja JavaScript-ohjelmat.
Lähes kaikki nykyiset web-ohjelmistot kuitenkin vaativat näiden ulkoisien resurssien käyttöä. CORS on tekniikka, jossa palvelin kertoo selaimelle sallitut osoitteet, mistä resursseja saa noutaa. CORS:n lisääminen tähän projektiin hoitui Actixin liitännäisellä actix\char`_cors.
\subsection{Tietokanta}
\label{project:database}
Tietokannaksi valikoitui itselleni tuttu MySQL. Relaatiotietokannan sai helposti yhdistettyä Rust-koodiini Diesel ORM:llä. Diesel on tähän mennessä käyttämistäni ORM-kirjastoista selkeästi mukavin käyttää.
Käytännöllisimmät ominaisuudet kehittäjän näkökulmasta olivat Dieselin mukana tuleva komentorivikäyttöliittymä ja migraatiot. Jokaiselle taululle luodaan uusi migraatio, esimerkiksi \mintinline{shell}{diesel migration generate users}, jonka jälkeen Dieselin luomaan hakemistoon kirjoitetaan up.sql- ja down.sql-tiedostot, eli ohjeet siitä, miten tämä taulu luodaan ja poistetaan. Taulu viedään tietokantaan komennolla \mintinline{shell}{diesel migration run} ja taulun voi poistaa ja luoda uudelleen komennolla \mintinline{shell}{diesel migration redo}. Tämä mahdollistaa myös samalla sen, että versiohallintaan voi tallentaa useita versioita samasta taulusta ja palata helposti takaisin vanhempaan versioon, jos uudemman kanssa ilmenee ongelmia.
Edellä mainitut työkalut helpottivat tietokannan kehitystä huomattavasti. Usein varsinkin projektin alkuvaiheilla tietokanta muuttuu jatkuvasti ja usein tulee tarve poistaa ja luoda tietokanta uudelleen. Monet kehittäjät pitävät juurikin tällaista Dieselin up.sql kaltaista tiedostoa versiohallinnassa ja tarpeen mukaan poistavat tietokannan käsin ja liittävät komennon tiedostosta tietokannan komentorivikäyttöliittymään. Dieseliä käytettäessä tämä tulee tehtyä automaattisesti ja se tuntuu todella luontevalta.
Päätin myös käyttää Dieselin kanssa yhteyksien yhdistämistä (eng. connection pooling). Tämä tarkoittaa sitä, että palvelin luo käynnistyessään prosessin, joka ottaa yhteyden tietokantaan, ja jakaa tätä yhteyttä sitä tarvitseville ohjelman osille. Tämä on tehokkaampaa, kuin vaihtoehto, jossa otetaan uusi yhteys jokaista tietokantakyselyä varten.
\section{Asiakaspuoli}
\subsection{Asennus}
\label{sect:asiakaspuoli:asennus}
Projektin asiakaspuolen aloittaminen vaati aika paljon tutkimustyötä. Rust-koodi pitää kääntää WebAssemblyksi, johon on olemassa useita eri työkaluja. Lisäksi selaimien rajapintojen käyttämiseen tarvitsee jonkinlaisen kirjaston, joita Rust-maailmassa on tällä hetkellä kaksi: stdweb\cite{webassembly:stdweb} ja web\char`_sys\cite{webassembly:websys}. Näistä stdweb on vanhempi, mutta myös tuetumpi. web\char`_sys on uudempi tulokas ja näyttää siltä, että se tulee korvaamaan stdweb:n tulevaisuudessa.
Valitsemani sovelluskehys tukee molempia kirjastoja, mutta päädyin kuitenkin valitsemaan stdweb:n. Suurimpana syynä oli projektin aloittamisen helppous, joka tehdään Cargon liitännäisellä cargo-web. Toisena syynä oli, että WebAssembly-ohjelman paketointi web\char`_sys:n kanssa on tätä kirjoittaessani on riippuvainen NodeJS:stä, kun taas stdweb ei vaadi NodeJS:n asennusta kehittäjän koneelle ollenkaan.
WebAssembly-koodin suorittamiseksi selain tarvitsee pienen pätkän JavaScriptiä, joka tekee tarvittavat toimet WebAssembly-ohjelman käynnistämiseksi. Tämän koodin generoi puolestani cargo-web. Lisäksi JavaScriptin suorittamiseen tarvitaan yksi HTML-tiedosto. Tähän tiedostoon voi myös sisällyttää metadataa, kuten esimerkiksi sivuston otsikon, joka näkyy selaimen välilehdessä. Tähän tiedostoon linkitetään myös kaikki muu staattinen data, kuten tyylimääritykset. Nämä tiedostot laitoin käytännön mukaisesti static-nimiseen hakemistoon asiakaspuolen projektin juureen. Tämän hakemiston linkitin symbolisella linkillä palvelinpuolen projektiin, jonka HTTP-serveri voi sitten lähettää HTML-dokumentin, JavaScript-tiedoston ja WebAssembly-binäärin käyttäjän selaimelle.
\subsection{Kehys}
Asiakaspuolen sovelluskehykseksi valitsin Yew:n\cite{yew:home}. Yew muistuttaa hyvin paljon JavaScript-maailmassa suosittua Reactia, eli sen on komponenttipohjainen. Tämä tarkoittaa sitä, että kaikkien ohjelman osien, joilla halutaan näyttää jotakin käyttöliittymän osaa, täytyy implementoida Yew:n Component-rajapintaa. Tässä rajapinnassa on funktiot create, change, update ja view. Näiden funktioiden avulla Yew pystyy orkestroimaan kullakin hetkellä näytettävien komponenttien tilaa.
\subsection{Ulkonäkö}
\label{sect:ulkonäkö}
Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein kirjasto, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista kirjastoa, joka toimisi Yew:n kanssa. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkit on CSS-kirjasto, missä on paljon valmiita ja hyvännäköisiä tyylimäärityksiä. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti (kuva \ref{fig:login}).
\begin{figure}[h]
\centering
\includegraphics[width=\linewidth]{illustration/login.png}
\caption{Login-komponentin tyylittely, joka on toteutettu käyttämällä UIKit-kirjastoa}
\label{fig:login}
\end{figure}
\subsection{Reititys}
Reititykseen käytin Yew:n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (katso luku \ref{sect:spa}), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahtu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu. Käyttöliittymää muokataan käyttämällä DOM:ia, joka mahdollistaa sivun sisällön muokkaamisen ilman sivun uudelleen lataamista.
Asiakaspuolen reititys vaatii myös palvelinpuolelta sen, että kaikki mahdolliset asiakaspuolen reitit palauttavat ohjelman. Toinen edellytys on se, että palvelin ei palauta uudelleenohjausta (HTTP 302), koska silloin myös osoitepalkissa oleva osoite muuttuu eikä sitä voida välittää asiakaspuolen ohjelmalle.
Yritin pitkään toteuttaa tällaista logiikkaa palvelinpuolelle siinä onnistumatta, mutta onnekseni yew\char`_routerin esimerkeistä löytyi esimerkkikoodia tämän saavuttamiseksi käyttämäni palvelinkehyksen kanssa. Päädyin laittamaan kaikki palvelimen omat reitit polun \mintinline{shell}{/api/} alle, ja asiakaspuolen ohjelman juureen (\mintinline{shell}{/}). Sitten määritin ns. ''catch-all'' reitin, joka palauttaa asiakaspuolen ohjelman ilman uudelleenohjausta. Tämä on Actixissa nimeltään \mintinline{shell}{default_service}.
Myös yew\char`_routerin kanssa oli omat haasteensa. Alkuun en saanut sitä toimimaan ollenkaan, reititin ohjasi kaikki osoitteet ensimmäisenä määritettyyn reittiin, mikä oli tässä tapauksessa ''/''. Ongelman syyksi paljastui yew\char`_routerin Switch-komponentin kokoava ''to'-makro (katso luku \ref{chap:macros}). Ratkaisuna oli reitin ''/'' makron siirtäminen viimeiseksi listassa. Tarkempaa tietoa tästä ongelmasta voi lukea Yew:n dokumentaatiosta \cite{yew:router-problem}.
\section{Ongelmat}
\subsection{Evästeet}
\label{sect:problems:cookies}
Projektin loppuvaiheilla ilmeni muutamia ongelmia, joita en ehtinyt enää ajan puutteen takia korjaamaan.
Palvelinpuolella evästeen kokoava kirjasto, Actixin liitännäinen actix\char`_identity, kirjoittaa oletuksena evästeeseen parametrin \mintinline{JavaScript}{"HttpOnly": true}, mikä tarkoittaa sitä, että selain ei anna sen sisällä suoritettaville ohjelmille pääsyä tähän evästeeseen. Tämä on todella hyvä turvallisuusominaisuus, joka estää haitallisia ohjelmia varastamasta käyttäjän kirjautumistietoja.
Suunnittelmana oli käyttää evästeen tietoja asiakaspuolella reititykseen. Esimerkiksi jos evästettä ei ole olemassa, käyttäjä tulee reitittää sisäänkirjautumiskomponenttiin, jossa eväste voidaan noutaa palvelimelta. Koska evästeessä on tämä HttpOnly-parametri, tähän ei ole mahdollisuutta. Tämän parametrin voisi tietysti laittaa pois päältä, mutta actix\char`_identity ei tarjoa tähän mitään mahdollisuutta.
Yhtenä ratkaisuna tähän ongelmaan olisi käyttää JSON Web Tokenin tallettamiseen evästeen sijasta selaimen LocalStoragea. Tämä vaatisi lisää työtä sekä asiakaspuolella että palvelinpuolella ja valitettavasti tähän ei jäänyt enää aikaa.
\subsection{Tietokantayhteys}
Palvelimen ja tietokannan välisessä yhteydessä oli koko projektin ajan ongelma, että jokainen kysely tietokantaan kesti noin viisi sekuntia. Luulen että tämä ongelma liittyy jotenkin käyttämääni yhteyksien yhdistämiseen (luku \ref{project:database}), koska palvelimen loki näyttää satunnaisesti virheviestin \mintinline{shell}{Failed to create DB pool: Error(Some("Too many connections")}. Ajan loppumisen takia tämä ongelma jäi korjaamatta.
\clearpage
\section{Tulokset}
\begin{figure}[h]
\centering
\includegraphics[width=\linewidth]{illustration/login-process.pdf}
\caption{Kirjautumisprosessi projektissa}
\label{fig:login-process}
\end{figure}
Tuloksena syntyi yksinkertainen web-ohjelma, jossa sekä palvelin- että asiakaspuoli on toteutettu Rustilla. Kuvassa \ref{fig:login-process} on määritelty kaikki kirjautumisprosessin vaiheet.
Ohjelman juuressa renderöidään komponentti ''ProtectedComponent'', missä on yksi nappi, jolla voidaan noutaa dataa palvelimelta. Tämä data on suojattu niin, että pyynnön mukana pitää olla eväste, jonka sisällä on JWT, joka sitten validoidaan palvelinpuolella. Jos validointi onnistuu, suojattu data lähetetään asiakkaalle. Asiakaspäässä HTTP-statuskoodin perusteella näytetään joko noudettu data, tai "403 Unauthorized".
Reitissä ''/login'' renderöidään ''LoginComponent'', joka koostuu kahdesta tekstikentästä, käyttäjänimi ja salasana, sekä napista, jolla kirjautumistiedot voi lähettää palvelimelle. Onnistumisen kirjautumisen jälkeen palvelimelta lähetetty eväste tallennetaan selaimen muistiin.
Luvussa \ref{sect:problems:cookies} mainittujen ongelmien takia toteutus jäi hieman kesken. Eväste saadaan onnistuneesti palvelimelta selaimeen, mutta koska eväste ei ole ohjelman saatavilla, en voi toteuttaa tämän perusteella reititystä uudelle sivulle. Tein tämän takia sivun ylälaitaan napit, joilla käyttäjä voi siirtyä manuaalisesti sivulta toiselle. Ajatuksena oli, että onnistuneen kirjautumisen jälkeen käyttäjä siirrettäisiin toiselle sivulle, ja evästeen puuttueessa taas kirjautumissivulle.

View file

@ -1,12 +0,0 @@
\clearpage
\chapter{Tulokset}
\begin{figure}[h]
\centering
\includegraphics[width=\linewidth]{illustration/login-process.pdf}
\caption{Kirjautumisprosessi projektissa}
\label{fig:login}
\end{figure}
Tuloksena syntyi yksinkertainen web-applikaatio, missä sekä palvelinpuoli, että asiakaspuoli oli toteutettu Rustilla. Kuvassa \ref{fig:login} on kuvattu kaikki kirjautumisprosessin vaiheet tässä projektissa.

View file

@ -0,0 +1,19 @@
\chapter{Rustin soveltuvuus web-ohjelmointiin}
\section{Palvelinpuoli}
\label{sect:soveltuvuus:palvelinpuoli}
Palvelinpuolella en näe Rustin käyttämisessä mitään varjopuolia. Jos palvelin tulee osaksi isoja nettisivuja, missä on päivittäin tuhansia kävijöitä, Rustin suorituskyvystä ja turvallisesta rinnakkaisajosta voi olla todella paljon hyötyä. Lisäksi jos palvelin tekee joitakin laskennallisesti raskaita tehtäviä, kuten vaikkapa 3D-mallinnusta tai koneoppimista, Rustin suorituskykyetu tulee entistäkin selvemmäksi.
Näen myös, että Rustin vahvasta tyypityksestä voi olla paljon hyötyä web-sovelluksen palvelinpuksen palvelinpuoltaa rakennettaessa. Kuten koodiesimerkissä \ref{code:rust:registeruser} nähtiin, vahvan tyypityksen tuomia etuja voi käyttää hyödykseen rajapintaa rakennettaessa. Lisäksi vahva tyypitys lisää ohjelman luotettavuutta vähentämällä ajonaikaisten virheiden riskiä. Vahvan tyypityksen voi kylläkin lisätä myös JavaScriptiin käyttämällä esimerkiksi TypeScriptiä \cite{typescript}.
Lisäksi Rust on suosittu kieli muuallakin kuin web-maailmassa. Tämän ansiosta kirjastojen saatavuus erityisesti palvelinpuolella ei ole ongelma. Rust-koodiin voi myös sisällyttää C- ja C++ -kirjastoja, jos jotakin projektissa tarvittavaa kirjastoa ei löydy Rust-pakettina.
\section{Asiakaspuoli}
Asiakaspuolella Rustin hyötyjä on vaikeampi oikeuttaa. Asiakaspuolen ohjelmat ovat useimmiten yksinkertaisia, koska niiden tehtävänä on usein vain piirtää käyttöliittymä ja välittää dataa palvelimen ja käyttöliittymän välillä. Näin ollen myös suorituskykyvaatimukset ovat todella pienet verrattuna palvelinpuoleen. Lisäksi asiakaspuoli palvelee aina vain yhtä asiakasta kerrallaan, verrattuna palvelinpuoleen, jossa usein yksi palvelin palvelee kaikkia asiakkaita. Tästä syystä myös Rustin rinnakkaisajo-ominaisuudet jäävät hyödyntämättä.
Vahvan tyypityksen hyötyjä ei voi kieltää, mutta kuten luvussa \ref{sect:soveltuvuus:palvelinpuoli} mainittiin, myös JavaScriptin saa tyypitettyä käyttämällä TypeScriptiä.
Kirjastojen saatavuus on ongelma asiakaspuolella. Kaikkien kirjastojen, jotka vuorovaikuttavat selaimen kanssa, pitää nimenomaan tukea WebAssemblya. WebAssembly-ekosysteemi on vielä melko alkutekijöissään, ja näitä kirjastoja on vielä hyvin vähän. Tämän projektin aikana tätä ei ilmennyt muuta kuin tyylimäärityksissä (luku \ref{sect:ulkonäkö}), mutta tämän projektin käyttöliittymä oli vielä todella yksinkertainen, eikä montaa kirjastoa tarvittukaan.
Jos lähdetään siitä oletuksesta, että suurin osa web-kehittäjistä osaa jo JavaScriptiä, en näiden syiden takia voi suositella kokonaan uuden kielen opettelemista asiakaspuolta varten. Mutta jos on esimerkiksi kehittäjä, jolla ei ole mitään taustaa web-kehityksessä, mutta osaa jo valmiiksi Rustia, tai muita matalemman tason kieliä, Rust voi olla mielekäs vaihtoehto. Tässäkin tapauksessa edellytyksenä on, että kaikki kehittäjän tarvitsemat kirjastot ovat saatavilla, tai kehittäjällä on aikaa ja halua toteuttaa puuttuvien kirjastojen toiminallisuus itse.
Rust-koodista käännettyjä WebAssembly-tiedostoja voi myös sisällyttää JavaScript-ohjelmiin \cite{javascript-wa}. Jos asiakaspuolen ohjelmassa on osia, jotka vaativat suoritustehoa ja/tai rinnakkaisajo-ominaisuuksia, voi olla hyvä vaihtoehto kirjoittaa vain nämä osat Rustilla ja sisällyttää ne JavaScript-koodiin.

View file

@ -1,2 +0,0 @@
\chapter{Yhteenveto}
\clearpage

View file

@ -0,0 +1,20 @@
\chapter{Yhteenveto}
Rust soveltuu todella hyvin palvelinpuolen web-ohjelmointikieleksi. Sen luotettavuus, suorituskyky ja rinnakkaisajo-ominaisuudet tuovat todellisia hyötyjä sekä kehittäjille että palvelimen asiakkaille.
Rust on asiakaspuolen web-ohjelmointikielenä riippuvainen WebAssembly-ekosysteemistä, minkä takia kirjastojen saatavuus on vielä melko huono. Lisäksi asiakaspuolen alhaisten suorituskykyvaatimuksien takia Rustin paremmasta suorituskyvystä verrattuna JavaScriptiin on vaikea oikeuttaa useimmissa tapauksissa.
Koko projektia ei ole pakko kirjoittaa alusta loppuun Rustilla, niinkuin tässä insinöörityössä tehtiin. Jos asiakaspuolen ohjelmassa on korkeampaa suorituskykyä vaativia osia, ne voi kirjoittaa Rustilla ja sisälllyttää WebAssembly-binääreinä osaksi muuten JavaScriptillä toteutettua ohjelmaa.
Myös palvelinpuolella on mahdollista toteuttaa suurin osa projektista JavaScriptilla, ja sisällyttää sitten parempaa suorituskykyä vaativat, Rustilla kirjoitetut ohjelman osat osaksi JavaScript-koodia.
Rustia voi sisällyttää edellä mainituilla tekniikoilla myös olemassa oleviin projekteihin. Jos kehittäjä haluaa siirtää olemassa olevan JavaScript-projektin kokonaan Rust-pohjaiseksi, sen voi tehdä asteittain.
Päätös Rustin käyttämisestä web-ohjelmointikielenä ei ole siis pelkkä kyllä tai ei, vaan se voi olla myös jotakin siltä väliltä. Tältä skaalalta löytyy varmasti jokin ratkaisu, millä jokainen kehittäjä voi hyödyntää Rustia projektissaan omilla ehdoillaan.
Itse tulen todennäköisesti käyttämään Rustia seuraavassa palvelinpuolen projektissani. Tietenkin kehitysympäristö ja tiimitoverien osaaminen vaikuttavat käytettävään kieleen paljon, koska uuden kielen opetteluun kuluu paljon aikaa.
Jään mielenkiinnolla seuraamaan web-kehityksen tulevaisuutta ja sitä, mihin WebAssemblyn ekosysteemi kehittyy lähivuosina. Kirjastojen saatavuuden parantumisen myötä uskon, että se tulee tulevaisuudessa olemaan entistäkin varteenotettavampi vaihtoehto JavaScriptille. En kuitenkaan usko, että JavaScriptistä pois siirtyminen tapahtuu yhdessä yössä, vaan WebAssembly kehittyy JavaScriptin rinnalla vielä pitkään.
Tämän insinöörityön lähdekoodi on avoin ja se löytyy kokonaisuudessaan GitLabista \cite{sourcecode}. Tähän sisältyy projektin lähdekoodi, sekä raportin \LaTeX{}-lähdekoodi.
\clearpage

12
tex/code/compiler-help Normal file
View file

@ -0,0 +1,12 @@
error[E0412]: cannot find type `Json` in this scope
--> src/component/login.rs:6:16
|
6 | fn create() -> Json {}
| ^^^^ not found in this scope
|
help: possible candidates are found in other modules, you can import them into scope
|
1 | use yew::format::Json;
|
1 | use yew::format::json::Json;
|

34
tex/code/trait.rs Normal file
View file

@ -0,0 +1,34 @@
trait Muoto {
fn pinta_ala(&self) -> u32;
}
struct Suorakulmio {
x: u32,
y: u32,
}
struct Ympyra {
r: f64,
}
impl Muoto for Suorakulmio {
fn pinta_ala(&self) -> u32 {
self.x * self.y
}
}
impl Muoto for Ympyra {
fn pinta_ala(&self) -> u32 {
(3.141 * self.r * self.r) as u32
}
}
fn main() {
let muoto1 = Suorakulmio { x: 15, y: 10 };
let muoto2 = Ympyra { r: 8.5 };
println!("Suorakulmion pinta-ala: {}", muoto1.pinta_ala());
println!("Ympyrän pinta-ala: {}", muoto2.pinta_ala());
}
// Suorakulmion pinta-ala: 150
// Ympyrän pinta-ala: 226

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

View file

@ -22,10 +22,11 @@
% Chapters % Chapters
\input{chapters/1-johdanto.tex} \input{chapters/1-johdanto.tex}
\input{chapters/2-kielet.tex} \input{chapters/2-web-ohjelmointi.tex}
\input{chapters/3-projekti.tex} \input{chapters/3-kielet.tex}
\input{chapters/4-tulokset.tex} \input{chapters/4-projekti.tex}
\input{chapters/5-yhteenveto.tex} \input{chapters/5-soveltuvuus.tex}
\input{chapters/6-yhteenveto.tex}
%%%%%%%%%% %%%%%%%%%%
\input{style/biblio.tex} \input{style/biblio.tex}

View file

@ -12,7 +12,7 @@
{\fontsize{13pt}{13pt}\bfseries\linespread{1}} {\fontsize{13pt}{13pt}\bfseries\linespread{1}}
{\thesection}{.5cm}{} {\thesection}{.5cm}{}
%Adapt labelwidth (sorry for the ugly hack) %Adapt labelwidth (sorry for the ugly hack)
\setlist[description]{leftmargin=!, labelwidth=4em} \setlist[description]{leftmargin=!, labelwidth=6em}
\IfLanguageName {finnish} { \IfLanguageName {finnish} {
\printacronyms[title=Lyhenteet] \printacronyms[title=Lyhenteet]
}{ }{

View file

@ -17,4 +17,3 @@
%avoid that the last page of bib get appendix header %avoid that the last page of bib get appendix header
\clearpage \clearpage