Rust Primitive Types

Rust offers a lot of the regular cast of types, many with some interesting twists.

The character type is called char, but unlike the common 1-byte ASCII characters in C, Rust characters are 4-byte Unicode. We can define character variables enclosing character values in single quotes:

Numeric types are named by bit size and signedness, with signed integer types beginning with “i” and unsigned types beginning with “u”, while floating-point types are always signed:

Rust also offers the types isize and usize, the bit-capacity of which is platform-dependent, but their “range is sufficient to express the size of any collection”, quoth the Rust documentation. [I presume that we’ll find out later what sorts of collections this refers to; I imagine that one of these types may be the type of the size of arrays or lists or some-such, though I’m not sure why we would need two different types, much less a signed version.]

Arrays are collections of values of the same type:

The type of the array consists of two components, the type of the elements within the array (char in the above example), and the size of the array (5 in the example).

If we want to have a collection of a values of a mixture of types, Rust provides tuples:

Here the type of the tuple indicates the type of each value in the tuple, in order. As usual in Rust, we could let the compiler infer the types if we don’t need, say, some particular size of number.

More: read the Primitives Types chapter in The Rust Programming Language.

Rust Functions

Rust program execution begins in the traditional main function, defined like so:

Apparently the default behavior for main is to accept no parameters and return no values. [I presume there is some means of accepting command-line arguments that we will learn about later.] Interesting that the Rust designers selected the term fn for functions; the Arc dialect of Lisp uses fn as well, aiming for the right balance of terseness and legibility.

Even if main does not accept any parameters or return any values, Rust functions in general certainly can. And unlike with regular variable declarations, the types of function parameters and return values must be explicitly stated:

A Rust function returns the final expression in the function. Note that in the function above, the expression x + 42 is just that: an expression. As in C, concluding an expression with a semicolon turns it into a statement, but in the context of a function return value, we just want the plain expression. We could explicitly write an actual return statement:

and while this might make us long-time C programmers feel happy, the Rust designers deem it “poor style” when a final expression return value would work instead. (Using a return statement may be appropriate in other cases, if you want to return out of a function before reaching the end of the function.)

Rust offers function pointers in a way that looks much cleaner than in C. We can declare a variable to be of a function type with a particular function signature:

This declares f to be of type, function that accepts a signed 32-bit integer and returns a 32-bit integer. This also appears to be an exception to the rule that variables must be initialized, as the above line of code works as-is. But we can initialize it with a value of a function, assuming we have defined a function that matches that type:

We could also let the Rust compiler just infer the type of the function when declaring the function pointer:

Using function pointers in C isn’t that hard once you get used to it, but Rust’s syntax is a lot easier to look at!

More: read the Functions chapter in The Rust Programming Language.

Rust Variable Bindings

Rust variable bindings are straightforward, but a bit different than in some other popular languages. An initial declaration / binding begins with the let keyword, and can assume an appropriate type based on the initial value:

This sets up the variable x as a signed 32-bit integer of type i32. If you need to specify a particular type, that can optionally follow the variable name

This gets us an unsigned 8-bit integer variable.

By default, Rust variables are immutable. This might seem like a frustrating choice at first, but the logic behind it includes the notion that it would be easier for the compiler to prevent you from altering a value you did not intend to change if you had to be explicit about wanting a value mutable in the first place. [Sounds good for safety-rich programming domains such as avionics!]

To purposefully make a variable mutable, include the mut modifier:

Another fine design choice for safety-conscious programmers is that Rust requires variables to have initial values. Granted, compilers for some other popular languages issue warnings when variables are left uninitialized, but deeming an uninitialized variable to be a compilation error should strongly encourage more intentional variable declaration behavior.

Rust variable bindings are block-scoped, such that

will not work, because y goes out of scope at the end of the block it is enclosed within.

Rust variable bindings also support shadowing, which is useful to introduce a new variable within a block with the same name as one outside the block, but which can seem a bit confusing when used to effectively sneak around the immutability rules:

What we have here are actually two variables named x. The first one is not destroyed or overwritten by the second one, but due to scoping and shadowing rules, the first one is now no longer accessible.

More: read the Variable Bindings chapter in The Rust Programming Language.