Home

Rust Lang Book

Resources for this can be found from here.

Hello World

// main.rs fn main() { println!("Hello, world!"); }

Note: println! calls a Rust macro. If it called a function instead, it would be entered as println (without the !). For now, you just need to know that using a ! means that you’re calling a macro instead of a normal function.

Hello Cargo

Cargo is Rust’s build system and package manager. Most Rustaceans use this tool to manage their Rust projects because Cargo handles a lot of tasks for you, such as building your code, downloading the libraries your code depends on, and building those libraries. (We call libraries your code needs dependencies.)

$ cargo new hello_cargo $ cd hello_cargo

The first command creates a new directory called hello_cargo. We’ve named our project hello_cargo, and Cargo creates its files in a directory of the same name.

Go into the hello_cargo directory and list the files. You’ll see that Cargo has generated two files and one directory for us: a Cargo.toml file and a src directory with a main.rs file inside. It has also initialized a new Git repository along with a .gitignore file.

This will be the cargo.toml file.

[package] name = "hello_cargo" version = "0.1.0" authors = ["Your Name <you@example.com>"] edition = "2018" [dependencies]

Building and running from a cargo project

$ cargo build # This command creates an executable file in target/debug/hello_cargo (or target\debug\hello_cargo.exe on Windows) rather than in your current directory. You can run the executable with this command: $ ./target/debug/hello_cargo > Hello, world! # We can also run with cargo run $ cargo run # Cargo also provides a command called cargo check. This command quickly checks your code to make sure it compiles but doesn’t produce an executable $ cargo check

Building for release

$ cargo build --release

This command will create an executable in target/release instead of target/debug. The optimizations make your Rust code run faster, but turning them on lengthens the time it takes for your program to compile. This is why there are two different profiles: one for development, when you want to rebuild quickly and often, and another for building the final program you’ll give to a user that won’t be rebuilt repeatedly and that will run as fast as possible. If you’re benchmarking your code’s running time, be sure to run cargo build --release and benchmark with the executable in target/release.

File layout

cargo new guessing_game

// guessing_game/src/main.rs use std::io; fn main() { println!("Guess the number!"); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); }

This code contains a lot of information, so let’s go over it line by line. To obtain user input and then print the result as output, we need to bring the io (input/output) library into scope. The io library comes from the standard library (which is known as std):

use std::io;

By default, Rust brings only a few types into the scope of every program in the prelude. If a type you want to use isn’t in the prelude, you have to bring that type into scope explicitly with a use statement. Using the std::io library provides you with a number of useful features, including the ability to accept user input.

The following example shows how to use mut before the variable name to make a variable mutable:

let foo = 5; // immutable let mut bar = 5; // mutable

The :: syntax in the ::new line indicates that new is an associated function of the String type. An associated function is implemented on a type, in this case String, rather than on a particular instance of a String. Some languages call this a static method.

This new function creates a new, empty string. You’ll find a new function on many types, because it’s a common name for a function that makes a new value of some kind.

To summarize, the let mut guess = String::new(); line has created a mutable variable that is currently bound to a new, empty instance of a String.

Recall that we included the input/output functionality from the standard library with use std::io; on the first line of the program. Now we’ll call an associated function, stdin, on io:

io::stdin().read_line(&mut guess) .expect("Failed to read line");

If we hadn’t listed the use std::io line at the beginning of the program, we could have written this function call as std::io::stdin. The stdin function returns an instance of std::io::Stdin, which is a type that represents a handle to the standard input for your terminal.

The & indicates that this argument is a reference, which gives you a way to let multiple parts of your code access one piece of data without needing to copy that data into memory multiple times. References are a complex feature, and one of Rust’s major advantages is how safe and easy it is to use references.

As mentioned earlier, read_line puts what the user types into the string we’re passing it, but it also returns a value—in this case, an io::Result. Rust has a number of types named Result in its standard library: a generic Result as well as specific versions for submodules, such as io::Result.

The Result types are enumerations, often referred to as enums. An enumeration is a type that can have a fixed set of values, and those values are called the enum’s variants.

For Result, the variants are Ok or Err. The Ok variant indicates the operation was successful, and inside Ok is the successfully generated value. The Err variant means the operation failed, and Err contains information about how or why the operation failed.

Printing values with the println placeholders

The set of curly brackets, {}, is a placeholder: think of {} as little crab pincers that hold a value in place. You can print more than one value using curly brackets: the first set of curly brackets holds the first value listed after the format string, the second set holds the second value, and so on. Printing multiple values in one call to println! would look like this:

fn main() { let x = 5; let y = 10; println!("x = {} and y = {}", x, y); }

This code would print x = 5 and y = 10.

Generating a random number

In this case, we would have to update our Cargo.toml file to include the rand crate

[dependencies] rand = "0.4.0"

cargo build to install the dependency, but running cargo update would also update the version for the latest minor version.

In use, we can now random generate a number:

use std::io; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("The secret number is: {}", secret_number); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); }

Comparing a guess to the secret number

We can now use another standard library to help give more information on the accuracy of the guess:

use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { // --snip-- let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = guess.trim().parse() .expect("Please type a number!"); println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } }

Note here that we are using some standard string methods to trim, parse and expect a number type for the guess variable.

The parse method on strings parses a string into some kind of number. Because this method can parse a variety of number types, we need to tell Rust the exact number type we want by using let guess: u32. The colon (:) after guess tells Rust we’ll annotate the variable’s type. Rust has a few built-in number types; the u32 seen here is an unsigned, 32-bit integer. It’s a good default choice for a small positive number. You’ll learn about other number types in Chapter 3. Additionally, the u32 annotation in this example program and the comparison with secret_number means that Rust will infer that secret_number should be a u32 as well. So now the comparison will be between two values of the same type!

The call to parse could easily cause an error. If, for example, the string contained A👍%, there would be no way to convert that to a number. Because it might fail, the parse method returns a Result type, much as the read_line method does (discussed earlier in "Handling Potential Failure with the Result Type"). We’ll treat this Result the same way by using the expect method again. If parse returns an Err Result variant because it couldn’t create a number from the string, the expect call will crash the game and print the message we give it. If parse can successfully convert the string to a number, it will return the Ok variant of Result, and expect will return the number that we want from the Ok value.

Allowing Multiple Guesses with Looping

// --snip-- println!("The secret number is: {}", secret_number); loop { println!("Please input your guess."); // --snip-- match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } } }

This is suboptimal as the only way to exit at the moment is to pass a string that cannot be passed to cause an error.

What we can do instead is update this is break the loop at particular parts:

// --snip-- match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } } } }

We could also now update the parse method to handle and continue on errors now that we are looping:

// --snip-- io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; println!("You guessed: {}", guess); // --snip--

The final code

use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); loop { println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } } } }

Repository

https://github.com/okeeffed/developer-notes-nextjs/content/rust/rust-lang-org-book

Sections


Related