Giter Club home page Giter Club logo

rust-for-typescript's Introduction

  • Running ts npx ts-node src/index.ts
  • Running rust cargo run

Intro

These are my notes from the course on Rust for Typescript Developers.

Assumptions

  • I think you are a bad programmer (everyone is expect John Carmack).
  • You can program Typescript with relative ease.
  • You know types, maybe no wizard.
// I assume everyone here can easily read this and understand what is happening.
// here just by type definitions.
type Promiseable<T> = {
  promise: Promise<T>,
  resolve: (value: T) => void,
  reject: (reason: any) => void,
};
type PromiseFactory<T> = () => Promiseable<T>;

function explodePromise<T>(): Promiseable<T> {
  // technically there would be some errors here, but just ignore that.
  let resolve, reject;
  let promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });

  return {
    promise,
    resolve,
    reject,
  };
}

This is the level I would expect, this shouldn't be very hard for you to understand this is just simply a promise that has been exploded and returned. If you can understand how the generic plays into the promise we are looking pretty good.

The only confusion with this code should be why would you do this, this is a good interview question.

Why Rust?

Ergonomics

For me, ergonomics is defined on two axes, one quickly being able to write software with low unexpected behavior, and two maintaining software longer.

This may seem like it would be the counter, as a lot of people say rust is a hard language. Its one of those things that if you can remember your first time programming everything was hard but anytime you have to learn something novel its always super hard. Rust just has more novel things than something like Typescript, Typescript is a fairly easy language you don't have too many hurdles to jump where as Rust has more hurdles but once you have done them there are things that are easy in Rust that are extremely difficult in Typescript.

Javascripts(TS) vs Rust Design decisions

  • Specify Readonly vs Specify Mutability
  • undefined/null vs Option
  • errors being thrown vs being returned

Goals

You end up with enough knowledge to be able to google your way through a small to mid sized cli application in rust.

What we wont cover

  • Errors - creating your own types
  • Wasm / UI
  • async
  • smart pointers and interior mutability
  • lifetimes - WHY NOT??
  • macros, both proc macros and declarative macros
    • thse truly make rust amazing
// ----- v that is a marco!!!
return view! {
    <div>
        <MyCustomComponent name="hello" />
    </div>
}

Comparisions

Variable

const foo = 5; // sort of constant but not really
let foo = 5; // definitely not a constant
const foo = [] as const; // const pointer to a const..
                          // i understand if you don't c the joke
let foo = 5; // real constant
let mut foo = 5; // mutable

Shadowing

// this wouldn't work in typescript
const foo = [...];
const foo = someMethod(foo);
let foo = [...]; // I am of Type A
let foo = someMethod(foo); // I am of Type B - works fine

Why though? One thing that makes shadowing amazing is that you can change types.

let foo = get_file(args); // FileHandle
let foo = read_file(foo); // String
let foo = tokenize_and_do_things_to_string(foo); // Vec<String>

if

if (condition && second || this_one) {
  ...
} else if ...
else
if condition && second || this_one {
  ...
} else if ...
else ...

Loops For

for (let i = 0; i < 10; i++) {
  // ...
}
for i in 0..10 {
}

// inclusive (includes 10)
for i in 0..=10 {
}

While

while (true) {
  // ...
}
while true {
}

For ever?

for (;;) {
  // while (true) {
  // ...
}
loop {
}

Collections?

for (const [key, value] of Object.entries(obj)) {
  // ...
}
for (const value of [1, 2, 3]) {
  // ...
}
for (const idx in [1, 2, 3]) {
  // ...
}

// array#map // copies
// array#filter // copies
// array#forEach // iterates
// array#reduce // always a bad decision
// map#forEach // weirdest interface in the universe

Rust

for x in &some_array {
  // x will be each time of an array
}

vec![1,2,3]
  .iter()
  .map(...)
  // HUGE AMOUNT OF THINGS HERE
  // you can create your own...
  .collect::<Vec<_>>();

Functions

function foo() {}
fn foo() {
}

Parameters

function foo(arg1: number, arg2: number) {}
fn foo(arg1: f64, arg2: f64) {
  // numbers can be a bit complicated
}

Return This is interesting in typescript. You may have to change some habbits

// The return type is based on the code below
// function foo(): number {
function foo() {
  return 5;
}
fn foo() -> usize {
  return 5;
}

Closures

(x) => {
  return x;
};

// or auto return x + 1 like
(x) => x + 1;
|x| {
  return x;
}

|x| x + 1

Class and Methods This one is where the truest magic happens

class Foo {
  properties...

  constructor() { ... }

  methods...

  static methods

  private methods

  protected methods // if you this i'll fire you
}

Pay real close attention

struct Foo {
  properties...
  properties
}

impl Foo {
  // these are both static methods
  fn this() // available usage within the file
  pub fn this() // available usage within the file

  // you should be able to understand this before the end of the day
  // and all of this can add pub
  // these are instance methods
  fn this(&self)...
  fn this(&mut self)...

  // public instance methods
  pub fn this(self)...
}

interfaces

interface Foo {
  properties: type; // gross
  method(): retType;
}

interface Foo {
  hey_another_method(); // i feel many things about this
}
trait Foo {
  // no properties
  fn method(&self) -> retType;
}

impl Foo for MyStruct {
  ...
}

traits compose this may not sound big, but its AMAZING effectively prevents the need for inheritance.

Numbers

// We would call this an interger in rust
4 // this... is technically a smi, but for you purpose, its a  number

// interger becomes a float auto_magically_ in js
4 / 3 = 1.3333333333

// this is totally cool
4 * -1 = -4

None of that was cool for rust Rust you have to specify the types = power of two i = an integer that can be negative or positive (signed) u = an integer that can be positive only (unsigned) f = a number that requires decimal point usize = a u where is your system arch. (64bit = u64) isize = a i where is your system arch. (64bit = i64)

4 / 3 = 1

// cannont divide {float} by {integer}
// yes... this is an error
4.0 / 3 = Nope

// 4 is an i32
4 * -1 = -4

// 4 is an i32
let foo = 4u23; // saying its a 4 that is a u32 (defining type)
foo * -1 // ERROR

If you have ever worked with any static language, this should be pretty straight forward.

The difference between String and &str Yes, you will see there are two types of strings you commonly run into. So what are they?

String

  • Well String is a heap allocated (heap may be a foreign word to you)
  • String can be mutable

&str

  • this points to a sequence of utf-8 characters. Its commonly called a slice. Its a view into a String
  • its immutable
  • its analogous to &[u8] So if i say String, i mean String and if i say stir i mean &str (quick whiteboarding)

The best way to think about this is in JavaScript is you were to pass a string around you don't have to copy all the data. In JavaScript land when you do substring do you get a new string or a reference I honestly have no idea.

You don't have control in JavaScript on how memory works.

Basics on Rust

Just some basics so we can understand things going forward

When you are starting out using rust you should see

  1. unwraps
  2. clonse

That is totally normal, completely fine. understanding, at least for me, comes in waves. The more I understand, the more I realize I understand less.

Vector Vec (rust) and are very similar. Their behaviors are near identical. These two are functionally equivalent

const a = [1,2,3,4,5] as const;
let a = vec![1,2,3,4,5];

Mutation

const a = [1, 2, 3, 4, 5];
a.push(6); // [1,2,3,4,5,6] // returns size
let a = vec![1,2,3,4,5];
a.push(6); // Error: a is not mutable

// but with rust we can shadow
let mut a = a;
a.push(6); // [1,2,3,4,5,6] // does not return size
const a = [1, 2, 3, 4, 5];
a.pop(); // [1,2,3,4] undefined or T
let mut a = vec![1,2,3,4,5];
a.pop(); // [1,2,3,4] Option<T>

Accessing Data

const a = [1,2,3,4,5] as const;
const item = a[2];
let a = vec![1,2,3,4,5];
let item = a[2]; // does work, but if out of bounds, panic
let item = a.get(2); // better, returns Option<T> where T can be i32

An Option is a possible undefined value. All things could be undefined, must be specified with an Option. We will talk about enums an Options in depth shortly

Tuple This doesn't really have a similarity in javascript

let a = (5, String::from("hello")); // this type is (i32, String)

it is "near" equivalent to

const a = [5, "hello"];

You can pattern match (think destructuring) tuples;

let a = (5, String::from("hello")); // this type is (i32, String)

// you probably best know thius as destructuring, but we will refer to this
// as pattern matching.
let (my_num, my_str) = a;

You can even pattern match in a function

let a = (5, String::from("hello")); // this type is (i32, String)

fn bar((my_num, my_str): (i32, String)) {

}

bar(a)

Structs

struct MyStruct {
  x: usize,
  y: usize,
  z: usize,
}

fn bar(MyStruct { y, z, .. }: MyStruct) -> bool {
  return y * z < 100;
}

fn main() {

  let foo = MyStruct {
    x: 69,
    y: 420,
    z: 1337
  }

  let MyStruct { x, .. } = foo;
  let MyStruct { x, y, .. } = foo;
  let MyStruct { x, y, z, .. } = foo;

  if let MyStruct { x, .. } = foo {
    println!("things about x {}", x)
  }
}

unwrap, todo, u

// todo is good for throwing an error when you want to implement the function later
fn not_complete_fn(x: usize) -> bool {
    if x < 10 {
        return true;
    }

    todo!("finish this later");
}

// unreachable will throw an error if the code is reached
fn only_evens(x: usize) -> bool {
    if x % 2 == 1 {
        unreachable!("this should never happen");
    }

    return true;
}

// unwrap will force the optional to be not optional but it is dangerous
fn main() {
    let foo = Some(5);
    let bar = foo.unwrap();

    println!("Hello, world!");
}

Value, Mutable, Immutable

One more thing about rust You will hear me say the following words

  • value
  • mutable reference or mutable borrow
  • immutable reference or immutable borrow

In rust you can have:

x = 5
The 5 is a value, it is something that is sitting there in memory. It is the thing.
The owner of said value is x, in rust land you can refer to a value.
y = &x
That means y refers to x, if you were to print out y it would always be the same as x. This is a read only reference.
y = &mut x
This means not only can I look at the data but it means I can also change the data. Not really done in any other languages. For you to be able to do &mut the original variable needs to be mut so:
mut x = 5

fn do_this(&self)
This means its a function that refers to self but it is not self it does not contain the value but it contains a reference.

fn do_this(&mut self)
This means you can only call it on a function on a object that is defined as mutable, that way you can seperate functions that mutate vs functions that don't mutate.
  • reference: means readonly
  • mutable reference: means write & read
  • value: means the thing itself

Borrow checker: In rust when you program there can be only one value owner, you can have as many readonly references as long as there are no write and read references out. You can only have one write reference and no read references.

More technical version of mutable reference or immutable reference is mutable borrow or immutable borrow. This will be in the complier errors.

() is a unit () is called "unit". Effectively it is "nothing" value.

if true {
  println!("foo");
}

// I could write this
// --v this has type ()
let foo = if true {
  println!("foo");
}

You will see this often: Won't make sense right now.

fn only_evens(x: usize) -> Result<()> {
  return Ok(())
}

This means i'm returning the unit of nothing, it doesn't exist but hey we are okay. Everything went Ok in this function, but we used the nothing unit because we have no value. Sometimes you have functions that have side effects that don't actually do anything for you. It's not like undefined.

Coding an Iterator

To begin with, There are 2 things that have to be understood in Rust. This is for fundamental understanding of the language.

  1. Iterators
  2. Enums

Once you get these two, it becomes easier to work with rust initially. As these concepts are a bit wonkey coming from TypeScript.

Often in TypeScript you iterate but you don't use iterators.

Lets start with iterators.

// If you have a vector which is just a list, a iterator is a seperate data structure.
// That can walk through that data.
// Doesn't matter what the data structure is, if there is an order an iterator can walk through it.
vec![1,2,3]
{
  prt => "["
  idx:
}
// You would have a reference to this iterator and call something like .next() this will give the next value out.

Iterators I think iterators will make the easiest transition as they have the strongest similarity in javascript and we can start with .map

.map is not an iterator it iterates not the same but quite similar but completely different at the same time.

Closures

Closures are different in JavaScript, when we refer to closures in JavaScript we are saying the value is closed of the function and can't be accessed outside of the closure.

In rust a closure is: |x| x + 1 In JavaScript this is a lambda function i.e. x => x + 1

What is collect? The Iterator is its own data type. So we must convert from an iterator back into the struct we want and in our case its a Vec

Borrow checker example

This is an error

let mut foo = vec![1,2,3]
  .iter()
  .map(|x| x + 1);

while let Some(x) = foo.next() {
}

This is because this iterator refers to the vec but who owns the vector nobody, how long does this value live it was created temp and then it went away. So now we have a reference to nothing so this needs to be.

let data = vec![1,2,3];
let mut foo = data
  .iter()
  .map(|x| x + 1);

while let Some(x) = foo.next() {
}

Its important to think how things live in memory.

rust-for-typescript's People

Contributors

alexanderdunlop avatar

Stargazers

Connor John avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.