mirror of https://github.com/rust-lang/book
Changes shipped to nostarch 2016-08-19
This commit is contained in:
parent
6b022d6972
commit
7bfbc68a4f
|
@ -26,9 +26,7 @@ well as how they work behind the scenes.
|
|||
## Contributing to the book
|
||||
|
||||
This book is open source. If you find an error, please don’t hesitate to file an
|
||||
issue or send a pull request [on GitHub].
|
||||
|
||||
[on GitHub]: https://github.com/rust-lang/book
|
||||
issue or send a pull request on GitHub at *https://github.com/rust-lang/book*.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -61,22 +59,24 @@ Rust is installed now. Great!
|
|||
|
||||
### Installing on Windows
|
||||
|
||||
If you're on Windows, please download the appropriate [installer][install-page].
|
||||
If you're on Windows, please go to *https://rustup.rs/* and follow
|
||||
the instructions to download rustup-init.exe. Run that and follow the rest of
|
||||
the instructions.
|
||||
|
||||
[install-page]: https://www.rust-lang.org/install.html
|
||||
The rest of the Windows-specific commands in the book will assume that you are
|
||||
using `cmd` as your shell. If you use a different shell, you may be able to run
|
||||
the same commands that Linux and Mac users do. If neither work, consult the
|
||||
documentation for the shell you are using.
|
||||
|
||||
### Uninstalling
|
||||
|
||||
Uninstalling Rust is as easy as installing it. On Linux or Mac, just run
|
||||
Uninstalling Rust is as easy as installing it. From your shell, run
|
||||
the uninstall script:
|
||||
|
||||
```bash
|
||||
$ rustup self uninstall
|
||||
```
|
||||
|
||||
If you used the Windows installer, you can re-run the `.msi` and it will give
|
||||
you an uninstall option.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If you've got Rust installed, you can open up a shell, and type this:
|
||||
|
@ -96,26 +96,22 @@ If you see this, Rust has been installed successfully!
|
|||
Congrats!
|
||||
|
||||
If you don't and you're on Windows, check that Rust is in your `%PATH%` system
|
||||
variable. If it isn't, run the installer again, select "Change" on the "Change,
|
||||
repair, or remove installation" page and ensure "Add to PATH" is checked.
|
||||
variable.
|
||||
|
||||
If not, there are a number of places where you can get help. The easiest is
|
||||
[the #rust IRC channel on irc.mozilla.org][irc], which you can access through
|
||||
[Mibbit][mibbit]. Click that link, and you'll be chatting with other Rustaceans
|
||||
(a silly nickname we call ourselves) who can help you out. Other great resources
|
||||
include [the user’s forum][users] and [Stack Overflow][stackoverflow].
|
||||
|
||||
[irc]: irc://irc.mozilla.org/#rust
|
||||
[mibbit]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust
|
||||
[users]: https://users.rust-lang.org/
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/rust
|
||||
If it still isn't working, there are a number of places where you can get help.
|
||||
The easiest is the #rust IRC channel on irc.mozilla.org, which you can access
|
||||
through Mibbit at
|
||||
*http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust*. Go to that
|
||||
address, and you'll be chatting with other Rustaceans (a silly nickname we call
|
||||
ourselves) who can help you out. Other great resources include the user’s forum
|
||||
at *https://users.rust-lang.org/* and Stack Overflow at
|
||||
*http://stackoverflow.com/questions/tagged/rust*.
|
||||
|
||||
### Local documentation
|
||||
|
||||
The installer also includes a copy of the documentation locally, so you can
|
||||
read it offline. On Linux or Mac, run `rustup doc` to open the local
|
||||
documentation in your browser. On Windows, the documentation is in a
|
||||
`share/doc` directory inside the directory where Rust was installed.
|
||||
read it offline. Run `rustup doc` to open the local documentation in your
|
||||
browser.
|
||||
|
||||
## Hello, World!
|
||||
|
||||
|
@ -126,7 +122,8 @@ tradition.
|
|||
|
||||
> Note: This book assumes basic familiarity with the command line. Rust itself
|
||||
> makes no specific demands about your editing, tooling, or where your code
|
||||
> lives, so if you prefer an IDE to the command line, that's an option.
|
||||
> lives, so if you prefer an IDE to the command line, feel free to use your
|
||||
> favorite IDE.
|
||||
|
||||
### Creating a Project File
|
||||
|
||||
|
@ -135,6 +132,8 @@ lives, but for this book, we'd suggest making a *projects* directory in your
|
|||
home directory and keeping all your projects there. Open a terminal and enter
|
||||
the following commands to make a directory for this particular project:
|
||||
|
||||
Linux and Mac:
|
||||
|
||||
```bash
|
||||
$ mkdir ~/projects
|
||||
$ cd ~/projects
|
||||
|
@ -142,18 +141,25 @@ $ mkdir hello_world
|
|||
$ cd hello_world
|
||||
```
|
||||
|
||||
> Note: If you’re on Windows and not using PowerShell, the `~` that represents
|
||||
> your home directory may not work.
|
||||
> Consult the documentation for your shell for more details.
|
||||
Windows:
|
||||
|
||||
```bash
|
||||
$ mkdir %USERPROFILE%\projects
|
||||
$ cd %USERPROFILE%\projects
|
||||
$ mkdir hello_world
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
### Writing and Running a Rust Program
|
||||
|
||||
Next, make a new source file and call it *main.rs*. Rust files always end with
|
||||
the *.rs* extension. If you’re using more than one word in your filename, use
|
||||
an underscore to separate them. For example, you'd use *hello_world.rs* rather
|
||||
than *helloworld.rs*.
|
||||
Next, make a new source file and call it `main.rs`. Rust files always end with
|
||||
the `.rs` extension. If you’re using more than one word in your filename, use
|
||||
an underscore to separate them. For example, you'd use `hello_world.rs` rather
|
||||
than `helloworld.rs`.
|
||||
|
||||
Now open the *main.rs* file you just created, and type the following code:
|
||||
Now open the `main.rs` file you just created, and type the following code:
|
||||
|
||||
Filename: main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
@ -170,10 +176,10 @@ $ ./main
|
|||
Hello, world!
|
||||
```
|
||||
|
||||
On Windows, just replace `main` with `main.exe`. Regardless of your operating
|
||||
system, you should see the string `Hello, world!` print to the terminal. If you
|
||||
did, then congratulations! You've officially written a Rust program. That makes
|
||||
you a Rust programmer! Welcome.
|
||||
On Windows, just replace `./main` with `.\main.exe`. Regardless of your
|
||||
operating system, you should see the string `Hello, world!` print to the
|
||||
terminal. If you did, then congratulations! You've officially written a Rust
|
||||
program. That makes you a Rust programmer! Welcome.
|
||||
|
||||
### Anatomy of a Rust Program
|
||||
|
||||
|
@ -189,12 +195,10 @@ fn main() {
|
|||
These lines define a *function* in Rust. The `main` function is special: it's
|
||||
the first thing that is run for every executable Rust program. The first line
|
||||
says, “I’m declaring a function named `main` that takes no arguments and
|
||||
returns nothing.” If there were arguments, they would go inside the parentheses
|
||||
(`(` and `)`). We aren’t returning anything from this function, so we have
|
||||
omitted the return type entirely. If there was a return type, there would be a
|
||||
`->` and the return type after the parentheses.
|
||||
returns nothing.” If there were arguments, they would go inside the parentheses,
|
||||
`(` and `)`.
|
||||
|
||||
Also note that the function body is wrapped in curly braces (`{` and `}`). Rust
|
||||
Also note that the function body is wrapped in curly braces, `{` and `}`. Rust
|
||||
requires these around all function bodies. It's considered good style to put
|
||||
the opening curly brace on the same line as the function declaration, with one
|
||||
space in between.
|
||||
|
@ -207,7 +211,7 @@ Inside the `main()` function:
|
|||
|
||||
This line does all of the work in this little program: it prints text to the
|
||||
screen. There are a number of details that are important here. The first is
|
||||
that it’s indented with four spaces, not tabs.
|
||||
that it’s indented with four spaces, not a tab.
|
||||
|
||||
The second important part is `println!()`. This is calling a Rust *macro*,
|
||||
which is how metaprogramming is done in Rust. If it were calling a function
|
||||
|
@ -249,8 +253,9 @@ main main.rs
|
|||
On Windows, you'd enter:
|
||||
|
||||
```bash
|
||||
$ dir
|
||||
main.exe main.rs
|
||||
$ dir /B # the /B option says to only show the file names
|
||||
main.exe
|
||||
main.rs
|
||||
```
|
||||
|
||||
This shows we have two files: the source code, with the `.rs` extension, and the
|
||||
|
@ -258,10 +263,10 @@ executable (`main.exe` on Windows, `main` everywhere else). All that's left to
|
|||
do from here is run the `main` or `main.exe` file, like this:
|
||||
|
||||
```bash
|
||||
$ ./main # or main.exe on Windows
|
||||
$ ./main # or .\main.exe on Windows
|
||||
```
|
||||
|
||||
If *main.rs* were your "Hello, world!" program, this would print `Hello,
|
||||
If `main.rs` were your "Hello, world!" program, this would print `Hello,
|
||||
world!` to your terminal.
|
||||
|
||||
If you come from a dynamic language like Ruby, Python, or JavaScript, you may
|
||||
|
@ -285,7 +290,7 @@ Cargo is Rust’s build system and package manager, and Rustaceans use Cargo to
|
|||
manage their Rust projects because it makes a lot of tasks easier. For example,
|
||||
Cargo takes care of building your code, downloading the libraries your code
|
||||
depends on, and building those libraries. We call libraries your code needs
|
||||
‘dependencies’ since your code depends on them.
|
||||
*dependencies*.
|
||||
|
||||
The simplest Rust programs, like the one we've written so far, don’t have any
|
||||
dependencies, so right now, you'd only be using the part of Cargo that can take
|
||||
|
@ -304,8 +309,8 @@ $ cargo --version
|
|||
```
|
||||
|
||||
If you see a version number, great! If you see an error like `command not
|
||||
found`, then you should look at the documentation for the way you installed
|
||||
Rust to determine how to install Cargo separately.
|
||||
found`, then you should look at the documentation for your method of
|
||||
installation to determine how to install Cargo separately.
|
||||
|
||||
### Creating a Project with Cargo
|
||||
|
||||
|
@ -313,11 +318,19 @@ Let's create a new project using Cargo and look at how it differs from our
|
|||
project in `hello_world`. Go back to your projects directory (or wherever you
|
||||
decided to put your code):
|
||||
|
||||
Linux and Mac:
|
||||
|
||||
```bash
|
||||
$ cd ~/projects
|
||||
```
|
||||
|
||||
And then run:
|
||||
Windows:
|
||||
|
||||
```bash
|
||||
$ cd %USERPROFILE%\projects
|
||||
```
|
||||
|
||||
And then on any operating system run:
|
||||
|
||||
```bash
|
||||
$ cargo new hello_cargo --bin
|
||||
|
@ -326,20 +339,22 @@ $ cd hello_cargo
|
|||
|
||||
We passed the `--bin` argument to `cargo new` because our goal is to make an
|
||||
executable application, as opposed to a library. Executables are often called
|
||||
*binaries* (as in `/usr/bin`, if you’re on a Unix system). `hello_cargo` is the
|
||||
name we've chosen for our project, and Cargo creates its files in a directory
|
||||
of the same name that we can then go into.
|
||||
*binaries* (as in `/usr/bin`, if you’re on a Unix system). We've given
|
||||
`hello_cargo` as the name for our project, and Cargo creates its files in a
|
||||
directory of the same name that we can then go into.
|
||||
|
||||
If we list the files in the `hello_cargo` directory, we can see that Cargo has
|
||||
generated two files and one directory for us: a `Cargo.toml` and a *src*
|
||||
directory with a *main.rs* file inside. It has also initialized a new `git`
|
||||
repository in the `hello_cargo` directory for us; you can change this to use a
|
||||
different version control system, or no version control system, by using the
|
||||
`--vcs` flag.
|
||||
generated two files and one directory for us: a `Cargo.toml` and a `src`
|
||||
directory with a `main.rs` file inside. It has also initialized a new `git`
|
||||
repository in the `hello_cargo` directory for us, along with a `.gitignore`
|
||||
file; you can change this to use a different version control system, or no
|
||||
version control system, by using the `--vcs` flag.
|
||||
|
||||
Open up `Cargo.toml` in your text editor of choice. It should look something
|
||||
like this:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello_cargo"
|
||||
|
@ -349,12 +364,10 @@ authors = ["Your Name <you@example.com>"]
|
|||
[dependencies]
|
||||
```
|
||||
|
||||
This file is in the *[TOML]* (Tom's Obvious, Minimal Language) format. TOML is
|
||||
This file is in the *TOML* (Tom's Obvious, Minimal Language) format. TOML is
|
||||
similar to INI but has some extra goodies and is used as Cargo’s
|
||||
configuration format.
|
||||
|
||||
[TOML]: https://github.com/toml-lang/toml
|
||||
|
||||
The first line, `[package]`, is a section heading that indicates that the
|
||||
following statements are configuring a package. As we add more information to
|
||||
this file, we’ll add other sections.
|
||||
|
@ -366,12 +379,15 @@ from your environment. If it’s not correct, go ahead and fix that and save the
|
|||
file.
|
||||
|
||||
The last line, `[dependencies]`, is the start of a section for you to list any
|
||||
crates that your project will depend on so that Cargo knows to download and
|
||||
compile those too. We won't need any other crates for this project, but we will
|
||||
in the guessing game tutorial in the next chapter.
|
||||
*crates* (which is what we call packages of Rust code) that your project will
|
||||
depend on so that Cargo knows to download and compile those too. We won't need
|
||||
any other crates for this project, but we will in the guessing game tutorial in
|
||||
the next chapter.
|
||||
|
||||
Now let's look at `src/main.rs`:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
@ -385,7 +401,7 @@ and the project generated by Cargo that we've seen so far are:
|
|||
1. Our code goes in the `src` directory
|
||||
2. The top level contains a `Cargo.toml` configuration file
|
||||
|
||||
Cargo expects your source files to live inside the *src* directory so that the
|
||||
Cargo expects your source files to live inside the `src` directory so that the
|
||||
top-level project directory is just for READMEs, license information,
|
||||
configuration files, and anything else not related to your code. In this way,
|
||||
using Cargo helps you keep your projects nice and tidy. There's a place for
|
||||
|
@ -403,20 +419,23 @@ program through Cargo! To do so, enter the following commands:
|
|||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling hello_cargo v0.1.0 (file:///home/yourname/projects/hello_cargo)
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
```
|
||||
|
||||
This should have created an executable file in `target/debug/hello_cargo` (or `target/debug/hello_cargo.exe` on Windows), which you can run with this command:
|
||||
This should have created an executable file in `target/debug/hello_cargo` (or
|
||||
`target\debug\hello_cargo.exe` on Windows), which you can run with this command:
|
||||
|
||||
```bash
|
||||
$ ./target/debug/hello_cargo # or ./target/debug/hello_cargo.exe on Windows
|
||||
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Bam! If all goes well, `Hello, world!` should print to the terminal once more.
|
||||
|
||||
Running `cargo build` for the first time also causes Cargo to create a new file
|
||||
at the top level called *Cargo.lock*, which looks like this:
|
||||
at the top level called `Cargo.lock`, which looks like this:
|
||||
|
||||
Filename: Cargo.lock
|
||||
|
||||
```toml
|
||||
[root]
|
||||
|
@ -424,7 +443,7 @@ name = "hello_cargo"
|
|||
version = "0.1.0"
|
||||
```
|
||||
|
||||
Cargo uses the *Cargo.lock* file to keep track of dependencies in your
|
||||
Cargo uses the `Cargo.lock` file to keep track of dependencies in your
|
||||
application. This project doesn't have dependencies, so the file is a bit
|
||||
sparse. Realistically, you won't ever need to touch this file yourself; just
|
||||
let Cargo handle it.
|
||||
|
@ -439,22 +458,29 @@ $ cargo run
|
|||
Hello, world!
|
||||
```
|
||||
|
||||
Notice that this time, we didn't see the output that Cargo was compiling
|
||||
`hello_cargo`. Cargo figured out that the files haven’t changed, so it just ran
|
||||
the binary. If you had modified your source code, Cargo would have rebuilt the
|
||||
project before running it, and you would have seen something like this:
|
||||
Notice that this time, we didn't see the output telling us that Cargo was
|
||||
compiling `hello_cargo`. Cargo figured out that the files haven’t changed, so
|
||||
it just ran the binary. If you had modified your source code, Cargo would have
|
||||
rebuilt the project before running it, and you would have seen something like
|
||||
this:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling hello_cargo v0.1.0 (file:///home/yourname/projects/hello_cargo)
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Running `target/debug/hello_cargo`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
So a few more differences we've now seen:
|
||||
|
||||
3. Instead of using `rustc`, build a project using `cargo build` (or build and run it in one step with `cargo run`)
|
||||
4. Instead of the result of the build being put in the same directory as our code, Cargo will put it in the `target/debug` directory.
|
||||
3. Instead of using `rustc`, build a project using `cargo build` (or build and
|
||||
run it in one step with `cargo run`)
|
||||
4. Instead of the result of the build being put in the same directory as our
|
||||
code, Cargo will put it in the `target/debug` directory.
|
||||
|
||||
The other advantage of using Cargo is that the commands are the same no matter
|
||||
what operating system you're on, so at this point we will no longer be
|
||||
providing specific instructions for Linux and Mac versus Windows.
|
||||
|
||||
### Building for Release
|
||||
|
||||
|
@ -477,7 +503,7 @@ projects composed of multiple crates, it’s much easier to let Cargo coordinate
|
|||
the build. With Cargo, you can just run `cargo build`, and it should work the
|
||||
right way. Even though this project is simple, it now uses much of the real
|
||||
tooling you’ll use for the rest of your Rust career. In fact, you can get
|
||||
started with virtually all Rust projects you might find that you want to work
|
||||
started with virtually all Rust projects you want to work
|
||||
on with the following commands:
|
||||
|
||||
```bash
|
||||
|
@ -487,6 +513,4 @@ $ cargo build
|
|||
```
|
||||
|
||||
> Note: If you want to look at Cargo in more detail, check out the official
|
||||
[Cargo guide], which covers all of its features.
|
||||
|
||||
[Cargo guide]: http://doc.crates.io/guide.html
|
||||
Cargo guide at *http://doc.crates.io/guide.html*, which covers all of its features.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,6 @@
|
|||
|
||||
[TOC]
|
||||
|
||||
# Common Programming Concepts in Rust
|
||||
|
||||
Let's look at concepts that appear in almost every programming language and see
|
||||
|
@ -42,7 +45,9 @@ $ cargo new --bin bindings
|
|||
$ cd bindings
|
||||
```
|
||||
|
||||
Then open *src/main.rs* and replace its code with the following:
|
||||
Then open `src/main.rs` and replace its code with the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
|
@ -146,6 +151,8 @@ code will be changing this value.
|
|||
|
||||
For example, change the program you just wrote to the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut x = 5;
|
||||
|
@ -176,12 +183,14 @@ depends on the tradeoffs you want to make in your situation.
|
|||
|
||||
As we saw in the guessing game tutorial, we can declare new bindings with the
|
||||
same name as a previous binding, and the new binding *shadows* the previous
|
||||
binding. We say that the first binding is ‘shadowed’ by the second, which means
|
||||
binding. We say that the first binding is *shadowed* by the second, which means
|
||||
that the second binding's value is what you will see when you use the variable
|
||||
after the second binding. This can be useful if you’d like to perform a few
|
||||
transformations on a value, but have the binding be immutable after those
|
||||
transformations have been completed. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
@ -293,6 +302,8 @@ your code if you suspect floating-point size is a problem in your case.
|
|||
|
||||
Here's an example showing floating-point numbers in action:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 2.0; // f64
|
||||
|
@ -310,6 +321,8 @@ Rust supports the usual basic mathematic operations you’d expect for all of
|
|||
these number types: addition, subtraction, multiplication, division, and
|
||||
modulo. This code shows how you'd use each one in a `let` statement:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// addition
|
||||
|
@ -338,6 +351,8 @@ As in most other programming languages, a boolean type in Rust has two possible
|
|||
values: `true` and `false`. The boolean type in Rust is specified with `bool`.
|
||||
For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let t = true;
|
||||
|
@ -356,6 +371,8 @@ So far we’ve only worked with numbers, but Rust supports letters too. Rust’s
|
|||
`char` type is the language's most primitive alphabetic type, and this code
|
||||
shows one way to use it:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let c = 'z';
|
||||
|
@ -375,6 +392,25 @@ about Unicode Scalar Values at
|
|||
*http://www.unicode.org/glossary/#unicode_scalar_value* and find a chart for
|
||||
all unicode code points at *http://www.unicode.org/charts/*.
|
||||
|
||||
#### The Byte Type
|
||||
|
||||
You can work with the bytes of data directly. Byte literals can be created from
|
||||
the ASCII characters using `b` and single quotes:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let byte = b'a';
|
||||
println!("byte is {}", byte);
|
||||
}
|
||||
```
|
||||
|
||||
This will print `byte is 97`. Similarly, byte string literals can be created
|
||||
using `b` and double quotes, like `b"some byte string"`. Note that since you are
|
||||
limited to ASCII characters, it's a best practice to use characters instead of
|
||||
bytes when you're working with natural language text.
|
||||
|
||||
### Compound Types
|
||||
|
||||
*Compound types* can group multiple values of other types into one type. Rust
|
||||
|
@ -389,6 +425,8 @@ types into one compound type.
|
|||
We create a tuple by writing a comma-separated list of values inside
|
||||
parentheses. Each position in the tuple has a distinct type, as in this example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
||||
|
@ -400,6 +438,8 @@ name `tup` to the entire tuple, emphasizing the fact that a tuple is considered
|
|||
a single compound element. We could then use pattern matching to destructure
|
||||
this tuple value, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
||||
|
@ -412,7 +452,7 @@ fn main() {
|
|||
|
||||
In this program, we first create a tuple and bind it to the name `tup`. We then
|
||||
use a pattern with `let` to take `tup` and turn it into three separate
|
||||
bindings, `x`, `y`, and `z`. This is called ‘destructuring’, because it breaks
|
||||
bindings, `x`, `y`, and `z`. This is called *destructuring*, because it breaks
|
||||
the single tuple into three parts.
|
||||
|
||||
Finally, we print the value of `y`, which is `6.4`.
|
||||
|
@ -423,6 +463,8 @@ In addition to destructuring through pattern matching, we can also access a
|
|||
tuple element directly by using a period (`.`) followed by the index of the
|
||||
value we want to access. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x: (i32, f64, u8) = (500, 6.4, 1);
|
||||
|
@ -449,6 +491,8 @@ in Rust have a fixed length-- once declared, they cannot grow or shrink in size.
|
|||
In Rust, the values going into an array are written as a comma separated list
|
||||
inside square brackets:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
@ -466,6 +510,8 @@ and we'll discuss them in more detail in chapter XX.
|
|||
An array is a single chunk of memory, allocated on the stack. We can access
|
||||
elements of an array using indexing, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
@ -487,6 +533,8 @@ values.
|
|||
What happens if you try to access an element of an array past the end of the
|
||||
array? Say we changed our program to:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
@ -533,6 +581,8 @@ words. (Rust also uses snake case for the names of variable bindings; we just
|
|||
haven't used any variable bindings with enough letters to need underscores
|
||||
yet). Here's a program containing an example function definition:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
@ -565,7 +615,7 @@ $ cargo new --bin functions
|
|||
$ cd functions
|
||||
```
|
||||
|
||||
Place the `another_function()` example in a file named *src/main.rs* and run
|
||||
Place the `another_function()` example in a file named `src/main.rs` and run
|
||||
it. You should see the following output:
|
||||
|
||||
```bash
|
||||
|
@ -585,6 +635,8 @@ message is printed.
|
|||
Functions can also take arguments. The following rewritten version of
|
||||
`another_function()` shows what arguments look like in Rust:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
another_function(5);
|
||||
|
@ -617,6 +669,8 @@ the code in order to figure out what you mean.
|
|||
When you want a function to have multiple arguments, just separate them inside
|
||||
the function signature with commas, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
another_function(5, 6);
|
||||
|
@ -666,6 +720,8 @@ evaluate to a resulting value. Let's look at some examples.
|
|||
`Let` bindings are statements. They instruct the program to create a binding
|
||||
name and assign a value to it. `let y = 6;` in this example is a statement:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let y = 6;
|
||||
|
@ -678,6 +734,8 @@ statement as well.
|
|||
Statements do not return values themselves. Therefore, you can’t assign a `let`
|
||||
binding to another binding, as this code tries to do:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let x = (let y = 6);
|
||||
|
@ -718,6 +776,8 @@ y = 6;`, `6` is an expression that evaluates to the value `6`. Calling a
|
|||
function is an expression. Calling a macro is an expression. The block that we
|
||||
use to create new scopes, `{}`, is an expression, for example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
@ -758,6 +818,8 @@ the "return value of the function” is synonymous with “the value of the fina
|
|||
expression in the block of the body of a function.” Here's an example of a
|
||||
function that returns a value:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn five() -> i32 {
|
||||
5
|
||||
|
@ -798,6 +860,8 @@ arguments and defines the type of the return value, but the body of the
|
|||
function is a lonely `5` with no semicolon because it is an expression whose
|
||||
value we want to return. Let's look at another example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
@ -814,6 +878,8 @@ Running this code will print `The value of x is: 6`. What happens if we put a
|
|||
semicolon at the end of the line containing `x + 1`, changing it from an
|
||||
expression to a statement?
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
@ -876,6 +942,8 @@ include `//` on each line, like this:
|
|||
|
||||
Comments can also be placed at the end of lines of code:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let lucky_number = 7; // I’m feeling lucky today.
|
||||
|
@ -884,6 +952,8 @@ fn main() {
|
|||
|
||||
But you’ll more often see them above, like so:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// I’m feeling lucky today.
|
||||
|
@ -918,6 +988,8 @@ $ cd branches
|
|||
Write this sample program using `if` and save it in the *branches* directory in
|
||||
`src/main.rs`:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
@ -934,7 +1006,7 @@ All `if` expressions start with `if`, which is followed by a condition. In this
|
|||
case, our condition is checking if our variable binding `number` has a value
|
||||
that is less than 5. The block of code we want to execute if the condition is
|
||||
true goes immediately after the condition, inside curly braces. These blocks
|
||||
are sometimes called ‘arms’. We can optionally also include an `else`
|
||||
are sometimes called *arms*. We can optionally also include an `else`
|
||||
statement, which we have chosen to do here. `else` gives the program a block of
|
||||
code to execute should `condition` evaluate to false.
|
||||
|
||||
|
@ -966,6 +1038,8 @@ condition was false
|
|||
It’s also worth noting that `condition` here _must_ be a `bool`. To see what
|
||||
happens if the condition isn't a `bool`, try running this code:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
@ -997,6 +1071,8 @@ not automatically try to convert non-boolean types to a boolean here, unlike
|
|||
languages like Ruby or JavaScript. We must be explicit and always give `if` a
|
||||
`boolean` as its condition. If your intention is for the `if` code block to be run if a number is not equal to `0`, for example, we would change the `if` expression to read:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
@ -1014,6 +1090,8 @@ Running this will print "number was something other than zero".
|
|||
We can have multiple coniditions by combining `if` and `else` in an `else if`
|
||||
expression. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 5;
|
||||
|
@ -1054,6 +1132,8 @@ The last detail you need to learn about `if` is that it’s an expression. That
|
|||
means that we can use it on the right hand side of a `let` binding, for
|
||||
instance:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let condition = true;
|
||||
|
@ -1084,6 +1164,8 @@ that results from both arms of the `if` must be the same type; in the previous
|
|||
example, they were both `i32` integers. But what happens if the types are
|
||||
mismatched, as in the following example?
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let condition = true;
|
||||
|
@ -1106,7 +1188,7 @@ single type. If we try to run this, we’ll get an error:
|
|||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
src/main.rs:4:18: 8:6 error: if and else have incompatible types:
|
||||
expected `_`,
|
||||
found `&‘static str`
|
||||
found `&'static str`
|
||||
(expected integral variable,
|
||||
found &-ptr) [E0308]
|
||||
src/main.rs:4 let number = if condition {
|
||||
|
@ -1143,9 +1225,11 @@ in.
|
|||
The `loop` keyword tells Rust to execute a block of code over and over again
|
||||
forever or until we explicitly tell it to stop.
|
||||
|
||||
For an example, change the *src/main.rs* file in your *loops* directory to look
|
||||
For an example, change the `src/main.rs` file in your *loops* directory to look
|
||||
like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
loop {
|
||||
|
@ -1191,6 +1275,8 @@ for it, called a `while` loop. Here's an example using `while`: this program
|
|||
loops three times, counting down each time. Finally, after the loop, it prints
|
||||
another message, then exits:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut number = 3;
|
||||
|
@ -1214,6 +1300,8 @@ this code; otherwise, exit the loop.
|
|||
We could use this `while` construct to loop over the elements of a collection,
|
||||
like an array. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
@ -1254,6 +1342,8 @@ loop.
|
|||
As a more efficient alternative, we can use a `for` loop and execute some code
|
||||
for each item in a collection. A `for` loop looks like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
@ -1272,7 +1362,7 @@ and missing some items.
|
|||
For example, in the previous code that uses the `while` loop, if we removed an
|
||||
item from the `a` array but forgot to update the condition to be `while index <
|
||||
4`, our code would panic. Using the `for` loop means we would not need to
|
||||
remember to change any other code if we changed the the number of values in the
|
||||
remember to change any other code if we changed the number of values in the
|
||||
array.
|
||||
|
||||
If you're wondering about the `.iter()` code in this example, keep reading! We
|
||||
|
@ -1288,6 +1378,8 @@ one number and ending before another number. Here's what the countdown would
|
|||
look like with a for loop, and using another method we haven't yet talked
|
||||
about, `.rev()`, to reverse the range:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
for number in (1..4).rev() {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
[TOC]
|
||||
|
||||
# Understanding Ownership
|
||||
|
||||
Ownership is important to understand: it's Rust's most unique feature, and
|
||||
|
@ -7,7 +10,7 @@ and how Rust lays things out in memory.
|
|||
|
||||
## Ownership
|
||||
|
||||
Rust’s central feature is called ‘ownership’. It is a feature that is
|
||||
Rust’s central feature is called *ownership*. It is a feature that is
|
||||
straightforward to explain, but has deep implications for the rest of the
|
||||
language.
|
||||
|
||||
|
@ -55,8 +58,8 @@ at which it’s declared until the end of the current _scope_. That is:
|
|||
|
||||
In other words, there are two important points in time here:
|
||||
|
||||
- When `s` comes ‘into scope’, it is valid.
|
||||
- It remains so until it ‘goes out of scope’.
|
||||
- When `s` comes "into scope", it is valid.
|
||||
- It remains so until it "goes out of scope".
|
||||
|
||||
At this point, things are similar to other programming languages. Now let’s
|
||||
build on top of this understanding by introducing the `String` type.
|
||||
|
@ -113,8 +116,8 @@ That first part is done by us: when we call `String::from()`, its
|
|||
implementation requests the memory it needs. This is pretty much universal in
|
||||
programming languages.
|
||||
|
||||
The second case, however, is different. In languages with a garbage collector
|
||||
(‘GC’), the GC will keep track and clean up memory that isn't being used
|
||||
The second case, however, is different. In languages with a *garbage collector*
|
||||
(*GC*), the GC will keep track and clean up memory that isn't being used
|
||||
anymore, and we, as the programmer, don’t need to think about it. Without GC,
|
||||
it’s our responsibility to identify when memory is no longer being used and
|
||||
call code to explicitly return it, just as we did to request it. Doing this
|
||||
|
@ -123,7 +126,6 @@ waste memory. If we do it too early, we will have an invalid variable. If we do
|
|||
it twice, that’s a bug too. We need to pair exactly one `allocate()` with
|
||||
exactly one `free()`.
|
||||
|
||||
|
||||
Rust takes a different path. Remember our example? Here’s a version with
|
||||
`String`:
|
||||
|
||||
|
@ -256,20 +258,21 @@ alone will free the memory, and we’re done.
|
|||
|
||||
This leads us to the Ownership Rules:
|
||||
|
||||
> 1. Each value in Rust has a variable binding that’s called its ‘owner’.
|
||||
> 1. Each value in Rust has a variable binding that’s called its *owner*.
|
||||
> 2. There can only be one owner at a time.
|
||||
> 3. When the owner goes out of scope, the value will be `drop()`ped.
|
||||
|
||||
Furthermore, there’s a design choice that’s implied by this: Rust will never
|
||||
automatically create ‘deep’ copies of your data. Therefore, any _automatic_
|
||||
automatically create "deep" copies of your data. Therefore, any _automatic_
|
||||
copying can be assumed to be inexpensive.
|
||||
|
||||
### Clone
|
||||
|
||||
But what if we _do_ want to deeply copy the `String`’s data and not just the
|
||||
`String` itself? There’s a common method for that: `clone()`. We will discuss
|
||||
methods in the section on [`structs`], but they’re a common enough feature
|
||||
in many programming languages that you have probably seen them before.
|
||||
methods in the section on `structs` in Chapter XX, but they’re a
|
||||
common enough feature in many programming languages that you have probably seen
|
||||
them before.
|
||||
|
||||
Here’s an example of the `clone()` method in action:
|
||||
|
||||
|
@ -280,8 +283,6 @@ let s2 = s1.clone();
|
|||
println!("{}", s1);
|
||||
```
|
||||
|
||||
[`structs`]: ch05-01-structs.html
|
||||
|
||||
This will work just fine. Remember our diagram from before? In this case,
|
||||
it _is_ doing this:
|
||||
|
||||
|
@ -333,6 +334,8 @@ but nothing that requires allocation or is some form of resource is `Copy`. Here
|
|||
|
||||
Passing a value to a function has similar semantics as assigning it:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
|
@ -357,6 +360,8 @@ Passing a binding to a function will move or copy, just like assignment. Here’
|
|||
the same example, but with some annotations showing where things go into and
|
||||
out of scope:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello"); // s goes into scope.
|
||||
|
@ -389,6 +394,8 @@ and where the ownership rules prevent you from doing so.
|
|||
|
||||
Returning values can also transfer ownership:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = gives_ownership();
|
||||
|
@ -412,6 +419,8 @@ fn takes_and_gives_back(a_string: String) -> String {
|
|||
|
||||
With similiar annotations:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = gives_ownership(); // gives_ownership moves its return
|
||||
|
@ -452,6 +461,8 @@ also needs to be passed back if we want to use it again, in addition to any
|
|||
data resulting from the body of the function that we might want to return as
|
||||
well. It's _possible_ to return multiple values, using a tuple, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
@ -477,6 +488,8 @@ next section is about.
|
|||
At the end of the last section, we had some example Rust that wasn’t very
|
||||
good. Here it is again:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
@ -499,6 +512,8 @@ function so that we can still use it there, since it was moved when we called
|
|||
|
||||
There is a better way. It looks like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
@ -520,7 +535,7 @@ function return value is gone. Next, note that we pass `&s1` into
|
|||
`calculate_length()`, and in its definition, we take `&String` rather than
|
||||
`String`.
|
||||
|
||||
These `&`s are called ‘references’, and they allow you to refer to some value
|
||||
These `&`s are called *references*, and they allow you to refer to some value
|
||||
without taking ownership of it. Here’s a diagram:
|
||||
|
||||
DIAGRAM GOES HERE of a &String pointing at a String, with (ptr, len, capacity)
|
||||
|
@ -528,11 +543,6 @@ DIAGRAM GOES HERE of a &String pointing at a String, with (ptr, len, capacity)
|
|||
Let’s take a closer look at the function call here:
|
||||
|
||||
```rust
|
||||
# fn calculate_length(s: &String) -> usize {
|
||||
# let length = s.len();
|
||||
#
|
||||
# length
|
||||
# }
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let len = calculate_length(&s1);
|
||||
|
@ -559,13 +569,15 @@ we don’t drop what a reference points to when the reference goes out of scope.
|
|||
This lets us write functions which take references as arguments instead of the
|
||||
values themselves, so that we won’t need to return them to give back ownership.
|
||||
|
||||
There’s another word for what references do, and that’s ‘borrowing’. Just like
|
||||
There’s another word for what references do, and that’s *borrowing*. Just like
|
||||
with real life, if a person owns something, you can borrow it from them. When
|
||||
you’re done, you have to give it back.
|
||||
|
||||
Speaking of which, what if you try to modify something you borrow from me? Try
|
||||
this code out. Spoiler alert: it doesn’t work!
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
|
@ -595,6 +607,8 @@ allowed to modify something we have a reference to.
|
|||
|
||||
We can fix this bug! Just a small tweak:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
@ -613,6 +627,8 @@ String`.
|
|||
|
||||
Mutable references have one big restriction, though. This code fails:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
let mut s = String::from("hello");
|
||||
|
||||
|
@ -693,6 +709,8 @@ out of scope before the reference does.
|
|||
|
||||
Let’s try to create a dangling reference:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let reference_to_nothing = dangle();
|
||||
|
@ -722,7 +740,7 @@ error: aborting due to previous error
|
|||
```
|
||||
|
||||
This error message refers to a feature we haven’t learned about yet,
|
||||
‘lifetimes’. The message does contain the key to why this code is a problem,
|
||||
*lifetimes*. The message does contain the key to why this code is a problem,
|
||||
though:
|
||||
|
||||
```bash
|
||||
|
@ -792,12 +810,14 @@ ownership, so this is fine. But what should we return? We don’t really have a
|
|||
way to talk about _part_ of a string. We could return the index of the end of
|
||||
the word, though. Let’s try that:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn first_word(s: &String) -> usize {
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &item) in bytes.iter().enumerate() {
|
||||
if item == 32 {
|
||||
if item == b' ' {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -832,35 +852,25 @@ match against the tuple with i for the index and &item for a single byte. Since
|
|||
we get a reference from `.iter().enumerate()`, we use `&` in the pattern.
|
||||
|
||||
```rust,ignore
|
||||
if item == 32 {
|
||||
if item == b' ' {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
s.len()
|
||||
```
|
||||
|
||||
We search for the value 32, which represents a space in UTF-8. If we find one,
|
||||
we return the position. Otherwise, we return the length of the string, using
|
||||
`s.len()`.
|
||||
We search for the byte that represents the space, using the byte literal
|
||||
syntax. If we find one, we return the position. Otherwise, we return the length
|
||||
of the string, using `s.len()`.
|
||||
|
||||
This works, but there’s a problem. We’re returning a `usize` on its own, but
|
||||
it’s only a meaningful number in the context of the `&String`. In other
|
||||
words, because it’s a separate value from the `String`, there’s no guarantee
|
||||
that it will still be valid in the future. Consider this:
|
||||
|
||||
```rust
|
||||
# fn first_word(s: &String) -> usize {
|
||||
# let bytes = s.as_bytes();
|
||||
#
|
||||
# for (i, &item) in bytes.iter().enumerate() {
|
||||
# if item == 32 {
|
||||
# return i;
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# s.len()
|
||||
# }
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
||||
|
@ -948,12 +958,14 @@ let slice = &s[..];
|
|||
|
||||
With this in mind, let’s re-write `first_word()` to return a slice:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn first_word(s: &String) -> &str {
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &item) in bytes.iter().enumerate() {
|
||||
if item == 32 {
|
||||
if item == b' ' {
|
||||
return &s[0..i];
|
||||
}
|
||||
}
|
||||
|
@ -977,6 +989,8 @@ We now have a straightforward API that’s much harder to mess up.
|
|||
But what about our error condition from before? Slices also fix that. Using
|
||||
the slice version of `first_word()` will throw an error:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
@ -1046,18 +1060,9 @@ the ability to talk about full `String`s. And additionally, we can take
|
|||
string slices of string literals too, so this function is more useful, but
|
||||
with no loss of functionality:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
# fn first_word(s: &str) -> &str {
|
||||
# let bytes = s.as_bytes();
|
||||
#
|
||||
# for (i, &item) in bytes.iter().enumerate() {
|
||||
# if item == 32 {
|
||||
# return &s[0..i];
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# &s[..]
|
||||
# }
|
||||
fn main() {
|
||||
let my_string = String::from("hello world");
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
[TOC]
|
||||
|
||||
# Structs
|
||||
|
||||
A `struct`, short for "structure", gives us the ability to name and package
|
||||
|
@ -22,6 +25,8 @@ $ cd points
|
|||
Here’s a short program which calculates the distance between two points. Put
|
||||
it into your `src/main.rs`:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x1 = 0.0;
|
||||
|
@ -78,7 +83,7 @@ modules and namespaces in depth yet, but you can think of the `powi()` function
|
|||
as being scoped inside of another name. In this case, the name is `f64`, the
|
||||
same as the type. The `powi()` function takes two arguments: the first is a
|
||||
number, and the second is the power that it raises that number to. In this
|
||||
case, the second number is an integer, hence the ‘i’ in its name. Similarly,
|
||||
case, the second number is an integer, hence the `i` in its name. Similarly,
|
||||
`sqrt()` is a function under the `f64` module, which takes the square root of
|
||||
its argument.
|
||||
|
||||
|
@ -100,6 +105,8 @@ and `(x2, y2)` together.
|
|||
We’ve already discussed one way to do that: tuples. Here’s a version of our
|
||||
program which uses tuples:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let p1 = (0.0, 5.0);
|
||||
|
@ -173,6 +180,8 @@ let x = p1.x;
|
|||
Let’s convert our program to use our `Point` `struct`. Here’s what it looks
|
||||
like now:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
#[derive(Debug,Copy,Clone)]
|
||||
struct Point {
|
||||
|
@ -224,6 +233,8 @@ So far, we’ve been printing values using `{}` in a `println!` macro. If we try
|
|||
that with a struct, however, by default, we'll get an error. Say we have the
|
||||
following program:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
struct Point {
|
||||
x: f64,
|
||||
|
@ -270,6 +281,8 @@ defined. To ask `println!` to use `Debug` formatting with our `Point`, we add
|
|||
the annotation to derive the trait and include `:?` in the print string, like
|
||||
this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
|
@ -298,8 +311,8 @@ Chapter XX.
|
|||
|
||||
## Method Syntax
|
||||
|
||||
In the last section on ownership, we made several references to ‘methods’.
|
||||
Methods look like this:
|
||||
In Chapter 4 when we discussed ownership, we made several references to
|
||||
*methods*. Methods look like this:
|
||||
|
||||
```rust
|
||||
let s1 = "hello";
|
||||
|
@ -310,8 +323,8 @@ let s2 = s1.clone();
|
|||
println!("{}", s1);
|
||||
```
|
||||
|
||||
The call to `clone()` is attached to `s1` with a dot. This is called ‘method
|
||||
syntax’, and it’s a way to call certain functions with a different style.
|
||||
The call to `clone()` is attached to `s1` with a dot. This is called *method
|
||||
syntax*, and it’s a way to call certain functions with a different style.
|
||||
|
||||
Why have two ways to call functions? We’ll talk about some deeper reasons
|
||||
related to ownership in a moment, but one big reason is that methods look nicer
|
||||
|
@ -335,7 +348,7 @@ methods.
|
|||
### Defining methods
|
||||
|
||||
We can define methods with the `impl` keyword. `impl` is short for
|
||||
‘implementation’. Doing so looks like this:
|
||||
*implementation*. Doing so looks like this:
|
||||
|
||||
```rust
|
||||
#[derive(Debug,Copy,Clone)]
|
||||
|
@ -432,28 +445,12 @@ assert_eq!(8.200609733428363, p1.distance(&p2));
|
|||
When we defined `distance()`, we took both `self` and the other argument by
|
||||
reference. Yet, we needed a `&` for `p2` but not `p1`. What gives?
|
||||
|
||||
This feature is called ‘automatic referencing’, and calling methods is one
|
||||
This feature is called *automatic referencing*, and calling methods is one
|
||||
of the few places in Rust that has behavior like this. Here’s how it works:
|
||||
when you call a method with `self.(`, Rust will automatically add in `&`s
|
||||
or `&mut`s to match the signature. In other words, these are the same:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
# impl Point {
|
||||
# fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
# }
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
p1.distance(&p2);
|
||||
(&p1).distance(&p2);
|
||||
```
|
||||
|
@ -471,14 +468,12 @@ s.push_str(" world!");
|
|||
assert_eq!("Hello, world!", s);
|
||||
```
|
||||
|
||||
Because [`push_str()`] has the following signature:
|
||||
Because `push_str()` has the following signature:
|
||||
|
||||
```rust,ignore
|
||||
fn push_str(&mut self, string: &str) {
|
||||
```
|
||||
|
||||
[`push_str()`]: http://doc.rust-lang.org/collections/string/struct.String.html#method.push_str
|
||||
|
||||
This automatic referencing behavior works because methods have a clear receiver
|
||||
— the type of `self` — and in most cases it’s clear given the receiver and name
|
||||
of a method whether the method is just reading (so needs `&self`), mutating (so
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
[TOC]
|
||||
|
||||
# Enums
|
||||
|
||||
Next, let’s look at *enumerations*, which allow you to define a type by
|
||||
|
@ -25,11 +28,6 @@ enumerate all of the possible kinds that our value can have.
|
|||
We can create values of `IpAddrKind` like this:
|
||||
|
||||
```rust
|
||||
# enum IpAddrKind {
|
||||
# V4,
|
||||
# V6,
|
||||
# }
|
||||
#
|
||||
let four = IpAddrKind::V4;
|
||||
let six = IpAddrKind::V6;
|
||||
```
|
||||
|
@ -98,7 +96,7 @@ let loopback = IpAddr::V6(String::from("::1"));
|
|||
```
|
||||
|
||||
You can put any kind of data inside of an enum variant, including another enum!
|
||||
The `IpAddr` enum is [in the standard library][IpAddr], but it embeds two
|
||||
The `IpAddr` enum is in the standard library, but it embeds two
|
||||
different structs inside of its variants:
|
||||
|
||||
```rust
|
||||
|
@ -116,8 +114,6 @@ enum IpAddr {
|
|||
}
|
||||
```
|
||||
|
||||
[IpAddr]: http://doc.rust-lang.org/std/net/enum.IpAddr.html
|
||||
|
||||
Here’s an enum with a variety of types embedded in its variants:
|
||||
|
||||
```rust
|
||||
|
@ -140,7 +136,7 @@ that you already know, except without the `struct` keyword and they are grouped
|
|||
together under the `Message` type. These structs could hold the same data that
|
||||
these enum variants hold:
|
||||
|
||||
```
|
||||
```rust
|
||||
struct QuitMessage; // unit struct
|
||||
struct MoveMessage {
|
||||
x: i32,
|
||||
|
@ -160,7 +156,7 @@ feature that we talked a little bit about in the previous chapter: generics.
|
|||
|
||||
Programming language design is often thought of as which features you include,
|
||||
but it's also about which features you leave out. Rust does not have a feature
|
||||
that is in many other languages: 'null'. In languages with this feature,
|
||||
that is in many other languages: *null*. In languages with this feature,
|
||||
variables can have two states: null or not-null.
|
||||
|
||||
The inventor of this concept has this to say:
|
||||
|
@ -195,13 +191,11 @@ enum Option<T> {
|
|||
}
|
||||
```
|
||||
|
||||
This enum is [provided by the standard library][option], and is so useful that
|
||||
This enum is provided by the standard library, and is so useful that
|
||||
it's even in the prelude; you don't need to import it explicitly. Furthermore,
|
||||
so are its variants: you can say `Some` and `None` directly, without prefixing
|
||||
them with `Option::`.
|
||||
|
||||
[option]: ../std/option/enum.Option.html
|
||||
|
||||
Here's an example of using `Option<T>`:
|
||||
|
||||
```rust
|
||||
|
@ -215,7 +209,7 @@ let absent_number: Option<i32> = None;
|
|||
Let's dig in. First, you'll notice that we used the `<T>` syntax when defining
|
||||
`Option<T>`: it's a generic enum. `Option<T>` has two variants: `Some`, which
|
||||
contains a `T`, and `None`, which has no data associated with it. In some
|
||||
sense, `None` means 'null', and `Some` means 'not null'. So why is this any
|
||||
sense, `None` means "null", and `Some` means "not null". So why is this any
|
||||
better than null?
|
||||
|
||||
In short, because `Option<T>` and `T` are different types. That's a bit too
|
||||
|
@ -230,7 +224,7 @@ let sum = x + y;
|
|||
|
||||
This will not compile. We get an error message like this:
|
||||
|
||||
```text
|
||||
```bash
|
||||
error: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is not
|
||||
satisfied [E0277]
|
||||
|
||||
|
@ -255,11 +249,9 @@ deliberate design decision for Rust to limit null's pervasiveness and increase
|
|||
the safety of Rust code.
|
||||
|
||||
So, how _do_ you get a `T` from an `Option<T>`? The `Option<T>` enum has a
|
||||
large number of methods that you can check out in [its documentation], and
|
||||
large number of methods that you can check out in its documentation, and
|
||||
becoming familiar with them will be extremely useful in your journey with Rust.
|
||||
|
||||
[its documentation]: ../std/option/enum.Option.html
|
||||
|
||||
But we want a deeper understanding than that. If we didn't have those methods
|
||||
defined for us already, what would we do? And more generally, how do we get
|
||||
the inner values out of any enum variant? We need a new feature: `match`.
|
||||
|
@ -332,13 +324,6 @@ print out "Lucky penny!" every time the method was called with a `Coin::Penny`,
|
|||
but would still return the last value of the block, `1`:
|
||||
|
||||
```rust
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter,
|
||||
# }
|
||||
#
|
||||
fn value_in_cents(coin: Coin) -> i32 {
|
||||
match coin {
|
||||
Coin::Penny => {
|
||||
|
@ -385,19 +370,6 @@ created if the coin matches the `Quarter` pattern. Then we can use the binding
|
|||
in the code for that arm:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# enum UsState {
|
||||
# Alabama,
|
||||
# Alaska,
|
||||
# }
|
||||
#
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter(UsState),
|
||||
# }
|
||||
#
|
||||
fn value_in_cents(coin: Coin) -> i32 {
|
||||
match coin {
|
||||
Coin::Penny => 1,
|
||||
|
@ -494,7 +466,7 @@ fn plus_one(x: Option<i32>) -> Option<i32> {
|
|||
A bug! We didn't handle the `None` case. Luckily, it's a bug Rust knows how to
|
||||
catch. If we try to compile this code, we'll get an error:
|
||||
|
||||
```text
|
||||
```bash
|
||||
error: non-exhaustive patterns: `None` not covered [E0004]
|
||||
match x {
|
||||
Some(i) => Some(i + 1),
|
||||
|
@ -539,7 +511,6 @@ There's one more advanced control flow structure we haven't discussed: `if
|
|||
let`. Imagine we're in a situation like this:
|
||||
|
||||
```rust
|
||||
# let some_option = Some(5);
|
||||
match some_option {
|
||||
Some(x) => {
|
||||
// do something with x
|
||||
|
@ -559,7 +530,6 @@ others."
|
|||
Enter `if let`:
|
||||
|
||||
```rust
|
||||
# let some_option = Some(5);
|
||||
if let Some(x) = some_option {
|
||||
// do something with x
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue