Rust for javascript utviklere - En myk start
Selvom Rust og JavaScript er veldig forskjellige både i syntax og utviklingsmetode, er det noen ting man kan sammeligne. Dette er en liten oversikt over likheter og ulikheter i disse språkene.
Dette innlegget er ment som en start-guide for Javascript utviklere som ønsker å lære Rust. Den tar for seg enkle temaer som gir et godt grunnlag.
Eksemplene er alltid JavaScript øverst og Rust nederst. I de fleste tilfeller, er main funksjonen i Rust utelatt.
Litt om likheter
Deklarasjon av variabler
let a = 1; // En variabel som kan forandre seg
const b = 2; // En variabel som ikke kan forandre seg
let a = 1 // En variabel som ikke kan forandre seg
let mut b = 2 // En variabel som kan forandre seg
Løkker
for (let i = 0; i < 5; i++) {
console.log(i);
}
const objectList = [{ a: 1 }, { a: 2 }, { a: 3 }];
objectList.forEach((item) => console.log(item));
for i in 0..5 {
println!("{}", i);
}
#[derive(Debug)]
struct O {
pub a: i32
}
let object_list = vec![
O{a: 1}, O{a: 2}, O{a: 3}
];
object_list.iter().for_each(|item| {
dbg!(item);
});
If statement
const a = 1;
const b = 2;
let isTrue = false;
if (a === b) {
isTrue = true;
}
let a = 1;
let b = 2;
let is_true = if a == b {
true
} else {
false
};
Deklarasjon av funksjoner
const f = () => 1;
const n = (a) => 1 + a;
fn f() -> i32 { 1 }
fn n(a: i32) -> i32 { 1 + a }
Data objekter
const o = {
a: 1,
b: 2,
};
struct O {
pub a: i32,
pub b: i32
}
let o = O { a: 1, b: 2 };
Klasser / Structs
I rust er det ikke noe begrep om klasser, men det går likevel an å implementere funksjoner i en struct.
class Potato {
constructor(juicyness) {
this.juicyness = juicyness;
}
isJuicy() {
return this.juicyness > 10;
}
}
const potato = new Potato(11);
if (potato.isJuicy()) {
console.log('The potato is juicy');
}
struct Potato {
juicyness: u8
}
impl Potato {
pub fn new(juicyness: u8) -> Self {
Potato {
juicyness
}
}
pub fn is_juicy(&self) -> bool {
self.juicyness > 10
}
}
let potato = Potato::new(11);
if potato.is_juicy() {
println!("The potato is juicy")
}
Match: en switch på steroider
const a = 1;
switch (a) {
case 1:
console.log('1');
break;
case 2:
console.log('2');
break;
case 3:
case 4:
console.error('Can not be 3 or 4');
default:
console.error('No case for', a);
}
let b = 0;
let c = 2;
switch (c) {
case 1:
b = 1;
break;
case 2:
b = 2;
break;
case 3:
case 4:
b = null;
}
function returnFromSwitch(a) {
switch (a) {
case 1:
return 1;
case 2:
return 2;
case 3:
case 4:
return null;
default:
return 2;
}
}
let a = 1;
match a {
1 => { println!("1"); },
2 => { println!("2"); },
3 | 4 => { eprintln!("Can not be 3 or 4"); },
_ => { eprintln!("No case for {}", a); }
}
let c = 2;
let b = match c {
1 => { Some(1) },
2 => { Some(1) },
3 | 4 => { Option::None },
_ => { Some(c) }
};
fn return_from_match(a: i32) -> Option<i32> {
match a {
1 => { Some(1) },
2 => { Some(1) },
3 | 4 => { Option::None },
_ => { Some(c) }
}
}
Litt om forskjeller
Null values
I Rust finnes ikke null verdier, og det nærmeste man kommer er Option
let a = null;
a = 1;
let mut a: Option<i32> = Option::None;
a = Some(1);
Hint: I virkeligheten trenger man bare å skrive let mut a = Option::None
siden compileren finner ut av typen på linje 2.
Videre eksempel
I javascript kan man for eksempel sammeligne a
med b
uansett om den er et tall eller null.
let a = null;
let b = 1;
let c = a === b; // false
let mut a: Option<i32> = Option::None;
let b = 1;
let c = a == b; // Throws: expected enum `Option`, found integer
let c = a.unwrap() == b // Throws: called `Option::unwrap()` on a `None` value
let c = a == Some(b) // false
Async await
Både Rust og Javascript har async/await, disse brukes også til ganske tilsvarende ting. Som å spørre en database, sende requester til servere etc.
I javascript returnerer en async funksjon en Promise type, som kan defineres som Promise
const fs = require('fs').promises;
const loadFile = async () => {
const data = await fs.readFile('myFile.txt', 'binary');
return Buffer.from(data);
};
const useLoadFile = async () => {
try {
let data = await loadFile();
return data;
} catch (e) {
console.error('loadFile failed with error:', e);
return null;
}
};
use thiserror::Error;
#[derive(Error, Debug)]
enum MyError {
#[error("IO error")]
Io(#[from] std::io::Error)
}
// Denne trenger ikke egentlig å være async, men jeg ville holde eksempelet enkelt
async fn load_file () -> std::result::Result<String, MyError> {
Ok(std::fs::read_to_string("myFile.txt")?)
}
#[tokio::main]
async fn main() {
let result = load_file().await;
match result {
Ok(_) => println!("Yay"),
Err(_) => println!("Woops")
}
}
Funksjon som argument
I javascript er/var dette et mye brukt mønster. I Rust er det ikke like vanlig å bruke det etter min erfaring.
const myFunction = (callback) => {
callback([1, 2]);
};
myFunction(([a, b]) => console.log(a, b));
fn my_function<F>(callback: F) where F: Fn((i32, i32)) -> () {
callback((1, 2))
}
my_function(|(a, b)| {
println!("{}, {}", a, b)
});