about
03/12/2023
RustBites - Starting
Code Repository Code Examples Rust Bites Repo

RustBites - Getting Started

Deeper look at "Why Rust?"   Make sure you look at Table 1

Even if you're on the right track, you'll get run over if you just sit there.
- Will Rogers

1.0 What is Rust?

Rust is an interesting language that is 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. Rust compiles to native code, safely working with data without using a garbage collector. Resources acquired by an instance of some type like Vec<T> are released when it goes out of scope. All heap memory is managed using a smart pointer type Box<T> that releases its heap resource when it goes out of scope.
This is a "Rust Short" - a brief video summarizing the Rust Programming language.
You will probably want to view "picture-in-picture" or "full-screen" so details are large enough to be seen easily.
Running as "picture in picture" allows you to move to other web pages while still watching and listening to the video.
 

2.0 Performance

Run-time performance of Rust code is comparable to C++. You may be interested to look at a comparison of two implementations of a message-passing channel using thread-safe blocking queues, one written with C++ and the other in Rust. The project: CommComp shows the performance of both designs, measured in MBytes per Sec, on several different platforms. That demonstates that the C++ and Rust implementations have essentially the same throughput.
 

3.0 Rust's Look and Feel

Once you are used to the language, programming in Rust feels a lot like using a simpler C++ with some nice features from C# sprinkled in. Add to that an Oracle, sitting at your elbow, with firm opinions about safe code. It will fail builds if your code doesn't satisfy its invariants: no uninialized or null references, no sharing of mutability through references, and referend lifetimes must match or exceed the life time of the reference. The Oracle makes up for its enforcements with very clear error messages that tell you where the offending lines of code are found, and often tell you how to fix the problem it detected.
Thinking about Rust's Safety rules These invariants are strong conditions that essentially imply that no program code can allow data to change if it is the target of a reference. That makes reasoning about program behavior much simpler, but also seems to mean that some things we may wish to build will not compile. Think, for example, of a directed graph. If we use references to link nodes together, most graphs would violate these safety rules. However, we can build graph classes in Rust. We just can't represent edges with references. Instead We move nodes into a vector and establish node linkage using vector indexing. The safety rules do not include indexes because indexes are not invalidated when the vector reallocates its storage to provide room for more nodes. After a year of intensive coding with Rust, building more than 20 repositories of code, I was able to build everything without unusual contortions or resorting to unsafe code blocks. And, I spent a lot less time debugging than I have with other languages because the compiler's great error messages guided me toward sound implementations and I didn't need to chase memory handling defects and data races.
You may find it a bit disconcerting at first to work with Rust'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.
 

3.1 Code Sample

The dropdown below contains two examples of Rust code that emphasize creating and calling functions and methods. Scanning them quickly may give you a taste of how Rust code operates.
Function Examples Below, in Figure 1., you see Rust code that implements a factorial function in a slightly unusual way. It uses, in factorial(i:u64) -> u64 an iterator (2..=i) over the range of intergers from 2 up to and including the value of i. It begins iterating when its function product() is invoked. There are a number of functions defined by the iterator trait that make many computations like this sytactically simple. The first function, demo_iter(i:64), illustrates how iterators work. We usually don't explicitly call the iterator function next(), but rather let other iterator functions, like product() do that. Not only do they call next(), they also handle the Option that indicates that there are or are not any more items to access. You probably have questions about some things you see here but notice that the code looks familiar, with clear and efficient syntax. References for Iterators and Options are provided below, along with a link to the Rust Playground with this code installed. You can run it, modify the code, and see what happens.
Figure 1. Source for Factorial Demo /*-- demonstrate simple iterator --*/ fn demo_iter(i: u64) { /* iterator over 0 to i */ let mut iter = 0..=i; /* iter.next() returns Option<Some(j), None> */ loop { let opt = iter.next(); if opt.is_some() { print!( "\n iter item = {}", opt.unwrap() ); /* unwrap returns value of Some else panics */ } else { } } } /*-- compute factorial with iterator Wikipedia: Rust Programming --*/ fn factorial(i: u64) -> u64 { (2..=i).product() } fn main() { demo_iter(5); println!(); for i in 0..=5 { print!( "\n factorial of {} = {}", i, factorial(i) ); } } Output iter item = 0 iter item = 1 iter item = 2 iter item = 3 iter item = 4 iter item = 5 factorial of 0 = 1 factorial of 1 = 1 factorial of 2 = 2 factorial of 3 = 6 factorial of 4 = 24 factorial of 5 = 120   References Rust playground provides a web-based environment for testing out small code ideas and looking at assembly or WAsm representations. You can make changes to this demo code to see what happens.
 

Code Sample - user-defined types:

Below, in Figure 2., you glimpse a little bit of code that implements a user defined type in Rust. You may have questions about some things you see here but notice, as before, that code looks familiar, with clear and efficient syntax.
Figure 1. Source for User-Defined Type // CreateObject::main.rs /*----------------------------------------------- - Declare DemoObj struct, like C++ class - Request compiler impl traits Debug & Clone */ #[derive(Debug, Clone)] pub struct DemoObj { name : String } /*-- implement functions new and name --*/ impl DemoObj { pub fn new(obj_name: &str) -> DemoObj { DemoObj { name: obj_name.to_string() } } pub fn name(&self) -> &str { &self.name } } /*-- Demo DemoObj instance in action --*/ fn main() { print!( "\n -- demonstrate object creation --\n" ); let dob = DemoObj::new("Frank"); print!( "\n DemoObj instance name is {:?}", dob.name() ); let cdob = dob.clone(); print!( "\n name of clone is {:?}", cdob.name() ); print!("\n\n That's all Folks!\n\n"); } Output cargo run -q -- demonstrate object creation -- DemoObj instance name is "Frank" name of clone is "Frank" That's all Folks!   References
Our main purpose here is to present Rust fundamentals in a quick, but easy to grasp way. Table 1. is where that begins.
 

4.0 So What is Rust?

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.
Safety model
Ownership model
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 a trait 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:
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 resources from source to destination. That's fast, usually just a pointer copy.
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.
RustBites_Data
Rust supports deep clones and defines a Clone trait that cloneable types implement.
Clones make deep copies and are always invoked explicitly.
RustBites_Data clone
playground example
Rust enumerations have elements that may wrap instances of specified types.
enum StepState { NotStarted, Reading { refs: Vec<String> }, ... }
RustStory_Data enums
RustBites_ErrHnd
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),
   ...
}
RustStory_Data enums
RustBites_ErrHnd
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);
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
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_Options.
Understanding lifetimes
RustBites_Options
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 byte 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
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

First Steps:

References in Table 2.a are a great way to get started. You can go through all in a morning and wind up with a clear idea of what Rust is all about. Pay special attention to the "Considering Rust" video.

Table 2.a - Getting your arms around Rust

Topic Description link
Why Rust? Quick survey of the main Rust concepts. Why Rust?
Rust Intro Basic introduction in three articles: Basics, Enums and Matching, and Generics. A bit long in places, but with interesting example codes. Rust in a NutShell
Ownership Very clear discussion of ownership in five short videos. intorust
Rust Survey Smart discussion that introduces the most important parts of Rust Considering Rust video
From this site, you may find explanations and references in Table 2.b useful. They summarize the beginning core of Rust. The entries are more detailed than those in Table 2.a but should be fairly quick to digest.

Table 2.b - First things to view from this site

Bite Selected Rust Bites from this site Link
Starting This page RustBites_GettingStarting
Tooling Installing Rust, Installing Visual Studio Code, setting up, basic work flow RustBites_Tooling
Safety Rust's safety model Rustbites_Safety
Facts Quick summaries of basic Rust language information Rustbites_Facts
Flash Cards Small discussions of important Rust collections, traits, and types RustBites_FlashCards
Data Structures References for primitive and common std library types RustBites_DataStr
If you are fairly new to software development you may want to stop here and look over the first three BuildOn Steps to get familiar with our project. Then follow instructions in Tooling to set up Rust and Visual Studio Code. After a break, you can try some of the "Rust in a Nutshell" examples, copying the screen code into the VS Code editor, then compiling with cargo in the attached terminal window.
The references in Table 3. are great places to get a quick tast of Rust and find out what it is all about. Most won't take too much of your time - an hour or so for each.
Table 3. - References for Starting Rust
Start these references after you've looked at the contents of Table 1a.

Table 3. - Starting Rust at the trailhead

Topics Descriptions
Ownership by Example Nicely organized and relatively complete discussion of ownership with simple clear examples.
Introducing Structs, traits, and zero-cost abstractions A YouTube video that uses simple examples for building user-defined types and abstracting out key traits. The video uses some very clever examples to get at the core of what zero-cost abstraction means.
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.
A brief introduction to Rust Very code-centric survey of Rust with a few diagrams. This is where you start to get serious about code. Combining this tutorial with the Rust Playground (see above) will give you a big boost.
A half-hour to learn Rust An unusual tour of Rust using small code fragments and quirky commentary.
RustTourAbbrev.pdf
RustTour.pdf
Rust Videos
A presentation summarizing essential features of Rust. Several videos cover specific parts of the tour.
RustErrorHandling.pdf A presentation covering interesting mechanisms that Rust provides for handling program run-time errors.
In the beginning, expect to spend a lot of time getting your code to compile. After a few hours and some reading of introductory material, that will improve. The good news is that compiler error messages are almost always quite helpful. As you get more experienced - in a week or two - you will still spend more time getting things to compile than you would with C++, but will spend much less time debugging. That's because you won't be chasing memory errors and data races. It is a very good idea to create code a few lines at a time, then build and run, repeating until done. That makes it much easier to locate problems and minimize compilation issues.
Cut Here: References below this line can wait for a week or two or three.
Table 2. 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. 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 4. - Deeper into Rust

Topic Description link
Survey and Reference 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. Highly recommended once you are getting up to speed (maybe even before then). easy rust
Code Examples Large collection of examples across the Rust language by the Rust team. Rust by Example
e-Books An e-Book surveying the best e-Books for Rust. Lots here I didn't know about until I saw this. Little Book of Rust Books
Rust Cheat Sheet Very comprehensive well-organized collection of code snippets with comments. Cheats.rs
Rust Bite by Byte Sequence of pages on this site, each of which focuses on a single Rust topic RustBites
Mother of RustBites Narrative discussion of Rust in six chapters Rust Story
std Rust Libs Excellent documentation for the Rust std libraries. Very readable with access to source code. std lib
More references The place to look for specialized topics and blogs Rust Story References
Table 5 collects resources from this site intended to become comprehensive references for Rust. Many of the examples are a lot more than simple demos, and exercises are provided in Rust Bites and Rust Story.
Table 5. - Other Intermediate References

Table 5. - Getting up to Speed

Topics Descriptions
Idioms and Patterns A sequence of pages, each of which compares the way C++, Rust, and C# implement a simple idiom or pattern. Building these pages has just started, so there are only a few. As time permits there should be a lot more pages of examples.
BuildOn BuildOn supports learning Rust by specifying Rust programs for you to complete that require several packages and use much of the Rust technology, but are otherwise relatively simple. BuildOn pages provide specification for each package and additional notes and references.
When starting Rust, reading through the Rust Story on a background thread while you work on BuildOn using these Rust Bites seems like a good strategy to me. BuildOn orders the implementation of the project's packages to start with a small knowledge base for Rust. Each succeeding package will require more understanding of Rust, and has references to help you get that. By the time you're done with the project you will have a base to keep learning on your own, as you have to do when learning any language. As always, Dr. Google and StackOverflow are your friends.
  Next Prev Pages Sections About Keys