Skip to content
Kevin Kredit edited this page Mar 24, 2020 · 6 revisions

Notes and lessons from the Rust programming language.

Language Features

Data

Data

  • Immutable by default
  • Easily printable through formatting traits, which optional formatting args for dec vs hex, e.g.
  • Array out-of-bounds fault is an compile-time error
  • Constants my be declared in any scope, and have two types:
    • const: unchangeable value
    • static: possibly mutable with a 'static lifetime; any use is unsafe

Types

  • Static
  • Declared or inferred
  • Literals can be type annotated
  • Support for tuples
  • Support for traits, which are like typeclasses or interfaces
  • Lists:
    • Array: collections of the same type; size known at compile time; signature [T]; on stack
    • Slice: arrays, but size not known at compile time; comprised of a pointer and a length; signature &[T]
    • Vec: growable arrays allocated on the heap
  • Structs:
    • Tuple structs (basically named tuples)
    • "Classic C structs"
    • Unit structs (empty, useful for generics)
  • Enums:
    • Creates a type that can be any type of struct, and even a mix! Kind of like a big Either. See here
  • Type aliases: type Alias = OldTypeName;
    • UpperCamelCase or produce a warning
    • Do not provide type safety, as they're not new types
  • Conversion is done using traits
  • ! is the divergent type; it matches all other types, but indicates that something does not return

Variables

  • Declared with let; types declared or inferred
  • As mentioned in the data section, variables are immutable by default; let mut declares mutable variables
  • Scoped to blocks, {}
  • Shadowing is allowed
    • Can shadow in a lower scope
    • Can shadow in same scope by redeclaring with let, which can also change types
  • May be cast using as; C-like conventions, except fully defined
  • Holes _ are allowed if you don't need to bind a variable

Structure and Control

Syntax

  • Uses semicolons (sadly to me), but they have meaning
  • Expression + ; = Statement (see here)
    • Variables are assigned from expressions
    • Blocks are expressions
    • Variables can be assigned from blocks if the last statement in the block is not suppressed with a semicolons
    • Similarly, functions do not need an explicit return at the end, but can end with an expression
  • if/else conditions don't need (), and are expressions--so can be used directly in assignments
  • loop: infinite loops
    • Standard break and continue
    • Can also annotate loops with 'names and break or continue from that named loop
    • break can also "return" a value for the loop expression
  • while: standard
  • Ranges can be built using 1..5; 1..=5 is inclusive
  • for uses 3 types of iterator (let's use names as an example vector):
    • names.iter(): borrows each element along the way; names is still available for use after the loop
    • names.into_iter(): consumes the collection; names has been "moved"
    • names.iter_mut(): mutable version of .iter()
  • Pattern matching using match
    • Can be used like a C switch
    • Can be used to de-structure tuples, enums, pointers, and structures
    • Supports guards (yes!)
    • Can bind the matched value using @
  • if let: lighter weight pattern matching often used to unwrap Options and match Enum types
  • while let: while-version of if-let

Macros

  • Highly capable macro system
  • C-like, but considerably more powerful since it uses type information instead of pure string manipulation
  • Macros defined using macro_rules! macro_name {}
  • Examples here

Functions

  • return is not mandatory; expressions == statements
  • Only one return value, but it can be a tuple
  • Declared with fn, signatures like `fn fn_name(var: type) -> returnType {}
  • methods are functions attached to objects; similar to Go; defined in impl blocks
  • Closures!
    • Types inferred, var names must be specified
    • Vars in |var|, brackets {} necessary only if only one expression
    • Can have argument-less closures by let closure = || expr;
    • Capture variables from enclosing scopes, so has borrowing implications
    • Any function can be used as an argument; inside the receiving function, it is a closure
  • Rust provides at least a few higher-order functions:
    • Iterators can be lazy, giving a Haskell-ish feel (if not runtime behavior, thankfully)
    • map, filter, fold, take_while, for_each
    • See Rust's iter documentation
    • Performance: they follow C++'s "zero overhead" principle (see here)

Objects and Interfaces

  • struct objects may have methods attached, creating Go-like interfaces; see functions section
  • traits are sets of methods than an object must implement in order to satisfy interfaces
  • Some traits can be derived

Error Handling

  • Forced by type system with Maybes
  • unsafe code (see here)
    • Must put in unsafe {} blocks
    • Limits scope of and makes explicit unsafe operations
    • Operations include:
      • dereferencing raw pointers
      • calling functions or methods which are unsafe (including calling a function over FFI)
      • accessing or modifying static mutable variables
      • implementing unsafe traits

Tooling

  • Installation process is simple
  • Compiler warnings and errors:
    • Helpful, and has many sensible defaults (e.g. warnings for unused variables)
    • Handy built-in conventions (e.g. no warnings for vars starting with _)
    • Enforces (or tries to enforce) complete pattern matching
  • Debugging
    • Easy to setup by following this blog post
    • Must build with rustc -g option

Feel

Initial reaction: I expected to instantly find my next favorite language, but the syntax isn't very pretty.

Later reaction: Rust feels strong and safe. You can appreciate the modern features such as its Maybes, its approach to objects and types, and its light functional capabilities, all while appreciating its strong types, helpful compiler, and promises of memory safety. Parts of the syntax are undoubtedly ugly, and in this time I have not completed the learning curve. However, syntax gets uglier when you try to have an expressive language, and embedding complexity in the language itself means you don't have to work around the language (c.f. waterbed theory). I like Rust.

Clone this wiki locally