mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-02-03 07:48:41 +08:00
undo the 'sentence per line' experiment
I tried it, I didn't like it.
This commit is contained in:
parent
795403668e
commit
7afd6feada
@ -1,8 +1,8 @@
|
|||||||
# Comments
|
# Comments
|
||||||
|
|
||||||
We strive to make our programs easy to understand, but sometimes, some extra explanation is warranted.
|
We strive to make our programs easy to understand, but sometimes, some extra
|
||||||
We can leave notes in our source code that the compiler will ignore.
|
explanation is warranted. We can leave notes in our source code that the
|
||||||
These notes are called ‘comments’.
|
compiler will ignore. These notes are called ‘comments’.
|
||||||
|
|
||||||
Here’s a comment:
|
Here’s a comment:
|
||||||
|
|
||||||
@ -10,8 +10,8 @@ Here’s a comment:
|
|||||||
// Hello, world.
|
// Hello, world.
|
||||||
```
|
```
|
||||||
|
|
||||||
Comments start with two slashes, and last until the end of the line.
|
Comments start with two slashes, and last until the end of the line. Larger
|
||||||
Larger comments will need more lines:
|
comments will need more lines:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// So we’re doing something complicated here, long enough that we need
|
// So we’re doing something complicated here, long enough that we need
|
||||||
@ -40,9 +40,10 @@ That’s all there is to it. Comments are not particularly complicated.
|
|||||||
|
|
||||||
## Documentation comments
|
## Documentation comments
|
||||||
|
|
||||||
However, Rust has another kind of comment: a documentation comment.
|
However, Rust has another kind of comment: a documentation comment. These
|
||||||
These comments don’t affect the way that the code works, but they do work with Rust’s tools.
|
comments don’t affect the way that the code works, but they do work with Rust’s
|
||||||
More specifically, the `rustdoc` tool that comes with Rust reads documentation comments and produces HTML documentation from them.
|
tools. More specifically, the `rustdoc` tool that comes with Rust reads
|
||||||
|
documentation comments and produces HTML documentation from them.
|
||||||
|
|
||||||
Documentation comments use an extra slash:
|
Documentation comments use an extra slash:
|
||||||
|
|
||||||
@ -58,11 +59,12 @@ fn bar() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This comment would then be interpreted by `rustdoc` as documenting the thing that follows it: `foo()` and `bar()`.
|
This comment would then be interpreted by `rustdoc` as documenting the thing
|
||||||
|
that follows it: `foo()` and `bar()`.
|
||||||
|
|
||||||
Because documentation comments have semantic meaning to `rustdoc`, the compiler will pay attention to the placement
|
Because documentation comments have semantic meaning to `rustdoc`, the compiler
|
||||||
of your documentation comments.
|
will pay attention to the placement of your documentation comments. For
|
||||||
For example, a program with only this:
|
example, a program with only this:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
/// What am I documenting?
|
/// What am I documenting?
|
||||||
@ -78,6 +80,7 @@ src/main.rs:1 /// What am I documenting?
|
|||||||
|
|
||||||
### Inner documentation comments
|
### Inner documentation comments
|
||||||
|
|
||||||
There is a secondary form of a documentation comment, an ‘inner’ documentation comment.
|
There is a secondary form of a documentation comment, an ‘inner’ documentation
|
||||||
Inner documentation comments look like this: `//!`.
|
comment. Inner documentation comments look like this: `//!`. They are only
|
||||||
They are only really used to document modules, however, and so we will talk about them in the modules section.
|
really used to document modules, however, and so we will talk about them in the
|
||||||
|
modules section.
|
||||||
|
143
src/functions.md
143
src/functions.md
@ -1,6 +1,7 @@
|
|||||||
# Functions
|
# Functions
|
||||||
|
|
||||||
Functions are pervasive in Rust code. We’ve already seen the most important function, `main()`, in previous sections of the book:
|
Functions are pervasive in Rust code. We’ve already seen the most important
|
||||||
|
function, `main()`, in previous sections of the book:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -16,9 +17,9 @@ fn another_function() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust code uses `snake_case` as a style for function names: all lower case, with underscores separating words.
|
Rust code uses `snake_case` as a style for function names: all lower case, with
|
||||||
(It also uses them for variable names, too.)
|
underscores separating words. (It also uses them for variable names, too.) We
|
||||||
We can can call any function we’ve defined by using its name and some parentheses:
|
can can call any function we’ve defined by using its name and some parentheses:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -32,9 +33,9 @@ fn another_function() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let’s start a new project to explore functions.
|
Let’s start a new project to explore functions. Open a terminal, and navigate
|
||||||
Open a terminal, and navigate to the directory where you’d like to keep your projects.
|
to the directory where you’d like to keep your projects. From there, use Cargo
|
||||||
From there, use Cargo to generate a new project:
|
to generate a new project:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo new --bin functions
|
$ cargo new --bin functions
|
||||||
@ -51,8 +52,9 @@ Hello, world!
|
|||||||
Another function.
|
Another function.
|
||||||
```
|
```
|
||||||
|
|
||||||
As we can see, the lines execute in order: first, we print out our “Hello, world!” message, and then `another_function()` is called.
|
As we can see, the lines execute in order: first, we print out our “Hello,
|
||||||
It then prints its message as well.
|
world!” message, and then `another_function()` is called. It then prints its
|
||||||
|
message as well.
|
||||||
|
|
||||||
## Function Arguments
|
## Function Arguments
|
||||||
|
|
||||||
@ -89,16 +91,17 @@ Declaring a function which takes a single argument looks like this:
|
|||||||
fn NAME(PATTERN: TYPE) {
|
fn NAME(PATTERN: TYPE) {
|
||||||
```
|
```
|
||||||
|
|
||||||
That’s right, patterns appear again.
|
That’s right, patterns appear again. Consider how the parameter declaration
|
||||||
Consider how the parameter declaration here looks like the `let` bindings we used earlier:
|
here looks like the `let` bindings we used earlier:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let x: i32;
|
let x: i32;
|
||||||
fn another_function(x: i32) {
|
fn another_function(x: i32) {
|
||||||
```
|
```
|
||||||
|
|
||||||
There’s only one difference here: in function signatures, we _must_ declare the type.
|
There’s only one difference here: in function signatures, we _must_ declare the
|
||||||
This is a deliberate decision; we find that requiring type annotations in functions means that you almost never need them anywhere else.
|
type. This is a deliberate decision; we find that requiring type annotations in
|
||||||
|
functions means that you almost never need them anywhere else.
|
||||||
|
|
||||||
You can separate multiple arguments with a comma:
|
You can separate multiple arguments with a comma:
|
||||||
|
|
||||||
@ -155,9 +158,10 @@ The value of x is: 5
|
|||||||
The value of y is: 6
|
The value of y is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that our bindings are called `a` and `b`, yet inside of the function, we refer to them by the names in the signature, `x` and `y`.
|
Note that our bindings are called `a` and `b`, yet inside of the function, we
|
||||||
Inside a function, only its parameters are in scope, so we need to use those names.
|
refer to them by the names in the signature, `x` and `y`. Inside a function,
|
||||||
Bindings passed as parameters don’t need to have the same name as the arguments.
|
only its parameters are in scope, so we need to use those names. Bindings
|
||||||
|
passed as parameters don’t need to have the same name as the arguments.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
|
|
||||||
@ -167,8 +171,8 @@ Functions can also return values back to the function that called them:
|
|||||||
fn NAME(PATTERN, PATTERN, PATTERN, PATTERN...) -> TYPE {
|
fn NAME(PATTERN, PATTERN, PATTERN, PATTERN...) -> TYPE {
|
||||||
```
|
```
|
||||||
|
|
||||||
We don’t name return values, but we do declare their type, after an arrow: `->`.
|
We don’t name return values, but we do declare their type, after an arrow:
|
||||||
Here’s a sample program:
|
`->`. Here’s a sample program:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -191,9 +195,8 @@ $ cargo run
|
|||||||
The value of x is: 5
|
The value of x is: 5
|
||||||
```
|
```
|
||||||
|
|
||||||
Let’s examine this in more detail.
|
Let’s examine this in more detail. There are two important bits. First, we can
|
||||||
There are two important bits.
|
use the return value of a function to initialize a binding:
|
||||||
First, we can use the return value of a function to initialize a binding:
|
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let x = five();
|
let x = five();
|
||||||
@ -213,11 +216,10 @@ fn five() -> i32 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We have no arguments, and our return type, `i32`.
|
We have no arguments, and our return type, `i32`. However, the body of this
|
||||||
However, the body of this function is a lonely `5`.
|
function is a lonely `5`. There’s a detail here that you may or may not have
|
||||||
There’s a detail here that you may or may not have noticed: we’ve ended almost every line in our programs with a semicolon.
|
noticed: we’ve ended almost every line in our programs with a semicolon.
|
||||||
There’s no semicolon here, though.
|
There’s no semicolon here, though. Why not?
|
||||||
Why not?
|
|
||||||
|
|
||||||
The answer to this question is:
|
The answer to this question is:
|
||||||
|
|
||||||
@ -228,16 +230,15 @@ Let’s go over that now.
|
|||||||
|
|
||||||
## Statements and Expressions
|
## Statements and Expressions
|
||||||
|
|
||||||
Expressions are bits of code that evaluate to a value.
|
Expressions are bits of code that evaluate to a value. Consider some math
|
||||||
Consider some math operations, like this:
|
operations, like this:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
5 + 6
|
5 + 6
|
||||||
```
|
```
|
||||||
|
|
||||||
We can evaluate this expression, and come up with a value: `11`.
|
We can evaluate this expression, and come up with a value: `11`. In Rust, most
|
||||||
In Rust, most bits of code are expressions.
|
bits of code are expressions. For example, calling a function is an expression:
|
||||||
For example, calling a function is an expression:
|
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
foo(5)
|
foo(5)
|
||||||
@ -245,15 +246,13 @@ foo(5)
|
|||||||
|
|
||||||
The value is equal to whatever the return value of `foo()` is.
|
The value is equal to whatever the return value of `foo()` is.
|
||||||
|
|
||||||
So why does this matter?
|
So why does this matter? Well, not everything is an expression. Some things are
|
||||||
Well, not everything is an expression.
|
‘statements’. Expressions _compute_ something, but statements _bind_ or _do_
|
||||||
Some things are ‘statements’.
|
something. It’s a subtle difference. We’ve already seen two kinds of
|
||||||
Expressions _compute_ something, but statements _bind_ or _do_ something.
|
statements: `let` statements, and `fn` declarations.
|
||||||
It’s a subtle difference.
|
|
||||||
We’ve already seen two kinds of statements: `let` statements, and `fn` declarations.
|
|
||||||
|
|
||||||
Because `let` is a statement, not an expression, you can’t assign it to another binding.
|
Because `let` is a statement, not an expression, you can’t assign it to another
|
||||||
Here’s an example that doesn’t work:
|
binding. Here’s an example that doesn’t work:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -277,10 +276,9 @@ Could not compile `functions`.
|
|||||||
|
|
||||||
We also cannot somehow assign a `fn` declaration to a binding, either.
|
We also cannot somehow assign a `fn` declaration to a binding, either.
|
||||||
|
|
||||||
So what’s this have to do with return values?
|
So what’s this have to do with return values? Well, `{}`, a ‘block’ that we
|
||||||
Well, `{}`, a ‘block’ that we used earlier to create new scopes, _is_ an expression.
|
used earlier to create new scopes, _is_ an expression. Let’s take a closer look
|
||||||
Let’s take a closer look at `{}`.
|
at `{}`. It looks like this:
|
||||||
It looks like this:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
{
|
{
|
||||||
@ -289,9 +287,10 @@ It looks like this:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `*` there means ‘zero or more’, so we can have any number of statements followed by an expression.
|
The `*` there means ‘zero or more’, so we can have any number of statements
|
||||||
Since blocks are expressions themselves, we can nest blocks inside of blocks.
|
followed by an expression. Since blocks are expressions themselves, we can nest
|
||||||
And since they return a value, we can use them in `let` statements:
|
blocks inside of blocks. And since they return a value, we can use them in
|
||||||
|
`let` statements:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -323,8 +322,9 @@ let y = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Since the block can contain statements, we create a new variable binding, `z`, and give it a value.
|
Since the block can contain statements, we create a new variable binding, `z`,
|
||||||
We then do some math for the final expression of the block:
|
and give it a value. We then do some math for the final expression of the
|
||||||
|
block:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
{
|
{
|
||||||
@ -334,8 +334,8 @@ We then do some math for the final expression of the block:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`5 + 1 + 5` is `11`, and so the value of the entire block is `11`.
|
`5 + 1 + 5` is `11`, and so the value of the entire block is `11`. This gets
|
||||||
This gets substituted into our `let` statement for `y`:
|
substituted into our `let` statement for `y`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let y = 11;
|
let y = 11;
|
||||||
@ -343,8 +343,7 @@ let y = 11;
|
|||||||
|
|
||||||
Hence our output saying `y` is `11`.
|
Hence our output saying `y` is `11`.
|
||||||
|
|
||||||
Where else do we use blocks? As the body of functions!
|
Where else do we use blocks? As the body of functions! They’re very similar:
|
||||||
They’re very similar:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -376,8 +375,8 @@ The value of y is: 6
|
|||||||
The value of y is: 6
|
The value of y is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
In both cases, we use a block to produce a value.
|
In both cases, we use a block to produce a value. In the first case, it’s
|
||||||
In the first case, it’s assigning with `let`:
|
assigning with `let`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let y = {
|
let y = {
|
||||||
@ -391,8 +390,9 @@ fn plus_one(x: i32) -> i32 {
|
|||||||
|
|
||||||
### Expression statements
|
### Expression statements
|
||||||
|
|
||||||
There’s one more detail about expressions and statements: a semicolon takes any expression, and turns it into a statement.
|
There’s one more detail about expressions and statements: a semicolon takes any
|
||||||
Let’s accidentally cause an error with `plus_one()`:
|
expression, and turns it into a statement. Let’s accidentally cause an error
|
||||||
|
with `plus_one()`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -406,7 +406,8 @@ fn plus_one(x: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead of an expression, `x + 1`, we’ve now turned it into a statement, `x + 1;`.
|
Instead of an expression, `x + 1`, we’ve now turned it into a statement,
|
||||||
|
`x + 1;`.
|
||||||
|
|
||||||
Running this gives an error:
|
Running this gives an error:
|
||||||
|
|
||||||
@ -425,8 +426,9 @@ error: aborting due to previous error
|
|||||||
Could not compile `functions`.
|
Could not compile `functions`.
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust has our back here: it even suggests removing the semicolon, which fixes the error.
|
Rust has our back here: it even suggests removing the semicolon, which fixes
|
||||||
But the main error message is the core of the issue: statements don’t evaluate to a value, yet we want to return an `i32`.
|
the error. But the main error message is the core of the issue: statements
|
||||||
|
don’t evaluate to a value, yet we want to return an `i32`.
|
||||||
|
|
||||||
In practice, Rust programmers don’t often think about these rules at this
|
In practice, Rust programmers don’t often think about these rules at this
|
||||||
level. Usually, you have a semicolon at the end of most lines, and maybe not at
|
level. Usually, you have a semicolon at the end of most lines, and maybe not at
|
||||||
@ -434,8 +436,8 @@ the end of blocks.
|
|||||||
|
|
||||||
## Multiple return values
|
## Multiple return values
|
||||||
|
|
||||||
Functions cannot directly return multiple values.
|
Functions cannot directly return multiple values. There’s a trick, however.
|
||||||
There’s a trick, however. Remember the `()`s we used when showing off complex bindings?
|
Remember the `()`s we used when showing off complex bindings?
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -443,9 +445,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
They form something called a ‘tuple’, one of Rust’s basic types.
|
They form something called a ‘tuple’, one of Rust’s basic types. A tuple is an
|
||||||
A tuple is an anonymous collection of elements.
|
anonymous collection of elements. But since a tuple is a singular thing, we can
|
||||||
But since a tuple is a singular thing, we can use it as a way to return multiple values from functions:
|
use it as a way to return multiple values from functions:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -470,7 +472,8 @@ The value of x is: 5
|
|||||||
The value of y is: 6
|
The value of y is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
There are two interesting changes here: assigning the return value of `two_numbers()` to `x` and `y`, and the declaration of `two_numbers()` itself.
|
There are two interesting changes here: assigning the return value of
|
||||||
|
`two_numbers()` to `x` and `y`, and the declaration of `two_numbers()` itself.
|
||||||
|
|
||||||
Let’s look at the declaration first:
|
Let’s look at the declaration first:
|
||||||
|
|
||||||
@ -480,15 +483,14 @@ fn two_numbers() -> (i32, i32) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `(i32, i32)` should look familiar.
|
The `(i32, i32)` should look familiar. We saw it in `let` bindings earlier:
|
||||||
We saw it in `let` bindings earlier:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let (x, y): (i32, i32) = (5, 6);
|
let (x, y): (i32, i32) = (5, 6);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `(i32, i32)` syntax says “a tuple with two `i32`s in it.”
|
The `(i32, i32)` syntax says “a tuple with two `i32`s in it.” The `(5, 6)`
|
||||||
The `(5, 6)` syntax creates a new one, with `5` and `6`.
|
syntax creates a new one, with `5` and `6`.
|
||||||
|
|
||||||
This tuple is then returned, and assigned to `x` and `y`:
|
This tuple is then returned, and assigned to `x` and `y`:
|
||||||
|
|
||||||
@ -498,4 +500,5 @@ let (x, y) = two_numbers();
|
|||||||
|
|
||||||
See how all these bits fit together?
|
See how all these bits fit together?
|
||||||
|
|
||||||
We call this behavior of `let` ‘destructuring’, because it takes the structure of the expression that comes after the `=` and takes it apart.
|
We call this behavior of `let` ‘destructuring’, because it takes the structure
|
||||||
|
of the expression that comes after the `=` and takes it apart.
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
# Primitive Types
|
# Primitive Types
|
||||||
|
|
||||||
We’ve seen that every value in Rust has a type of some kind.
|
We’ve seen that every value in Rust has a type of some kind. There are a number
|
||||||
There are a number of types which are built into the language itself.
|
of types which are built into the language itself. We call these types
|
||||||
We call these types ‘primitive’ types, since you can’t re-create them yourself.
|
‘primitive’ types, since you can’t re-create them yourself. There are, of
|
||||||
There are, of course, many non-primitive types provided by the standard library as well.
|
course, many non-primitive types provided by the standard library as well.
|
||||||
|
|
||||||
Remember, you can rely on type inference to figure out the type of a binding, or you can annotate it explicitly:
|
Remember, you can rely on type inference to figure out the type of a binding,
|
||||||
|
or you can annotate it explicitly:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -15,8 +16,8 @@ fn main() {
|
|||||||
|
|
||||||
## Integers
|
## Integers
|
||||||
|
|
||||||
You’ve already seen one primitive type: `i32`.
|
You’ve already seen one primitive type: `i32`. There are a number of built-in
|
||||||
There are a number of built-in number types in Rust.
|
number types in Rust.
|
||||||
|
|
||||||
Here’s a chart of Rust’s integer types:
|
Here’s a chart of Rust’s integer types:
|
||||||
|
|
||||||
@ -28,24 +29,24 @@ Here’s a chart of Rust’s integer types:
|
|||||||
| 64-bit | i64 | u64 |
|
| 64-bit | i64 | u64 |
|
||||||
| arch | isize | usize |
|
| arch | isize | usize |
|
||||||
|
|
||||||
We have both signed and unsigned variants of numbers, and each variant has an explicit size.
|
We have both signed and unsigned variants of numbers, and each variant has an
|
||||||
Unsigned numbers are always positive, and signed numbers can be positive or negative.
|
explicit size. Unsigned numbers are always positive, and signed numbers can be
|
||||||
(Think ‘plus sign’ or ‘minus sign’: that’s a signed number.)
|
positive or negative. (Think ‘plus sign’ or ‘minus sign’: that’s a signed
|
||||||
Signed numbers are stored using ‘two’s compliment’ representation.
|
number.) Signed numbers are stored using ‘two’s compliment’ representation.
|
||||||
|
|
||||||
Finally, `isize` and `usize` are different sizes based on the kind of computer your program is running on.
|
Finally, `isize` and `usize` are different sizes based on the kind of computer
|
||||||
If you are on a 64-bit architecture, they are 64 bits, and if you’re on a 32-bit one, they’re 32 bits.
|
your program is running on. If you are on a 64-bit architecture, they are 64
|
||||||
|
bits, and if you’re on a 32-bit one, they’re 32 bits.
|
||||||
|
|
||||||
So how do you choose from all these options? Well, if you really don’t know, the defaults are a good choice:
|
So how do you choose from all these options? Well, if you really don’t know,
|
||||||
integer types default to `i32`.
|
the defaults are a good choice: integer types default to `i32`. The primary use
|
||||||
The primary use case for `isize`/`usize` is when indexing some sort of collection.
|
case for `isize`/`usize` is when indexing some sort of collection. We’ll talk
|
||||||
We’ll talk more about our first collection, arrays, in just a moment.
|
more about our first collection, arrays, in just a moment.
|
||||||
|
|
||||||
## Floating-point numbers
|
## Floating-point numbers
|
||||||
|
|
||||||
Rust also has two primitive floating-point numbers: `f32` and `f64`.
|
Rust also has two primitive floating-point numbers: `f32` and `f64`. They are
|
||||||
They are 32 bits and 64 bits in size, respectively.
|
32 bits and 64 bits in size, respectively. The default is `f64`.
|
||||||
The default is `f64`.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -60,9 +61,8 @@ Floating-point numbers are represented according to the IEEE-754 standard.
|
|||||||
|
|
||||||
## Tuples
|
## Tuples
|
||||||
|
|
||||||
The other type we’ve seen previously is the tuple type.
|
The other type we’ve seen previously is the tuple type. Tuples have an ‘arity’,
|
||||||
Tuples have an ‘arity’, or size.
|
or size. We might say “that’s a 3-tuple” or “that’s a 5-tuple.”
|
||||||
We might say “that’s a 3-tuple” or “that’s a 5-tuple.”
|
|
||||||
|
|
||||||
Each position in a tuple has a distinct type:
|
Each position in a tuple has a distinct type:
|
||||||
|
|
||||||
@ -72,12 +72,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Tuples are used sparingly in Rust code.
|
Tuples are used sparingly in Rust code. This is because the elements of a tuple
|
||||||
This is because the elements of a tuple are anonymous, which can make code hard to read.
|
are anonymous, which can make code hard to read.
|
||||||
|
|
||||||
### Tuple indexing
|
### Tuple indexing
|
||||||
|
|
||||||
To access an element of a tuple, we use a `.` followed by the index we want to access:
|
To access an element of a tuple, we use a `.` followed by the index we want to
|
||||||
|
access:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -93,8 +94,8 @@ As you can see, the first index is `0`.
|
|||||||
|
|
||||||
### Single-element tuples
|
### Single-element tuples
|
||||||
|
|
||||||
There’s one last trick with tuples: `(5)` is actually ambiguous: is it a tuple, or is it a `5` in parethesis?
|
There’s one last trick with tuples: `(5)` is actually ambiguous: is it a tuple,
|
||||||
If you need to disambiguate, use a comma:
|
or is it a `5` in parethesis? If you need to disambiguate, use a comma:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -106,10 +107,9 @@ fn main() {
|
|||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
There’s one more type that we’ve been using, but you haven’t seen written explicitly.
|
There’s one more type that we’ve been using, but you haven’t seen written
|
||||||
Functions!
|
explicitly. Functions! Functions also have a type, and yes, you can even have
|
||||||
Functions also have a type, and yes, you can even have variables which hold functions!
|
variables which hold functions! Here’s an example:
|
||||||
Here’s an example:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn plus_one(x: i32) -> i32 {
|
fn plus_one(x: i32) -> i32 {
|
||||||
@ -124,8 +124,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, the type is very similar to the declaration.
|
As you can see, the type is very similar to the declaration. Here, let’s put
|
||||||
Here, let’s put them side by side:
|
them side by side:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn(i32) -> i32 // type
|
fn(i32) -> i32 // type
|
||||||
@ -148,7 +148,8 @@ fn(i32) -> i32 {
|
|||||||
|
|
||||||
It’s the same! Well, we need to drop that `{` as well.
|
It’s the same! Well, we need to drop that `{` as well.
|
||||||
|
|
||||||
Finally, if you’ll notice in that example, we can create a binding with a function in it:
|
Finally, if you’ll notice in that example, we can create a binding with a
|
||||||
|
function in it:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -162,9 +163,8 @@ fn main() {
|
|||||||
|
|
||||||
### Functions as arguments
|
### Functions as arguments
|
||||||
|
|
||||||
So why not just use the original name?
|
So why not just use the original name? Well, we can pass functions as arguments
|
||||||
Well, we can pass functions as arguments to other functions!
|
to other functions! Check this out:
|
||||||
Check this out:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn plus_one(x: i32) -> i32 {
|
fn plus_one(x: i32) -> i32 {
|
||||||
@ -208,10 +208,12 @@ Let’s investigate in more detail.
|
|||||||
fn twice(x: i32, f: fn(i32) -> i32) -> i32 {
|
fn twice(x: i32, f: fn(i32) -> i32) -> i32 {
|
||||||
```
|
```
|
||||||
|
|
||||||
This says “`twice()` is a function which takes two arguments.
|
This says “`twice()` is a function which takes two arguments. `x` is a
|
||||||
`x` is a thirty-two bit integer, and `f` is a function which takes an `i32` and returns an `i32`.”
|
thirty-two bit integer, and `f` is a function which takes an `i32` and returns
|
||||||
|
an `i32`.”
|
||||||
|
|
||||||
Inside of `twice()`, as you might imagine, we call the function `f` twice on `x`, and return the result.
|
Inside of `twice()`, as you might imagine, we call the function `f` twice on
|
||||||
|
`x`, and return the result.
|
||||||
|
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
@ -219,17 +221,17 @@ let y = twice(x, plus_one);
|
|||||||
let z = twice(x, plus_two);
|
let z = twice(x, plus_two);
|
||||||
```
|
```
|
||||||
|
|
||||||
The first time we call `twice()`, we pass `plus_one()` as an argument.
|
The first time we call `twice()`, we pass `plus_one()` as an argument. And `x`
|
||||||
And `x` is `5`.
|
is `5`. So `5 + 1 + 1 == 7`, hence our first line of output. The second time,
|
||||||
So `5 + 1 + 1 == 7`, hence our first line of output.
|
we pass `plus_two()` instead. `5 + 2 + 2` is `9`, and our second line checks
|
||||||
The second time, we pass `plus_two()` instead.
|
out too.
|
||||||
`5 + 2 + 2` is `9`, and our second line checks out too.
|
|
||||||
|
|
||||||
Passing functions to functions is very, very powerful.
|
Passing functions to functions is very, very powerful.
|
||||||
|
|
||||||
## Booleans
|
## Booleans
|
||||||
|
|
||||||
Somewhat fundamental to all computing, Rust has a boolean type, `bool`, with two possible values:
|
Somewhat fundamental to all computing, Rust has a boolean type, `bool`, with
|
||||||
|
two possible values:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -242,10 +244,10 @@ That’s really all there is to say about that!
|
|||||||
|
|
||||||
## Arrays
|
## Arrays
|
||||||
|
|
||||||
So far, we’ve only represented single values in a binding.
|
So far, we’ve only represented single values in a binding. Sometimes, though,
|
||||||
Sometimes, though, it’s useful to have more than one value.
|
it’s useful to have more than one value. These kinds of data structures are
|
||||||
These kinds of data structures are called ‘collections’, and arrays are the ones we’ll learn about first.
|
called ‘collections’, and arrays are the ones we’ll learn about first. Arrays
|
||||||
Arrays look like this:
|
look like this:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -253,7 +255,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
An array’s type consists of the type of the elements it contains, as well as the length:
|
An array’s type consists of the type of the elements it contains, as well as
|
||||||
|
the length:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -274,9 +277,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, `first` will hold the value `1`, and `second` will be bound to `2`.
|
In this example, `first` will hold the value `1`, and `second` will be bound to
|
||||||
Note that these values are copied out of the array; if the array changes, these bindings will not.
|
`2`. Note that these values are copied out of the array; if the array changes,
|
||||||
Here’s an example, which also shows us how we can modify elements of the array:
|
these bindings will not. Here’s an example, which also shows us how we can
|
||||||
|
modify elements of the array:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -290,18 +294,21 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Running this example will show that `first` is still `1`.
|
Running this example will show that `first` is still `1`. If we didn’t want a
|
||||||
If we didn’t want a copy, but instead wanted to refer to the first element, whatever its value was, we need a new concept.
|
copy, but instead wanted to refer to the first element, whatever its value was,
|
||||||
We’ll talk about ‘references’ in Section 4.
|
we need a new concept. We’ll talk about ‘references’ in Section 4.
|
||||||
|
|
||||||
One last thing: now that we are modifying the array, `a` needs to be declared `mut`.
|
One last thing: now that we are modifying the array, `a` needs to be declared
|
||||||
|
`mut`.
|
||||||
|
|
||||||
Arrays are our first real data structure, and so there’s a few other concepts that we haven’t covered in full yet.
|
Arrays are our first real data structure, and so there’s a few other concepts
|
||||||
There are two: the `panic!` macro, and a new way of printing things: `Debug`.
|
that we haven’t covered in full yet. There are two: the `panic!` macro, and a
|
||||||
|
new way of printing things: `Debug`.
|
||||||
|
|
||||||
### Panic
|
### Panic
|
||||||
|
|
||||||
We showed what happens when you access elements of an array, but what if we give an invalid index?
|
We showed what happens when you access elements of an array, but what if we
|
||||||
|
give an invalid index?
|
||||||
|
|
||||||
```rust,should_panic
|
```rust,should_panic
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -313,9 +320,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If we run this example, we will get an error.
|
If we run this example, we will get an error. Let’s re-use our `functions`
|
||||||
Let’s re-use our `functions` project from before.
|
project from before. Change your `src/main.rs` to look like the example, and
|
||||||
Change your `src/main.rs` to look like the example, and run it:
|
run it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo run
|
$ cargo run
|
||||||
@ -325,8 +332,9 @@ thread ‘<main>’ panicked at ‘index out of bounds: the len is 5 but the ind
|
|||||||
Process didn’t exit successfully: `target/debug/functions` (exit code: 101)
|
Process didn’t exit successfully: `target/debug/functions` (exit code: 101)
|
||||||
```
|
```
|
||||||
|
|
||||||
It says that our thread panicked, and that our program didn’t exit successfully.
|
It says that our thread panicked, and that our program didn’t exit
|
||||||
There’s also a reason: we had a length of five, but an index of 10.
|
successfully. There’s also a reason: we had a length of five, but an index of
|
||||||
|
10.
|
||||||
|
|
||||||
A ‘panic’ can also be induced manually, with the `panic!` macro:
|
A ‘panic’ can also be induced manually, with the `panic!` macro:
|
||||||
|
|
||||||
@ -336,27 +344,24 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When the `panic!` macro runs, it will cause a panic.
|
When the `panic!` macro runs, it will cause a panic. When a Rust program
|
||||||
When a Rust program panics, it starts a kind of controlled crash.
|
panics, it starts a kind of controlled crash. The current thread of execution
|
||||||
The current thread of execution will stop entirely.
|
will stop entirely. As such, panics are reserved for serious, program-ending
|
||||||
As such, panics are reserved for serious, program-ending errors.
|
errors. They’re not a general error-handling mechanism.
|
||||||
They’re not a general error-handling mechanism.
|
|
||||||
|
|
||||||
So why did this code panic?
|
So why did this code panic? Well, arrays know how many elements they hold. When
|
||||||
Well, arrays know how many elements they hold.
|
we access an element via indexing, Rust will check that the index is less than
|
||||||
When we access an element via indexing, Rust will check that the index is less than the length.
|
the length. If it’s greater, it will panic, as something is very wrong. This is
|
||||||
If it’s greater, it will panic, as something is very wrong.
|
our first example of Rust’s safety principles in action. In many low-level
|
||||||
This is our first example of Rust’s safety principles in action.
|
languages, this kind of check is not done. If you have an incorrect index,
|
||||||
In many low-level languages, this kind of check is not done.
|
invalid memory can be accessed. Rust protects us against this kind of error.
|
||||||
If you have an incorrect index, invalid memory can be accessed.
|
|
||||||
Rust protects us against this kind of error.
|
|
||||||
|
|
||||||
**Steve’s note: this next bit might be our first ‘advanced’ section, on get()?**
|
**Steve’s note: this next bit might be our first ‘advanced’ section, on get()?**
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
So far, we’ve been printing values using `{}`.
|
So far, we’ve been printing values using `{}`. If we try that with an array,
|
||||||
If we try that with an array, though...
|
though...
|
||||||
|
|
||||||
```ignore
|
```ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -383,18 +388,18 @@ src/main.rs:4:25: 4:26 note: required by `core::fmt::Display::fmt`
|
|||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
```
|
```
|
||||||
|
|
||||||
Whew! The core of the error is this part: the trait `core::fmt::Display` is not implemented.
|
Whew! The core of the error is this part: the trait `core::fmt::Display` is not
|
||||||
We haven’t discussed traits yet, so this is bound to be confusing!
|
implemented. We haven’t discussed traits yet, so this is bound to be confusing!
|
||||||
Here’s all we need to know for now: `println!` can do many kinds of formatting.
|
Here’s all we need to know for now: `println!` can do many kinds of formatting.
|
||||||
By default, `{}` implements a kind of formatting known as `Display`: output for end-users.
|
By default, `{}` implements a kind of formatting known as `Display`: output for
|
||||||
The primitive types we’ve seen so far implement `Display`, as there’s only one way you’d show a `1` to a user.
|
end-users. The primitive types we’ve seen so far implement `Display`, as
|
||||||
But with arrays, the output is less clear.
|
there’s only one way you’d show a `1` to a user. But with arrays, the output is
|
||||||
Do you want commas or not?
|
less clear. Do you want commas or not? What about the `[]`s?
|
||||||
What about the `[]`s?
|
|
||||||
|
|
||||||
Due to these questions, more complex types in the standard library do not implement `Display` formatting.
|
Due to these questions, more complex types in the standard library do not
|
||||||
There is another kind of formatting, `Debug`, which is a bit different: output for programmers and debuggers.
|
implement `Display` formatting. There is another kind of formatting, `Debug`,
|
||||||
We can ask `println!` to use `Debug` formatting with `:?`:
|
which is a bit different: output for programmers and debuggers. We can ask
|
||||||
|
`println!` to use `Debug` formatting with `:?`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -413,13 +418,13 @@ $ cargo run
|
|||||||
a is [1, 2, 3, 4, 5]
|
a is [1, 2, 3, 4, 5]
|
||||||
```
|
```
|
||||||
|
|
||||||
You’ll see this repeated later, with other types.
|
You’ll see this repeated later, with other types. And we’ll cover traits fully
|
||||||
And we’ll cover traits fully later in the book, Section 9.
|
later in the book, Section 9.
|
||||||
|
|
||||||
## char
|
## char
|
||||||
|
|
||||||
We’ve only worked with numbers so far, but what about letters?
|
We’ve only worked with numbers so far, but what about letters? Rust’s most
|
||||||
Rust’s most primitive alphabetic type is the `char`:
|
primitive alphabetic type is the `char`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -428,18 +433,21 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust’s `char` represents a [Unicode Scalar Value], which means that it can represent a lot more than just ASCII.
|
Rust’s `char` represents a [Unicode Scalar Value], which means that it can
|
||||||
“Character” isn’t really a concept in Unicode, however: your human intutition for what a ‘character’ is may not match up with a `char`.
|
represent a lot more than just ASCII. “Character” isn’t really a concept in
|
||||||
It also means that `char`s are four bytes each.
|
Unicode, however: your human intutition for what a ‘character’ is may not match
|
||||||
|
up with a `char`. It also means that `char`s are four bytes each.
|
||||||
|
|
||||||
[Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value
|
[Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value
|
||||||
|
|
||||||
The single quotes are important: to define a literal single character, we use single quotes.
|
The single quotes are important: to define a literal single character, we use
|
||||||
If we used double quotes, we’d be defining a `&str`. Let’s talk about that next!
|
single quotes. If we used double quotes, we’d be defining a `&str`. Let’s talk
|
||||||
|
about that next!
|
||||||
|
|
||||||
## str
|
## str
|
||||||
|
|
||||||
We can declare literal strings with `"`s. We’ve seen them already, with `println!`:
|
We can declare literal strings with `"`s. We’ve seen them already, with
|
||||||
|
`println!`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -451,8 +459,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
String literals are immutable, and of a fixed length.
|
String literals are immutable, and of a fixed length. Rust has a second string
|
||||||
Rust has a second string type, `String`, that we’ll discuss in section 8.
|
type, `String`, that we’ll discuss in section 8.
|
||||||
|
|
||||||
`&str`s are UTF-8 encoded.
|
`&str`s are UTF-8 encoded.
|
||||||
|
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
# Variable Bindings
|
# Variable Bindings
|
||||||
|
|
||||||
The foundation of virtually every program is the ability to store and modify data.
|
The foundation of virtually every program is the ability to store and modify
|
||||||
Rust programs are no different.
|
data. Rust programs are no different. Let’s start with a short example.
|
||||||
Let’s start with a short example.
|
|
||||||
|
|
||||||
## The basics of bindings
|
## The basics of bindings
|
||||||
|
|
||||||
First, we’ll generate a new project with Cargo.
|
First, we’ll generate a new project with Cargo. Open a terminal, and navigate
|
||||||
Open a terminal, and navigate to the directory where you’d like to keep your projects.
|
to the directory where you’d like to keep your projects. From there, let’s
|
||||||
From there, let’s generate a new project:
|
generate a new project:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo new --bin bindings
|
$ cargo new --bin bindings
|
||||||
$ cd bindings
|
$ cd bindings
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates a new project, ‘bindings’, and sets up our `Cargo.toml` and `src/main.rs` files.
|
This creates a new project, ‘bindings’, and sets up our `Cargo.toml` and
|
||||||
As we saw in “Hello, World!”, Cargo will generate these files and create a little ‘hello world’ program for us:
|
`src/main.rs` files. As we saw in “Hello, World!”, Cargo will generate these
|
||||||
|
files and create a little ‘hello world’ program for us:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -43,16 +43,17 @@ $ cargo run
|
|||||||
The value of x is: 5
|
The value of x is: 5
|
||||||
```
|
```
|
||||||
|
|
||||||
If you see an error instead, double check that you have copied the program exactly as written.
|
If you see an error instead, double check that you have copied the program
|
||||||
Let’s break this down, line by line.
|
exactly as written. Let’s break this down, line by line.
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
```
|
```
|
||||||
|
|
||||||
The `main()` function is the entry point of every Rust program.
|
The `main()` function is the entry point of every Rust program. We’ll talk more
|
||||||
We’ll talk more about functions in the next section, but for now, all we need to know is that this is where our program begins.
|
about functions in the next section, but for now, all we need to know is that
|
||||||
The opening curly brace, `{`, indicates the start of the function’s body.
|
this is where our program begins. The opening curly brace, `{`, indicates the
|
||||||
|
start of the function’s body.
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let x = 5;
|
let x = 5;
|
||||||
@ -67,9 +68,9 @@ let NAME = EXPRESSION;
|
|||||||
```
|
```
|
||||||
|
|
||||||
A `let` statement first evaluates the `EXPRESSION`, and then binds the
|
A `let` statement first evaluates the `EXPRESSION`, and then binds the
|
||||||
resulting value to `NAME` so that it can be referred to later in the program. In
|
resulting value to `NAME` so that it can be referred to later in the program.
|
||||||
our simple example, the expression was already a value, 5, but we could achieve
|
In our simple example, the expression was already a value, 5, but we could
|
||||||
the same effect with:
|
achieve the same effect with:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 2 + 3;
|
let x = 2 + 3;
|
||||||
@ -79,22 +80,24 @@ In general, `let` statements work with patterns; a name is a particularly
|
|||||||
humble form of pattern. Patterns are a big part of Rust, we’ll see more complex
|
humble form of pattern. Patterns are a big part of Rust, we’ll see more complex
|
||||||
and powerful patterns as we go along.
|
and powerful patterns as we go along.
|
||||||
|
|
||||||
Before we do that, though, let’s finish investigating this example.
|
Before we do that, though, let’s finish investigating this example. Here’s the
|
||||||
Here’s the next line:
|
next line:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
println!("The value of x is: {}", x);
|
println!("The value of x is: {}", x);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `println!` macro prints text to the screen.
|
The `println!` macro prints text to the screen. We can tell that it’s a macro
|
||||||
We can tell that it’s a macro due to the `!`.
|
due to the `!`. We won’t learn how to write macros until much later in the
|
||||||
We won’t learn how to write macros until much later in the book, but we’ll use macros provided by the standard library throughout.
|
book, but we’ll use macros provided by the standard library throughout. Every
|
||||||
Every time you see a `!`, remember that it signifies a macro.
|
time you see a `!`, remember that it signifies a macro. Macros can add new
|
||||||
Macros can add new syntax to the language, and the `!` is a reminder that things may look slightly unusual.
|
syntax to the language, and the `!` is a reminder that things may look slightly
|
||||||
|
unusual.
|
||||||
|
|
||||||
`println!`, specifically, has one required argument, a ‘format string’, and zero or more optional arguments.
|
`println!`, specifically, has one required argument, a ‘format string’, and
|
||||||
The format string can contain the special text `{}`.
|
zero or more optional arguments. The format string can contain the special text
|
||||||
Each instance of `{}` corresponds to an additional argument. Here’s an example:
|
`{}`. Each instance of `{}` corresponds to an additional argument. Here’s an
|
||||||
|
example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 2 + 3;
|
let x = 2 + 3;
|
||||||
@ -102,14 +105,16 @@ let y = x + 5;
|
|||||||
println!("The value of x is {}, and the value of y is {}", x, y);
|
println!("The value of x is {}, and the value of y is {}", x, y);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can think of `{}` as little crab pincers, holding the value in place.
|
You can think of `{}` as little crab pincers, holding the value in place. This
|
||||||
This placeholder has a number of more advanced formatting options that we’ll discuss later.
|
placeholder has a number of more advanced formatting options that we’ll discuss
|
||||||
|
later.
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, a closing curly brace matches up with the opening curly brace that declared the `main()` function, and declares its end.
|
Finally, a closing curly brace matches up with the opening curly brace that
|
||||||
|
declared the `main()` function, and declares its end.
|
||||||
|
|
||||||
This explains our output:
|
This explains our output:
|
||||||
|
|
||||||
@ -117,12 +122,12 @@ This explains our output:
|
|||||||
The value of x is: 5
|
The value of x is: 5
|
||||||
```
|
```
|
||||||
|
|
||||||
We assign `5` to a binding, `x`, and then print it to the screen with `println!`.
|
We assign `5` to a binding, `x`, and then print it to the screen with
|
||||||
|
`println!`.
|
||||||
|
|
||||||
## Multiple binding
|
## Multiple binding
|
||||||
|
|
||||||
Let’s try a more complex pattern.
|
Let’s try a more complex pattern. Change our example program to this:
|
||||||
Change our example program to this:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -143,8 +148,7 @@ The value of x is: 5
|
|||||||
The value of y is: 6
|
The value of y is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
We’ve created two bindings with one `let`!
|
We’ve created two bindings with one `let`! Here’s our pattern:
|
||||||
Here’s our pattern:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
(x, y)
|
(x, y)
|
||||||
@ -156,8 +160,8 @@ And here’s the value:
|
|||||||
(5, 6)
|
(5, 6)
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, the two line up visually, and so `let` binds `5` to `x` and `6` to `y`.
|
As you can see, the two line up visually, and so `let` binds `5` to `x` and `6`
|
||||||
We could have used two `let` statements as well:
|
to `y`. We could have used two `let` statements as well:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -166,15 +170,18 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In simple cases like this, two `let`s may be clearer, but in others, creating multiple bindings at once is nice.
|
In simple cases like this, two `let`s may be clearer, but in others, creating
|
||||||
As we become more proficient in Rust, we’ll figure out which style is better, but it’s mostly a judgement call.
|
multiple bindings at once is nice. As we become more proficient in Rust, we’ll
|
||||||
|
figure out which style is better, but it’s mostly a judgement call.
|
||||||
|
|
||||||
## Type annotations
|
## Type annotations
|
||||||
|
|
||||||
You may have noticed that we didn’t declare the type of `x` or `y` in our previous examples.
|
You may have noticed that we didn’t declare the type of `x` or `y` in our
|
||||||
Rust is a *statically typed* language, which means that at compile time, we must know the types of all bindings.
|
previous examples. Rust is a *statically typed* language, which means that at
|
||||||
But annotating every single binding with a type can feel like busywork, and make code noisy.
|
compile time, we must know the types of all bindings. But annotating every
|
||||||
To solve this issue, Rust uses ‘type inference’, meaning that it attempts to infer the types of your bindings.
|
single binding with a type can feel like busywork, and make code noisy. To
|
||||||
|
solve this issue, Rust uses ‘type inference’, meaning that it attempts to infer
|
||||||
|
the types of your bindings.
|
||||||
|
|
||||||
The primary way that the type is inferred is by looking at how it is used.
|
The primary way that the type is inferred is by looking at how it is used.
|
||||||
Let’s look at the example again:
|
Let’s look at the example again:
|
||||||
@ -186,8 +193,8 @@ fn main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
When we bind `x` to `5`, the compiler knows that `x` should be a numeric type.
|
When we bind `x` to `5`, the compiler knows that `x` should be a numeric type.
|
||||||
Without any other information, it defaults to `i32`, a thirty-two bit integer type.
|
Without any other information, it defaults to `i32`, a thirty-two bit integer
|
||||||
We’ll talk more about Rust’s basic types in section 3.3.
|
type. We’ll talk more about Rust’s basic types in section 3.3.
|
||||||
|
|
||||||
Here’s what a `let` statement with a ‘type annotation’ looks like:
|
Here’s what a `let` statement with a ‘type annotation’ looks like:
|
||||||
|
|
||||||
@ -197,15 +204,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can add a colon, followed by the type name.
|
We can add a colon, followed by the type name. Here’s the structure of a `let`
|
||||||
Here’s the structure of a `let` statement with a type annotation:
|
statement with a type annotation:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
let PATTERN: TYPE = VALUE;
|
let PATTERN: TYPE = VALUE;
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the colon and the `TYPE` go _after_ the `PATTERN`, not in the pattern itself.
|
Note that the colon and the `TYPE` go _after_ the `PATTERN`, not in the pattern
|
||||||
As an example, here’s our more complex pattern with two bindings:
|
itself. As an example, here’s our more complex pattern with two bindings:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -213,11 +220,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Just like we match up the `VALUE` with the `PATTERN`, we match up the `TYPE` with the `PATTERN`.
|
Just like we match up the `VALUE` with the `PATTERN`, we match up the `TYPE`
|
||||||
|
with the `PATTERN`.
|
||||||
|
|
||||||
## Delayed Initialization
|
## Delayed Initialization
|
||||||
|
|
||||||
We do not have to provide bindings with an initial value, and can assign it later. Try this program:
|
We do not have to provide bindings with an initial value, and can assign it
|
||||||
|
later. Try this program:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -238,9 +247,9 @@ $ cargo run
|
|||||||
The value of x is: 5
|
The value of x is: 5
|
||||||
```
|
```
|
||||||
|
|
||||||
It’s all good.
|
It’s all good. This raises a question, though: what if we try to print out a
|
||||||
This raises a question, though: what if we try to print out a binding before we declare a value?
|
binding before we declare a value? Here’s a program that demonstrates this
|
||||||
Here’s a program that demonstrates this question:
|
question:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -269,14 +278,14 @@ Could not compile `bindings`.
|
|||||||
To learn more, run the command again with --verbose.
|
To learn more, run the command again with --verbose.
|
||||||
```
|
```
|
||||||
|
|
||||||
An error!
|
An error! The compiler won’t let us write a program like this. This is our
|
||||||
The compiler won’t let us write a program like this.
|
first example of the compiler helping us find an error in our program.
|
||||||
This is our first example of the compiler helping us find an error in our program.
|
Different programming languages have different ways of approaching this
|
||||||
Different programming languages have different ways of approaching this problem.
|
problem. Some languages always initialize values with some sort of default.
|
||||||
Some languages always initialize values with some sort of default.
|
Other languages leave the value uninitialized, and make no promises about what
|
||||||
Other languages leave the value uninitialized, and make no promises about what happens if you try to use something before initialization.
|
happens if you try to use something before initialization. Rust chooses
|
||||||
Rust chooses something else: error and force the programmer to explain what they want.
|
something else: error and force the programmer to explain what they want. We
|
||||||
We must do some sort of initialization before we can use `x`.
|
must do some sort of initialization before we can use `x`.
|
||||||
|
|
||||||
### Extended error explanations
|
### Extended error explanations
|
||||||
|
|
||||||
@ -287,9 +296,9 @@ src/main.rs:4:39: 4:40 help: run `rustc --explain E0381` to see a detailed expla
|
|||||||
```
|
```
|
||||||
|
|
||||||
We can see an extended explanation by passing the `--explain` flag to `rustc`.
|
We can see an extended explanation by passing the `--explain` flag to `rustc`.
|
||||||
Not every error has a longer explanation, but many of them do.
|
Not every error has a longer explanation, but many of them do. These extended
|
||||||
These extended explanations try to show off common ways that the error occurs, and common solutions to the issue.
|
explanations try to show off common ways that the error occurs, and common
|
||||||
Here’s `E0381`:
|
solutions to the issue. Here’s `E0381`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ rustc --explain E0381
|
$ rustc --explain E0381
|
||||||
@ -303,12 +312,13 @@ To fix this, ensure that any declared variables are initialized before being
|
|||||||
used.
|
used.
|
||||||
```
|
```
|
||||||
|
|
||||||
These explanations can really help if you’re stuck on an error.
|
These explanations can really help if you’re stuck on an error. The compiler is
|
||||||
The compiler is your friend, and is here to help.
|
your friend, and is here to help.
|
||||||
|
|
||||||
## Mutable bindings
|
## Mutable bindings
|
||||||
|
|
||||||
What about changing the value of a binding? Here’s another sample program that asks this question:
|
What about changing the value of a binding? Here’s another sample program that
|
||||||
|
asks this question:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -334,11 +344,10 @@ src/main.rs:2 let x = 5;
|
|||||||
^
|
^
|
||||||
```
|
```
|
||||||
|
|
||||||
The error mentions `re-assigment of immutable variable`.
|
The error mentions `re-assigment of immutable variable`. That’s right: bindings
|
||||||
That’s right: bindings are immutable.
|
are immutable. But they’re only immutable by default. In a pattern, when we’re
|
||||||
But they’re only immutable by default.
|
creating a new name, we can add `mut` in front to make the binding a mutable
|
||||||
In a pattern, when we’re creating a new name, we can add `mut` in front to make the binding a mutable one.
|
one. Here’s an example:
|
||||||
Here’s an example:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -362,9 +371,9 @@ The value of x is: 5
|
|||||||
The value of x is: 6
|
The value of x is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
We can now change the value that `x` binds to.
|
We can now change the value that `x` binds to. Note that the syntax is not `let
|
||||||
Note that the syntax is not `let mut` exactly; it’s using `mut` in a pattern.
|
mut` exactly; it’s using `mut` in a pattern. This becomes more obvious with our
|
||||||
This becomes more obvious with our `()` pattern:
|
`()` pattern:
|
||||||
|
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
@ -390,13 +399,13 @@ src/main.rs:2 let (mut x, y) = (5, 6);
|
|||||||
^
|
^
|
||||||
```
|
```
|
||||||
|
|
||||||
It’s fine with re-assigning `x`, but not `y`.
|
It’s fine with re-assigning `x`, but not `y`. The `mut` only applies to the
|
||||||
The `mut` only applies to the name that follows it, not the whole pattern.
|
name that follows it, not the whole pattern.
|
||||||
|
|
||||||
### Reassignment, not mutation
|
### Reassignment, not mutation
|
||||||
|
|
||||||
There is one subtlety we haven’t covered yet: `mut` allows you to mutate _the binding_, but not _what the binding binds to_.
|
There is one subtlety we haven’t covered yet: `mut` allows you to mutate _the
|
||||||
In other words:
|
binding_, but not _what the binding binds to_. In other words:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -406,18 +415,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is not changing the value that `x` is bound to, but creating a new value, `6`, and changing the binding to bind to it instead.
|
This is not changing the value that `x` is bound to, but creating a new value,
|
||||||
It’s a subtle but important difference.
|
`6`, and changing the binding to bind to it instead. It’s a subtle but
|
||||||
Well, for now, it does not make a lot of difference, but when our programs get more complex, it will.
|
important difference. Well, for now, it does not make a lot of difference, but
|
||||||
Specifically, passing arguments to functions will illustrate the difference.
|
when our programs get more complex, it will. Specifically, passing arguments to
|
||||||
We’ll talk about that in the next section, when we discuss functions.
|
functions will illustrate the difference. We’ll talk about that in the next
|
||||||
|
section, when we discuss functions.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
Variable bindings have a ‘scope’ in which they’re valid.
|
Variable bindings have a ‘scope’ in which they’re valid. That scope begins from
|
||||||
That scope begins from the point at which the binding is declared, and ends at the end of the next block of code.
|
the point at which the binding is declared, and ends at the end of the next
|
||||||
We can only access bindings which are ‘in scope’.
|
block of code. We can only access bindings which are ‘in scope’. We cannot
|
||||||
We cannot access them ‘before they come into scope’ or ‘after they go out of scope’.
|
access them ‘before they come into scope’ or ‘after they go out of scope’.
|
||||||
Here’s an example:
|
Here’s an example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -459,12 +469,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
What bindings are in and out of scope will become much more important later, once we learn about ‘references’ and ‘traits’.
|
What bindings are in and out of scope will become much more important later,
|
||||||
|
once we learn about ‘references’ and ‘traits’.
|
||||||
|
|
||||||
## Shadowing
|
## Shadowing
|
||||||
|
|
||||||
A final thing about bindings: they can ‘shadow’ previous bindings with the same name.
|
A final thing about bindings: they can ‘shadow’ previous bindings with the same
|
||||||
Here’s a sample program:
|
name. Here’s a sample program:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -485,14 +496,13 @@ src/main.rs:2 let x = 5;
|
|||||||
The value of x is: 6
|
The value of x is: 6
|
||||||
```
|
```
|
||||||
|
|
||||||
There are two interesting things in this output.
|
There are two interesting things in this output. First, Rust will compile and
|
||||||
First, Rust will compile and run this program, no problem.
|
run this program, no problem. And as we can see, the value of `x` is `6`. But
|
||||||
And as we can see, the value of `x` is `6`.
|
we didn’t declare `x` as mutable. Instead, we declared a _new_ binding that is
|
||||||
But we didn’t declare `x` as mutable.
|
_also_ named `x`, and gave it a new value. The older value that we bound `x` to
|
||||||
Instead, we declared a _new_ binding that is _also_ named `x`, and gave it a new value.
|
is inaccessible as soon as the new `x` is declared. This can be useful if you’d
|
||||||
The older value that we bound `x` to is inaccessible as soon as the new `x` is declared.
|
like to perform a few transformations on a value, and leave it immutable. For
|
||||||
This can be useful if you’d like to perform a few transformations on a value, and leave it immutable.
|
example:
|
||||||
For example:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -512,10 +522,10 @@ This will print:
|
|||||||
The value of x is: 12
|
The value of x is: 12
|
||||||
```
|
```
|
||||||
|
|
||||||
This lets us modify `x`, but not deal with mutation.
|
This lets us modify `x`, but not deal with mutation. This is nice because we
|
||||||
This is nice because we know that the compiler will let us know if we try to modify it later.
|
know that the compiler will let us know if we try to modify it later. Let’s
|
||||||
Let’s assume that after we calculate `12`, we don’t want to modify `x` again.
|
assume that after we calculate `12`, we don’t want to modify `x` again. If we
|
||||||
If we had written this program in a mutable style, like this:
|
had written this program in a mutable style, like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -531,8 +541,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust is happy to let us mutate it again, to `15`.
|
Rust is happy to let us mutate it again, to `15`. A similar program in our
|
||||||
A similar program in our immutable style will let us know about that accidental mutation, however:
|
immutable style will let us know about that accidental mutation, however:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -566,10 +576,11 @@ Could not compile `bindings`.
|
|||||||
|
|
||||||
Exactly what we wanted.
|
Exactly what we wanted.
|
||||||
|
|
||||||
Shadowing can take some time to get used to, but it’s very powerful, and works well with immutability.
|
Shadowing can take some time to get used to, but it’s very powerful, and works
|
||||||
|
well with immutability.
|
||||||
|
|
||||||
There was one more thing we should talk about in the output from compiling our initial program.
|
There was one more thing we should talk about in the output from compiling our
|
||||||
It’s this part:
|
initial program. It’s this part:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variables)] on by default
|
src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variables)] on by default
|
||||||
@ -583,20 +594,21 @@ let x = 6;
|
|||||||
```
|
```
|
||||||
|
|
||||||
Rust knows that we shadowed `x`, but we never ended up using the initial value.
|
Rust knows that we shadowed `x`, but we never ended up using the initial value.
|
||||||
This isn’t _wrong_, exactly, it just may not have been what we wanted.
|
This isn’t _wrong_, exactly, it just may not have been what we wanted. In this
|
||||||
In this case, the compiler issues a ‘warning’, but still compiles our program.
|
case, the compiler issues a ‘warning’, but still compiles our program. The
|
||||||
The `#[warn(unused_variables)]` syntax is called an ‘attribute’, which we’ll discuss in a later section.
|
`#[warn(unused_variables)]` syntax is called an ‘attribute’, which we’ll
|
||||||
More specifically, a warning like this is called a ‘lint’, which is an old term for the bits of sheep’s wool that you wouldn’t want to put in cloth.
|
discuss in a later section. More specifically, a warning like this is called a
|
||||||
Similarly, this lint is telling us that we may have an extra bit of code we don’t need.
|
‘lint’, which is an old term for the bits of sheep’s wool that you wouldn’t
|
||||||
Our program would work just fine without it.
|
want to put in cloth. Similarly, this lint is telling us that we may have an
|
||||||
|
extra bit of code we don’t need. Our program would work just fine without it.
|
||||||
It’s worth listening to these warnings, and fixing the problems they point out.
|
It’s worth listening to these warnings, and fixing the problems they point out.
|
||||||
They can be signs of a larger problem.
|
They can be signs of a larger problem. In this case, we may not have realized
|
||||||
In this case, we may not have realized that we were shadowing `x`.
|
that we were shadowing `x`.
|
||||||
|
|
||||||
### Shadowing and scopes
|
### Shadowing and scopes
|
||||||
|
|
||||||
Like any binding, a binding that shadows another binding will go away at the end of a scope.
|
Like any binding, a binding that shadows another binding will go away at the
|
||||||
Here’s an example program:
|
end of a scope. Here’s an example program:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user