3.1 Variables and Mutability
By default, variables are immutable.
Once the variable is set to immutable, you cannot change the value. Rust automatically finds the error and tries to fix it before compiling it.
fn main() {
let x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
}
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
3 | println!("The value of x is: {x}");
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` due to previous error
Rust compiler exactly pinpoints where and what the error is. This ensures bugless code.
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
}
adding mut
in front of the variable’s name makes it so that the value of the variable can be changed.
Constants
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
constants: values that are bound to a name and not allowed to change. It is stated by const
statement.
mut
is not able to be used withconsts
- convention of the naming is to use uppercase and underscore between words.
- valid for the entire time a program runs, within the scope in which they were declared.
Shadowing
Shadowing: declaring a new variable with the same name as a previous variable
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");
}
When applying shadowing in the inner scope, the variable only temporarily shadows the variable of the outer scope.
It is different with mut
because shadowing is creating a new variable by using the let
keyword.
3.2 Data Types
Rust is a statically typed language, which means that it must know the types of all variables at compile time.
- the compiler usually can infer the data type, but for some cases like
parse
, the type of the variable must be explicitly stated with:
. let guess: u32 = "42".parse().expect("Not a number!");
Data type subsets in Rust:
- Scalar
- Integers
- Floating-point numbers
- Booleans
- Characters
- Compound
- Tuples
- Arrays
Scalar Types - Integers
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
architecture | isize | usize |
The size of usize
and isize
is dependent on whether the computer’s architecture is 32-bit or 64-bit.
Integer literal can be written in many forms. (Refer to table below)
Number literals | Example |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte (u8 only) | b'A' |
To avoid overflow and underflow, the following methods can be used.
- Wrap in all modes with the
wrapping_*
methods, such aswrapping_add
. - Return the
None
value if there is an 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.
Scalar Types - Floating-Point Types
Rust’s floating-point types are f32
and f64
, which are 32 bits and 64 bits in size.
- IEEE-754 standard
Scalar Types - The Boolean Type
Specified by bool
, the possible values are true
and false
.
Scalar Types - The Character Type
char
type is the alphabetic type of Rust. Char literal can be specified by using a single quote.
Compound Types - The Tuple Type
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
A tuple is a general way of grouping together a number of values with a variety of types into one compound type
- fixed in size
- wrapped in brackets and separated by comma
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {y}");
}
Pattern matching can be used to destructure a tuple.
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
A tuple element directly by using a period (.
) followed by an index
unit: tuple with empty value
- returned by expression if it does not return any other value
Compound Types - The Array Type
Array: collection of values with the same type.
- used inside a comma-separated square bracket
- have fixed length
let a: [i32; 5] = [1, 2, 3, 4, 5];
An array’s type is written using square brackets with the type of each element, a semicolon, and then the number of elements in the array
let a = [3; 5]; //let a = [3, 3, 3, 3, 3];
Initializing with the same value for all values in an array.
// accessing element with index
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
Trying to access an index that is not within the length of the array will result in a **runtime error**.
3.3 Functions
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
fn
keyword is used to declare a function.
- snake case is the normal convention for naming a function
- a function with the name
main
is an entry point for most programs.
Parameters
fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {value}{unit_label}");
}
Parameters are separated by comma.
Statements and Expressions
Statements are instructions that perform some action and do not return a value.
Expressions evaluate to a resultant value.
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {y}");
}
curly bracket is an expression, and it can be assigned to another value.
Functions with Return Values
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {x}");
}
return
keyword can be used to return a value early
the returned value must not end with a semicolon
3.4 Comments
// hello, world
two slashes are used to represent comments
3.5 Control Flow
if Expressions
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
operations under if
is performed when the condition returns true
if else
keyword is used, it is executed when condition in if
returns false
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
multiple conditions can be used with else if
Using if in a let Statement
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {number}");
}
to use the style above, the return type must be matched.
Repetition with Loops
fn main() {
loop {
println!("again!");
}
}
loop
keyword executes a block of code over and over.
continue
keyword is used to skip to the next iteration without executing the rest of the code
Returning Values from Loops
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}
Put the value to return after the break statement to return a value.
Loop Labels to Disambiguate Between Multiple Loops
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}
loop labels can be used to break specific loops
Conditional Loops with while
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
Looping Through a Collection with for
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}
similar to for-each
statement in Python.
'Rust > The Rust Programming Language' 카테고리의 다른 글
[The Rust Programming Language] Ch. 2: Programming a Guessing Game (0) | 2023.11.05 |
---|