The fundamentals of the languages.

We are going to go over some fundamentals of each language to hopefully make the transitioning easier between these 3 languages.


Remember, this is fast paced, but there is room for questions, as I have even made explicit stops. If you are viewing this live on FrontEndMasters.com or twitch.tv/ThePrimeagen and have a question, please feel free to throw it in the chat and hopefully I can answer it!

















Syntax

I am not going to cover language specifics and syntax.

Example: what happens here? If you have some experience with rust, please don't answer.

let foo = if boolean_statement {
    0
} else {
    1
};
















TypeScript version

const foo = boolean_statement ? 0 : 1;

So if you do have a question, speak up during those times and I'll go over anything specific. But the goal here is to make a course that is geared towards people who feel comfortable with programming and would like to pick up a second or third language.

















Rust Borrow Checker

Rust is famous for difficulty. For its borrow checker. There are memes and I personally have rage quit using rust because I didn't understand the basics of rust.

From the rearview mirror, this is simple

















Lets start with other programming languages

JavaScript

const a = [];
const b = a;
b.push(5);
console.log(a);
console.log(b);
















C++

std::vector<int> a;
std::vector<int> b = a;
b.push_back(5);

printf("a size: %zu\n", a.size());
printf("b size: %zu\n", b.size());
















Rust

let a: Vec<i32> = vec![];
let mut b = a;
b.push(5);

println!("a size: {}", a.len());
println!("b size: {}", b.len());
















The three types

  • Value
  • Reference
  • Mutable Reference

The three simple rules

Rule #1: Value

Only one value owner.

Note

If the object implements copy, it can be implicitly copied

let x = 5;
let y = x;

println!("{}", x + y);

Rule #2: Reference

You can have as many references as you like with the constraint that there are no mutable references alive.

let x = 5;
let y = &x;

println!("here is {} and {}", x, y);

Rule #3:1 Mut Reference

You can have one mut reference and no reference at the same time.

fn main() {
    let mut x = 5;
    let y = &x;
    let z = &mut x; // cannot borrow x as mutable
                    // because its already as immutable
    println!("{}", x + y + z);
}
















Questions?

















Moar things to cover

  • Rust + Enums (Options)
    • Options
  • Error Handling
    • Results
  • Testing
















Enums

Lets look at emuns in typescript, go, and rust.

First typescript, lets program up a quick example.

















I should have typed something like this.

enum Thing {
    Foo = 1, // or give them no value and it will start at 0 and increment
    Bar = 2,
    Baz = 3,
}
















What about go?

Onto another example!

















Code

type Foo = int

const (
    Thing Foo = iota
    Other
    That
)
















Now Rust

















Or is this typescirpt?

enum Thing {
    Foo, // or give them no value and it will start at 0 and increment
    Bar,
    Baz,
}
















Why go over enums...

They are simple constructs. Well, they are simple in other languages.

Lets dive more into them with rust. Let me show you how you can add types...

















What my code should of approximately looked like

enum Option2 {
    Baz,
    Foo(isize),
    Bar(String),
    Fuzz(Vec<String>), // string[], or a []string
}

fn main() {
    let opt2 = Option2::Foo(5);

    let mut opt22 = Option2::Fuzz(vec![]);

    if let Option2::Foo(x) = opt2 {
        let _ = x + 5;
        // x = 7;
    }

    if let Option2::Fuzz(vec) = &mut opt22 {
        vec.push(String::from("Hello, world!"));
    }

    match opt2 {
        Option2::Baz => todo!(),
        Option2::Foo(_) => todo!(),
        Option2::Bar(_) => todo!(),
        Option2::Fuzz(_) => todo!(),
    }
}
















What does this mean?

This means that you can have an enum with many types, and these types can be generic.

enum Foo<T> {
    Bar(T)
}
















But how is this practically useful?

3 things.

  1. lists with many types
  2. Nullable
  3. Error Handling
















Lets start with nullable and TypeScript

I think we have all seen code like this

type Foo = {
    bar?: number;
}

function test(foo: Foo) {
    if (foo.bar) { // this is annoying, yes
        // undang
    } else {
        // dang
    }
}
















Let me show you nullables in Rust

These are Options, they are enums, they have a generic.

enum Option<T> {
    None,
    Some(T)
}

Lets see how you can play with these in Rust

















Potential code for options

struct Foo {
    bar: Option<i32>
}

fn main() {
    let foo = Foo {
        bar: None
    };

    let foo2 = Foo {
        bar: Some(2)
    };

    if foo.bar.is_some() {
        let sum = foo.bar.unwrap() + 5;
    }

    foo.bar.unwrap_or(0);

    foo.bar.unwrap_or_else(|| {
        return 5;
    });

    let out = foo.bar.map(|x| {
        return x + 5;
    });
}
















Questions so far?

















Lets implement the Option enum!

For fun lets try to implement map and is_some in rust on our "option" type.

















Results of this

enum Option2<T> {
    None,
    Some(T)
}

impl<T> Option2<T> {
    pub fn map(&self, f: fn(&T) -> T) -> Option2<T> {
        return match self {
            Option2::None => Option2::None,
            Option2::Some(v) => Option2::Some(f(v)),
        }
    }

    pub fn is_some(&self) -> bool {
        return match self {
            Option2::None => false,
            Option2::Some(_) => true,
        }
    }
}

fn main() {
    let opt = Some(5);
    let opt2 = Option2::Some(5);

    opt.map(|x| x + 5);
    let opt2 = opt2.map(|x| x + 5);

    if opt2.is_some() {

    }
}
















Questions? That was pretty radical section

Hopefully you can see the incredible value of sumtypes.

There is this concept in 1984 that peoples ability to think is directly tied with the language they communicate with. I think this is true in programming as well. This is one of the reasons learning a ton of languages is REALLY beneficial.

















What about error handling?

















JavaScript (TypeScript)

"Exceptions as control flow"

2 types of errors that you will run across.

  1. returned errors
  2. thrown errors

Pretty classic javascriptism -- conflation issues

Let me program you a live example!

















Code

function foo() {
    throw new Error("Goodbye, World");
}

try {
    foo();
} catch (e) {
    console.log("We had a problem, but we are ok", e);
}

console.log("great success()");
















In general TypeScript uses exceptions for control flow and with promises its a mix of value vs throwing due to .catch.

not all errors can be caught and will just simply blow up somewhere...

















Lets look at Go

We haven't done much of go, but it does differ here from typescript.

This is one of the most fundamental arguments against and for go is its error handling. I will say that the error handling i find better than typescript but definitely more boilerplate to deal with it.

The reason why i like it is because of control flow and where things can go wrong.

Example time!

Remember, errors are just values

  • create error
  • return error + struct
















The go code

package main

import (
    "errors"
    "fmt"
)

func example() error {
    return fmt.Errorf("here is an error with a string");
}

func otherExample() error {
    return errors.New("here is an error, but with errors") // approx same thing
}


// errors are pointers under the hood, so you can return the empty type
func exampleNoError() error {
    return nil;
}

type Thing struct { }

func exampleWithData(should bool) (*Thing, error) {
    if should {
        return &Thing{}, nil
    }
    return nil, fmt.Errorf("nice try, guy")
}

func main() {
    err := example();
    if err != nil {
        // handle error
    }

    _, err = exampleWithData(true)
    if err != nil {
        // handle error
    }
}
















Rust

Remember those enums (sumtypes) and how I told you they handled errors? Well, here is the Result type.

type Result<V, E> {
    Err(E),
    Ok(V)
}

Lets make some examples of how to use them!

















The code I wrote on a sunday morning

I am sure there is a Johnny Cash reference somewhere around here.

fn error(num: i32) -> Result<(), usize> {
    if num < 0 {
        return Err((num * -1) as usize);
    }

    return Ok(());
}

fn main() -> Result<(), usize> {
    let res = error(5);

    if res.is_ok() {
        //...
    }

    match res {
        Err(e) => // ...
        Ok(v) => // ...
    }

    let x = res.unwrap_or(());
    let x = res.expect("THIS BETTER EXIST");
    let x = res.unwrap(); // BAD

    let x = res?;

    return Ok(());
}
















Anyhow?

A nice library for writing great code with error handling is anyhow! Lets look at it

















Unit Testing!

  • TypeScript : Cries in Configuration
  • GoLang : Meh
  • Rust : oyes
















src/__tests__/test.ts
test("foo", function() {
    expect("foo").toEqual("foo");
});
yarn add jest ts-jest @types/jest
npx jest
















Go version

there is some contention with how / where to put your tests.

  • test public interfaces only
  • test within the package
pkg/name/file.go
pkg/name/file_test.go
package name_test

import "testing"

func TestThisFunc(t *testing.T) {
    this := 5
    if this != 7 {
        t.Errorf("expected %v to equal 7", this)
    }
}
go test ./...
go test ./path/to/package
















Go does have an assertion library

But we will not be using it. We will just use what is built in during this course.

















Rust Version

Rust, of course, is the best

  • test in file
  • One thing to be careful of is to what level private interfaces should be tested.
















... // code ...

#[cfg(test)]
mod test {
    #[test]
    fn this_test() {
        assert_eq!(5, 7);
    }
}
cargo test
cargo test path/to/file.rs
















You will forget everything i just said

That is ok. The best way to make it set? Build it.