Learning Rust from a TypeScript perspective
Pretty unstructured ramblings as I try to make sense of Rust.
Warning: This is just some incoherent thoughts on Rust as I try to learn it, it isn't much of a blog post
Learnings
Rust Structs are sort of JS Objects
// Rust_Struct
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let user1 = User {
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
rust// JavaScript_Object
const user1 = {
username: "someusername123",
active: true,
sign_in_count: 1,
};
jsBut sort of aren't as they can have methods, so kind of similar to some form of class, except the first parameter of a Struct method is self, essentially this in JS land.
function Rect({ height, width }) {
this.height = height;
this.width = width;
}
Rect.prototype.area = function () {
return this.width * this.height;
};
const rect = new Rect({ height: 20, width: 10 });
console.info(rect.area());
jsstruct Rect {
height: u32
width: u32
}
impl Rect {
fn area (&self) -> u32 {
self.width * self.height
}
}
let rect = Rect {height: 20, width: 10};
println!(rect.area());
rustRusts Field Init Shorthand is Object Property Value Shorthand in JS
// Rust_Field_Init_Shorthand
let username = String::from("someusername123");
let user1 = User {
email,
username,
active: true,
sign_in_count: 1,
};
rust// JavaScript_Object_Property_Value_Shorthand
const username = "someusername123";
const user1 = {
email,
username,
active: true,
sign_in_count: 1,
};
jsRusts Struct Update Syntax is using Destructuring one object to create another
// Rust_Struct_Update_Syntax
let user1 = User {
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
username: String::from("someusername123"),
...user1
};
rust// JavaScript_Object_Destructuring
const user1 = {
username: "someusername123",
active: true,
sign_in_count: 1,
};
const user2 = {
...user1,
username: "someusername123",
};
jsDifferences
Order doesn't seem to matter for Rust Struct Update Syntax, while in JavaScript it's important.
const var1 = { a: 1, b: 2 };
const var2 = { ...var1, a: 3 }; // { a: 3, b: 2 }
const var3 = { a: 3, ...var1 }; // { a: 1, b: 2 }
jsThe properties are assigned left to right, so the right most wins.
Rust const isn't like JS const
Both are always immutable, however in JS they can be the result of function etc, something commuted at runtime, however in Rust, they can only be actually constant expressions.
For example, in Rust you can't do this:
let a = "what";
const c: String = a;
rustlet mut is sort of similar to let
let in JavaScript marks something as mutable.
Variable shadowing is much more important with Rust - used as a way to declare transformations on otherwise immutable variables
In JavaScript a shadowed variable just declaring a new variable with the same name as a previous variable. The new variable is said to shadow the original, as the new variable is what is seen when used.
let a = "first";
// This `a` shadows the one above
let a = "second";
jsWith Rust however, shadowing has more to it. The important thing to know here is that by default variables in Rust are immutable, you opt in to making them mutable with the let mut syntax, like:
// This causes an error, as x is immutable
let x = 5;
x = 6;
// We've told Rust `x` will be mutated with the `let mut`
// So we don't get any errors here
let mut x = 5;
x = 6;
rustBut if you do this, it means the variable is always mutable. Rust gives us a middle ground with shadowing, where shadowing allows us to essentially do a transformation on the original variable.
It makes the variable mutable for the single let expression.
And then after it, the variable is locked back up, and can no longer be mutated.
// This is fine
let x = 5;
let x = x + 1;
// This will cause an error
x = 7;
rustWith this use of shadowing you can even change the variables type, something you can't do with the let mut syntax.
// This works
let x = "what";
let x = x.len();
// This doesn't
let x = "what";
x = x.len();
rustRust Enums can contain any kind of data
Rust enums allow the variants to store different types of data.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
rustA sort of not really rough approximation of this in TypeScript would look like this:
enum EMessage {
Quit,
Move,
Write,
ChangeColour,
}
const Message = {
[EMessage.Quit]: (data: void) => data,
[EMessage.Move]: (data: { x: number; y: number }) => data,
[EMessage.Write]: (data: string) => data,
[EMessage.ChangeColour]: (data: [number, number, number]) => data,
};
Message[EMessage.Move]({ x: 1, y: 2 });
tsYou can add methods to Rust enums, just like structs:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
let result = match self {
Self::Quit => 0,
Self::Move {x: _, y: _} => 1,
Self::Write(_) => 2,
Self::ChangeColor(_, _, _) => 3
};
println!("Got: {}", result);
}
}
let m = Message::Write(String::from("hello"));
m.call();
rust