Data types and Variables
Data types
The primitive (i.e. built in) data types in Rust can be sub divided into two namely:
- Scalar types
- Compound types
Scalar types
Scalar types are types that can hold a single value.
- Numeric types: numeric types have a signed and an unsigned version. The numeric types in Rust are:
Type | Description |
---|---|
u8 | Unsigned 8 bit integer |
i8 | Signed 8 bit integer |
u16 | Unsigned 16 bit integer |
i16 | Signed 16 bit integer |
u32 | Unsigned 32 bit integer |
i32 | Signed 32 bit integer |
u64 | Unsigned 64 bit integer |
i64 | Signed 64 bit integer |
f32 | 32 bit floating point |
f64 | 64 bit floating point |
The non signed versions of the numeric types listed above can hold a value between -2 ^ N to (2 ^ N) - 1 while the unsigned versions can hold a value between 0 and (2 ^ N) - 1 where N is the number of bits. E.g. a i8 can hold value from -128 to 127 while a u8 can hold a value from 0 to 255.
- Non numeric types
- bool: the boolean type i.e. true or false
- char: can hold a single character e.g. 'A', 'B', 'C'
Compound types
Compound types are types that can hold two or more values. Rust has only two primitive compound types namely:
- Array
- Tuple See Arrays and tuples for more information
Variable declaration
You declare a variable in Rust using the let
keyword. For example:
#![allow(unused)] fn main() { let num1 = 100; let letter = 'A'; let amount = 7.5; }
Rust infers the type of the variable based on the value you assign to it at declaration. In the above, variables num1, letter and amount are inferred to be i32, char and f64 respectively. If you like you can explicitly specify a type using a suffix as shown below in the case of numeric types:
#![allow(unused)] fn main() { let num1 = 100u8; }
or by specifiying the variable type explicitly:
#![allow(unused)] fn main() { let num2:u8 = 100; let amount:f32 = 7.5; }
Variable shadowing
In Rust, you can re-use a variable name in the same scope. When you do this, the old variable goes out of scope i.e. you can not use it anymore from that point. You're essentially declaring a new variable and it's like the old variable never existed.
#![allow(unused)] fn main() { let name = "Jack"; println!("The name is {}", name); //shadow's previous variable let name = "John"; println!("The name is {}", name); }
You can declare the variable as a completely different type:
#![allow(unused)] fn main() { let number = "three"; println!("The number is {}", number); //shadow's previous variable let number = 3; println!("The number is {}", number); }
Variables and immutability
Variables are immutable by default in Rust. If you try to asign a new value to the above variable after the declaration you'll get a compiler error. That is,
#![allow(unused)] fn main() { let num1 = 100; num1 = 5; //compiler error }
To make a variable mutable, you must use the mut
keyword:
#![allow(unused)] fn main() { let mut num1 = 100; println!("The number is {}", num1); num1 = 5; println!("The number is {}", num1); }
Note: that making a variable mutable is not the same as shadowing. When you declare a variable as mutable, you're saying the value of that variable can be changed to another value which must be of the same data type as the initial value. In addition, the variable doesn't go out of scope and you can continue to use the variable until the end of the current scope e.g. method, code block etc.
fn main() { let mut name = "foo"; println!("The name is {}", name); name = "bar"; //must be same data type println!("The name is {}", name); //name = 3; // compiler error }
On the other hand, with shadowing, you're declaring a completely new variable - it just so happens that you're using the same name as the old variable. In this case, the old variable immediately goes out of scope and can no longer be used. From then on, that name is associated with the new variable. This is why you can change the data type in the case of variable shadowing.
#![allow(unused)] fn main() { let number = "hundred"; println!("The number is {}", number); //shadow's previous variable let number = 100; println!("The number is {}", number); }