$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh [...] 1) Proceed with installation (default) 2) Customize installation 3) Cancel installation >1 [...] To configure your current shell, run: source $HOME/.cargo/env $ $ source $HOME/.cargo/env $ rustc --version rustc 1.53.0 (53cb7b09b 2021-06-17) $
$HOME/.rustup
$HOME/.cargo
$HOME/.profile
and $HOME/.bashrc
rustc --version
to verify the installation$ cp .profile .profile.orig $ cp .bashrc .bashrc.orig $ $ # install rust $ $ diff .profile.orig .profile 27a28 > . "$HOME/.cargo/env" $ diff .bashrc.orig .bashrc 128a129 > . "$HOME/.cargo/env" $The changes to
.profile
and .bashrc
are limited to sourcing the environment file.
Rust can be updated by rustup update
and uninstalled by rustup self uninstall
. rustup doc
will open the documentation in a web browser.
*.rs
hello_world.rs
)fn main() { println!("Hello, world!"); }
fn main
is the entrance point - like in C/C++rustfmt
for automatic formatting.println!(...)
calls a Rust macro - macros are handled later. println(...)
would be a function call.$ mkdir Projects/playground/hello_rust $ cd Projects/playground/hello_rust $ vi main.rs $ rustc main.rs $ ls main main.rs $ ./main Hello, world! $ file main main: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=fcf290d46f244d46f80e05c5e5fa97eff197144b, with debug_info, not stripped $
*.rs
file - in this example main.rs
rustc
main
$ cat main.rs fn main() { println!( "Hello, world!" ); } $ rustc main.rs # compiles ! $ rustfmt main.rs # format with standard style $ cat main.rs fn main() { println!("Hello, world!"); } $
rustfmt
to format your code to the official style guidecargo
like this:
$ cargo new hello_cargo Created binary (application) `hello_cargo` package $ cd hello_cargo/ $ tree -a . ├── Cargo.toml ├── .git │ [...] ├── .gitignore └── src └── main.rs 10 directories, 18 files $
.gitignore
Cargo.toml
in TOML format (Tom’s Obvious, Minimal Language) - the Cargo configurationsrc/main.rs
template for source code (hello world)$ cat Cargo.toml [package] name = "hello_cargo" version = "0.1.0" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] $
package
section for configuring the package, the dependencies
section for the dependencies.authors = ["Your Name <you@example.com>"]
to the package section.$ cargo build Compiling hello_cargo v0.1.0 (/home/krach/Projects/playground/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.35s $ $ ./target/debug/hello_cargo Hello, world! $ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running `target/debug/hello_cargo` Hello, world! $ $ touch src/main.rs $ cargo run Compiling hello_cargo v0.1.0 (/home/krach/Projects/playground/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.14s Running `target/debug/hello_cargo` Hello, world! $
cargo build
builds the projectcargo build --release
builds the project in release mode - which is faster but takes longer to compilecargo run
runs the executable (and also builds it if needed)Cargo.lock
keeps track of dependencies (cargo internal)cargo check
checks that the code would compile - is much faster as cargo build
- e.g. for IDE21:49:26[rust]$ rustc --version rustc 1.51.0 21:49:34[rust]$ cargo new guessing_game Created binary (application) `guessing_game` package 21:49:55[rust]$And compiling and running the code...
21:49:55[rust]$ cd guessing_game/ 21:51:02[guessing_game]$ cargo run Compiling guessing_game v0.1.0 (/home/charly/tmp/rust/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 0.70s Running `target/debug/guessing_game` Hello, world! 21:51:18[guessing_game]$In the next steps we expend the code:
using
we can bring a library into scope. Without the using std::io
term we have to write std::io::stdin()
instead of io::stdin()
. There is a default set of type in scope - called the prelude.let foo = 5;
creates a constant (immutable) whilelet mut bar = 42;
creates a variable (mutable).let mut guess = String::new();
creates a variable guess
with a "growable, UTF-8 encoded bit of text".::new
means calling a associated function of the String type - in c++ a static function. The new
function creates a empty string and is available on many types.&
- mutable variables with &mut
. So calling the method fn read_line(buf: &mut String)
looks like that: read_line(&mut guess)
io::Result
is an enumeration which can hold one value of a fix set of values (in our case Ok
and Err
), called enum's variants.Results
are tuples which hold a (return) value and an error code - e.g. in this case String
and io::Error
. This allows in the OK-case to work with the return value - and in error case to work with the error code.io::Result
has an expect
method which acts like an assert in case of an error. In case of no error, it simply returns the value.Result
but not handling the error code - e.g. with expect()
.println!
you can use {}
as placeholder for a value, e.g. println!("x = {} and y = {}", x, y);
String
to i32
.0.8.3
means that all crates greater equal 0.8.3
up to (exclusive) 0.9.0
may be used.Cargo.lock
file specifies which concrete version is used - in this case 0.8.4
. It is added to the versioning system. Explicit updates are done with cargo update
.10:48:03[guessing_game]$ cat Cargo.lock | grep package | wc -l 1 10:48:15[guessing_game]$ ls -l target/debug/guessing_game -rwxrwxr-x 2 charly charly 10794144 Aug 28 22:01 target/debug/guessing_game 10:48:19[guessing_game]$ gvim Cargo.toml 10:48:25[guessing_game]$ git diff --unified=0 Cargo.toml | tail -n +5 @@ -9,0 +10 @@ edition = "2018" +rand = "0.8.3" 10:48:45[guessing_game]$ cargo build Updating crates.io index Finished dev [unoptimized + debuginfo] target(s) in 0.83s 10:48:53[guessing_game]$ ls -l target/debug/guessing_game -rwxrwxr-x 2 charly charly 10794152 Aug 29 10:39 target/debug/guessing_game 10:48:55[guessing_game]$ 10:48:58[guessing_game]$ cat Cargo.lock | grep package | wc -l 10 10:49:00[guessing_game]$
Result
the compiler generates a warning when the error-code is not checked. This can be done in two ways. When an error is not expected, the expect()
method can be used - it works like an assert and crashes the program when an error appears:
let guess: u32 = guess.trim().parse().expect("Please type a number!");
When it is likely that the method returns an error - e.g. in user-interaction - the error shall be handled:
let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(e) => { println!("Got invalid input: {}", e); continue }, };
let x = 7;
let mut y = 8;
const Z: i32 = 9;
(snake_case)error[E0282]: type annotations needed
says that Rust cannot figure out the correct type, e.g. in let guess: i32 = "42".parse().expect("Not a number!");
i8
, u8
, i16
, u16
, i32
, u32
, i64
, u64
, i128
, u128
, isize
, usize
(where the last two are architecture dependent, e.g. 64 bit on a 64-bit machine)12_000
for 12000
(like in C++14 as 12'000
)98_222
), hex (0x1_7F_AE
), octal (0o27_76_56
) and binary (0b0001_0111_1111_1010_1110). Bytes (u8
) can be written like this: b'A'
wrapping_
(for explicitly wrapping around), checked_
(returns None
on overflow), overflowing_
(provides additional bool
) and saturating_
(staying at max/min).f32
and f64
. f64
is the default because on modern machines they have roughly the same speed as f32
.bool
with true
and false
as values.char
: let heart_eyed_cat = '😻';
It's Unicode and with a size of 4 bytes.let first_tup = (500, 6.4, 1); let second_tup: (i32, f64, u8) = (500, 6.4, 1); // same as first_tup let (x, y, z) = first_tup; // called destructing let five_hundred = second_tup.0; let six_point_four = second_tup.1;
let a = [1, 2, 3, 4, 5]; let b: [i32; 5] = [1, 2, 3, 4, 5]; // same as array a let c = [1; 5]; // creates [1, 1, 1, 1, 1] (value is 1, size is 5) let first = a[0]; let second = a[1];
fn another_function(x: i32) {
let y = 6;
fn main() { let y = 6; }
5 + 6
{ let x = 3; x + 1 }
fn plus_one(x: i32) -> i32 { x + 1 }
return
keywordif
, else
, else if
, loop
, while
and for
if number < 5 { println!("small number"); } else if number > 100 { println!("big number"); } else { println!("regular number"); }
bool
.else if
you might refactor your code with the match
statementlet number = if value != 7 { 5 } else { 6 };
(both arms must have the same type, e.g. i32
)loop
:
let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } };
while
loop:
let a = [10, 20, 30, 40, 50]; let mut index = 0; while index < 5 { println!("the value is: {}", a[index]); index += 1; }
for
loop and iterator:
let a = [10, 20, 30, 40, 50]; for element in a.iter() { println!("the value is: {}", element); }
for
loop:
for number in (1..4).rev() { println!("{}!", number); }