Scalar
Integer
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
isize and usize types depend on the architecture of the computer your program is running on, which is denoted in the table as “arch”: 64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit architecture. |
Number literals can be written as:
Number literals | Example |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte (u8 only) | b'A' |
What happens when integer overflows, in debug mode it panics, in release mode Rust will perform two’s complement wrapping - the value will “wrap around” from maximum to the minimum value, e.g. for u8
the value 256 becomes 0, 257 - 1, and so on.
To explicitly handle the possibility of overflow, you can use these families of methods provided by the standard library for primitive numeric types:
- Wrap in all modes with the
wrapping_*
methods, such aswrapping_add
. - Return the
None
value if there is overflow with thechecked_*
methods. - Return the value and a boolean indicating whether there was overflow with the
overflowing_*
methods. - Saturate at the value’s minimum or maximum values with the
saturating_*
methods.
Float
Rust has f32
and f64
types. The default is f64
(on modern CPU the speed is roughly the same as f32
but it’s capable of more precision).
All are signed types.
Boolean
Has true
and false
.
One byte in size.
Character char
4 bytes in size.
Represents a Unicode Scalar Value, which means it’s not limited to ASCII.
Compound
Tuple
Have fixed length: once declared cannot grow/shrink in size.
The tuple without any values has a special name, unit. This value and its corresponding type are both written () and represent an empty value or an empty return type. Expressions implicitly return the unit value if they don’t return any other value.
Array
All elements must have the same type.
Has fixed length. Can’t grow in size.
Can be initialized with the same initial value like this:
Slice
Struct
Not sure
Collections
Unlike the built-in array and tuple types, the data these collections point to is stored on the heap, which means the amount of data does not need to be known at compile time and can grow or shrink as the program runs.
Vector
https://doc.rust-lang.org/stable/std/vec/struct.Vec.html Vectors allow you to store more than one value in a single data structure that puts all the values next to each other in memory. Vectors can only store values of the same type.
Rust provides the vec!
macro for convenience, which will create a new vector:
To add new elements to the vector, use push()
method.
As with any variable, if we want to be able to change its value, we need to make vector mutable using the mut
keyword.
To read elements of vector we either can use indexing syntax of get()
method:
Indexing syntax will panic if index outside of vector range.
You can’t have immutable and mutable references to a vector at the same time, including references to its elements, for example this code will not compile:
This error is due to the way vectors work: because vectors put the values next to each other in memory, adding a new element onto the end of the vector might require allocating new memory and copying the old elements to the new space, if there isn’t enough room to put all the elements next to each other where the vector is currently stored. In that case, the reference to the first element would be pointing to deallocated memory. The borrowing rules prevent programs from ending up in that situation.
Iteration:
To change the value that the mutable reference refers to, we have to use the *
dereference operator to get to the value in i
before we can use the +=
operator.
To store values of different types we could use enum variants:
If you don’t know the exhaustive set of types a program will get at runtime to store in a vector, the enum technique won’t work. Instead, you can use a trait object.
String
Strings are complicated. Rust has chosen to make the correct handling of
String
data the default behavior for all Rust programs, which means programmers have to put more thought into handling UTF-8 data upfront. This trade-off exposes more of the complexity of strings than is apparent in other programming languages, but it prevents you from having to handle errors involving non-ASCII characters later in your development life cycle.
Implemented as a collection of bytes.
Rust has only one string type in the core language, which is the string slice str
that is usually seen in its borrowed form &str
.
The String
type, which is provided by Rust’s standard library rather than coded into the core language, is a growable, mutable, owned, UTF-8 encoded string type.
All types that implement Display
trait have .to_string()
method.
Strings are UTF-8 encoded, so we can include any properly encoded data in them
We can grow a String
by using the push_str
method to append a string slice:
The push
method takes a single character as a parameter and adds it to the String
:
Combine strings:
Looks not that useful to me because the first variable is moved. Better way is to use format!
macro:
Compiler can coerce the &String
argument into a &str
automatically.
Rust strings don’t support indexing. This is because how Rust stores strings in memory.
A String
is a wrapper over a Vec<u8>
. Unicode scalar value can take more than 1 byte, therefore an index into the string’s bytes will not always correlate to a valid Unicode scalar value.
There are three relevant ways to look at strings from Rust’s perspective: as bytes, scalar values, and grapheme clusters (the closest thing to what we would call letters).
For example:
“नमस्ते” in vector’s u8
values would look like this:
In Unicode scalar values (Rust’s char
):
In letters:
You should use ranges to create string slices with caution, because doing so can crash your program.
If we were to try to slice only part of a character’s bytes with something like &hello[0..1]
, Rust would panic at runtime in the same way as if an invalid index were accessed in a vector:
So, how to iterate over strings: the best way to operate on pieces of strings is to be explicit about whether you want characters or bytes.
But be sure to remember that valid Unicode scalar values may be made up of more than 1 byte.
Getting grapheme clusters from strings as with the Devanagari script is complex, so this functionality is not provided by the standard library.
Hash map
The type HashMap<K, V>
stores a mapping of keys of type K
to values of type V
using a hashing function, which determines how it places these keys and values into memory.
Note that we need to first use the HashMap from the collections portion of the standard library. Of our three common collections, this one is the least often used, so it’s not included in the features brought into scope automatically in the prelude. Hash maps also have less support from the standard library; there’s no built-in macro to construct them, for example.
Just like vectors, hash maps store their data on the heap.
For types that implement the Copy
trait, like i32
, the values are copied into the hash map. For owned values like String
, the values will be moved and the hash map will be the owner of those values
If we insert references to values into the hash map, the values won’t be moved into the hash map. The values that the references point to must be valid for at least as long as the hash map is valid.
Overwrite the value:
Add if key isn’t present:
Update the value based on the old value:
The
or_insert
method returns a mutable reference (&mut V
) to the value for the specified key. Here we store that mutable reference in thecount
variable, so in order to assign to that value, we must first dereferencecount
using the asterisk (*
). The mutable reference goes out of scope at the end of thefor
loop, so all of these changes are safe and allowed by the borrowing rules.
By default, HashMap
uses a hashing function called SipHash that can provide resistance to Denial of Service (DoS) attacks involving hash tables. You can switch to another function by specifying a different hasher. A hasher is a type that implements the BuildHasher
trait.