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