about
10/11/2024
Distinguishing Rust
Rust Bites Code

Distinguishing Rust

what Rust omits, changes, requires, and supports

About
click header to toggle Rust Explorer
"Do the best you can until you know better. Then when you know better, do better"
- Maya Angelou

Synopsis:

Rust has syntax and semantics similar to, but simpler than C++. And it shares some language features with C#. But it has a unique type system that supports memory and thread safety by construction without the need for garbage collection. The Rust code development infrastructure is very well designed and easy to use. In this post we largely ignore infrastructure comparisons in order to focus on the language of Rust and what makes it unique.

1.0 Rust Programming Language

Rust is an interesting language, similar to C++ with the same performance potential, but also incorporates some unique and clever ideas that allow it to make guarantees, by construction, about freedom from memory handling defects and race conditions. You may find it a bit disconcerting at first to work with Rust compiler's safety Oracle, called the "borrow-checker" (Rust docs refer to references as borrows). Soon however, you will come to think of the borrow-checker as a stern friend, often very helpful. Here's a great reference for understanding borrows: intorust. With a bit of practice you may find Rust to be less complex than C++, largely because it has far fewer context dependencies. That is due, in large measure, to Rust's type system design. Table 1., below, lists things that distinguish the Rust programing language from C++ and C#. Rust provides performance and memory management in a manner similar to C++ but adds memory and data race safety by construction. It delivers memory safe operation like C#, but without performance penalties and non-deterministic release of resources due to garbage collection. The table gives a quick overview of what makes Rust the language that it is, describing features briefly and providing a few small code examples. References supply additional details.

Table 1. What Rust Omits, Changes, Requires, and Supports

Rust Omits: References:
Garbage Collection.
Rust uses C++ style scope-based resource management. Its clever design prevents unsound memory accesses and data races. That's achieved with compile-time reference analysis augmented with occasional run-time checks. Raw pointers are not allowed. Indirection is achieved with references that are pointers wrapped in a strict ownership policy.
Ownership model
Safety
function overloading.
Not required since constructors do not share the type name. Rust generics and traits allow you to define functions that will operate over a set of types.
internals.rust-lang.org
StackOverflow
inheritance of non-static implementation
Rust supports inheriting one or more traits which may have function definitions, but can not have data members. A struct inherits a trait by an impl statement:
impl SomeTrait for MyStruct { ... }
Traits - The Book
playground example
implicit conversion between types. All expressions must have exact type matches.
Rust supports casting with the "as" keyword, and provides conversion functions for some standard types. Application programs may also provide conversion methods.
Casting - Rust by example
dereferencing raw pointers outside unsafe blocks.
Our goal: use no unsafe blocks, deferring instead to std::library facilities where the Rust designers used, and carefully vetted, unsafe blocks to provide functionality we need.
Unsafe - The Book
Unsafe - how and when
Rust Changes: References:
Objects - Rust declares the layout of objects, a.k.a instances, using the struct keyword.
Structs and their child members are accessible anywhere in the crate1 of definition, but require a pub declaration to become accessible in another crate. Methods are declared in a separate "impl" block.
  1. Crates are the Rust unit of compilation, containing a src folder and a Cargo.toml file to define versions and dependencies.
OOP - The Book
Copy Types - Only types with contiguous memory are implicitly copied.
That is the set of primitive types and aggregates that have only copy type members.
Move Types - All else are implicitly moved.
Operations of construction, assignment, passing arguments and returning results from functions by value will move ownership of resources from source to destination. That's fast, usually just a few bytes copied.
Move invalidates the source of the "moved" operation. Using a moved variable is a compile error. This is one of two frequent Gotchas for new-comers to Rust.
RustBites copy & move
playground example
semantics for copy types and move types are fixed.
Unlike C++, you don't have to, and can't, define implicitly called copy and move constructors and assignment operators.
User defined semantics are not needed due to the way data is managed in Rust.
Moves, Copies, and Clones
Rust supports deep clones and defines a Clone trait that cloneable types implement.
Clones make deep copies and are always invoked explicitly.
RustBites clone
playground example
Rust enumerations have elements that may wrap instances of specified types.
enum StepState { NotStarted, Reading { refs: Vec<String> }, ... }
enums - The Book
RustBites Enums
playground example
Rust enumerations are often used with a matching syntax.
match state {
  StepState::NotStarted => print!("\n getting late"),
  StepState::Reading { refs } => print!("\n reading {:?}", refs),
   ...
}
Patterns and Matching
RustBites Matching
playground example
Rust iterators are similar to C# iterators. They have many predefined adapters.
Strings have an iterator over their characters, chars().
let third_char = my_string.chars().nth(2);
RustBits iterators
RustStory iterators
RustBites iterators
std::iter::Iterator
Rust Requires: References:
Explicit declaration of mutability:
let mut x = 42;
let rx = &mut x;
RustBites Data
playground example
No concurrent aliasing and mutation (with references)
This is the other common GotCha for those new to Rust.
This means, essentially, that a Rust program cannot read or write to memory it does not own. One nearly equivalent statement is that Rust will not change the contents a reference points to unless the reference is mutable and responsible for the change.
It also means that it is difficult to build linked data structures using references in Rust. You can build them in Rust, but it is much easier to build them using indexes in a vector container.
We will (eventually) illustrate that with a Directed Graph type that holds its vertices in a Vec<Vertex>.
RustBites Safety
playground example
tree with ref links
References must not outlive their referends.
The Rust compiler's "borrow-checker" analyzes reference lifetimes and refuses to compile cases where it cannot prove this invariant.
Usually it can do that silently, but, on occasion it needs help and askes you to provide lifetime annotations. To see an example, look at RustBites Pluggin reference.
Understanding lifetimes
RustBites Pluggin
Only one owner of data
Owned resources are released when owner goes out of scope, and transferred when owner is moved.
Ownership by example
Direct access to heap storage uses the smart pointer Box<T>.
Using Box<T> places t ε T in the heap and implicitly dereferences the Box pointer, giving code access to T's interface. Most of the Rust std::library containers place their contents in the heap, e.g., vector, dequeue, map, ...
RustBites_SmrtPtrs
playground example
Function invocations on a generic type, T, require a type constrait on T using a trait declaring that function.
fn demo<T>(rt: &T) where T:Clone { let tc = rt.clone(); // use tc }
Here, clone() function is invoked on generic type rt with Clone constraint.
Traits - The Rust Book playground example
Rust Supports: References:
Rust supports error handling with enums Result<T, E> and Option<T>:
enum Result<T, E> { Ok(T), Err(E) }
enum Option<T> { Some(T), None }
RustBites Error Handling
RustStory Error Handling
RustBites Options
Rust Strings are collections of utf-8 characters, which may have sizes from 1 to 4 bytes. That enables Strings to contain texts using diacritics, arabic scripts, Sanskrit, Hanzi, kangi, ...
It also means that Strings cannot be indexed. Program code uses, instead, the iterator chars() that knows how to detect bit sequences denoting character boundaries.
RustBites_Strings
RustBites_DataStr
RustStory_Data
std::String
std::str
Function and Type declarations and definitions may be ordered without concern for dependencies within a single file.
Functions - The Rust Book Rust Ref Statements
Panic on integer underflow or overflow in debug mode. Rust the Book
Rust std::ffi library provides OsString which wraps platform specific strings and OsStr, a literal string with platform enccoding. std::ffi::OsString
std::ffi::OsStr
Rust provides PathBuf and Path which wrap OsString and OsStr, respectively, adding methods for handling paths . std::path::PathBuf, std::path::Path
Rust can generate WebAssembly code and Rust Playground can show both Rust code and WASM it generates.
Rust and WebAssembly
Table 1. summarized all of the major features of Rust and provided links to references and sample code. This has been a "show and tell" that's intended to help you acquire an accurate mindset for the Rust language.
Rust Highs and Lows
The references in Table 2. are great places to get a quick view of Rust and find out what it is all about. Most won't take too much of your time - a few hours or so for each.
Table 2. - References for Starting Rust
Start these references after you've looked at the contents of Table 1.

Table 2. - Starting Rust at the trailhead

Topics Descriptions
The Rust Programming Language, a.k.a, The Book Usual starting place in the Rust documentation.
Rust Documentation Officially sanctioned learning materials.
Ownership by Example Nicely organized and relatively complete discussion of ownership with simple clear examples.
A half-hour to learn Rust An unusual tour of Rust using small code fragments and quirky commentary.
Rust playground Playground is a tool for compiling and executing Rust code from a web interface. It's a great way to try out Rust syntax and operations.
RustTour.pdf
A presentation summarizing essential features of Rust.
RustErrorHandling.pdf A presentation covering interesting mechanisms that Rust provides for handling program run-time errors.
Contents of the table, below, will help you acquire much deeper knowledge of Rust with reference material and e-books. These can be good companions on the Rust road. There are many more specialized references in the Reference link, but these are the place to start.
Table 3. - References with more details

Table 3. - Deeper into Rust

Topic Description
Rust by Example Large collection of examples across the Rust language by the Rust team.
Little Book of Rust Books An e-Book surveying the best e-Books for Rust. Lots here I didn't know about until I saw this.
Rust Guide Definition and examples of Rust collections, iterators, and concurrency constructs.
Cheats.rs Comprehensive well-organized collection of code snippets with comments.
RustBites Sequence of pages on this site, each of which focuses on a single Rust topic
Rust Story Narrative discussion of Rust in six chapters
easy rust Github site with well written collection of reference materials using a clear simple style with lots of details. Also accompanied with a collection of tutorial videos. Recommended once you are getting up to speed (maybe even before then).
std lib Excellent documentation for the Rust std libraries. Very readable with access to source code.
Rust Story References The place to look for specialized topics and blogs
  Next Prev Pages Sections About Keys