undo the 'sentence per line' experiment

I tried it, I didn't like it.
This commit is contained in:
Steve Klabnik 2015-12-28 10:51:03 -05:00
parent 795403668e
commit 7afd6feada
4 changed files with 333 additions and 308 deletions

View File

@ -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.
Heres a comment:
@ -10,8 +10,8 @@ Heres 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 were doing something complicated here, long enough that we need
@ -40,9 +40,10 @@ Thats all there is to it. Comments are not particularly complicated.
## Documentation comments
However, Rust has another kind of comment: a documentation comment.
These comments dont affect the way that the code works, but they do work with Rusts 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 dont affect the way that the code works, but they do work with Rusts
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.

View File

@ -1,6 +1,7 @@
# Functions
Functions are pervasive in Rust code. Weve already seen the most important function, `main()`, in previous sections of the book:
Functions are pervasive in Rust code. Weve 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 weve 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 weve defined by using its name and some parentheses:
```rust
fn main() {
@ -32,9 +33,9 @@ fn another_function() {
}
```
Lets start a new project to explore functions.
Open a terminal, and navigate to the directory where youd like to keep your projects.
From there, use Cargo to generate a new project:
Lets start a new project to explore functions. Open a terminal, and navigate
to the directory where youd 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) {
```
Thats right, patterns appear again.
Consider how the parameter declaration here looks like the `let` bindings we used earlier:
Thats 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) {
```
Theres 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.
Theres 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 dont 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 dont 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 dont name return values, but we do declare their type, after an arrow: `->`.
Heres a sample program:
We dont name return values, but we do declare their type, after an arrow:
`->`. Heres a sample program:
```rust
fn main() {
@ -191,9 +195,8 @@ $ cargo run
The value of x is: 5
```
Lets examine this in more detail.
There are two important bits.
First, we can use the return value of a function to initialize a binding:
Lets 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`.
Theres a detail here that you may or may not have noticed: weve ended almost every line in our programs with a semicolon.
Theres 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`. Theres a detail here that you may or may not have
noticed: weve ended almost every line in our programs with a semicolon.
Theres no semicolon here, though. Why not?
The answer to this question is:
@ -228,16 +230,15 @@ Lets 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.
Its a subtle difference.
Weve 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. Its a subtle difference. Weve already seen two kinds of
statements: `let` statements, and `fn` declarations.
Because `let` is a statement, not an expression, you cant assign it to another binding.
Heres an example that doesnt work:
Because `let` is a statement, not an expression, you cant assign it to another
binding. Heres an example that doesnt 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 whats this have to do with return values?
Well, `{}`, a block that we used earlier to create new scopes, _is_ an expression.
Lets take a closer look at `{}`.
It looks like this:
So whats this have to do with return values? Well, `{}`, a block that we
used earlier to create new scopes, _is_ an expression. Lets 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!
Theyre very similar:
Where else do we use blocks? As the body of functions! Theyre 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, its assigning with `let`:
In both cases, we use a block to produce a value. In the first case, its
assigning with `let`:
```rust,ignore
let y = {
@ -391,8 +390,9 @@ fn plus_one(x: i32) -> i32 {
### Expression statements
Theres one more detail about expressions and statements: a semicolon takes any expression, and turns it into a statement.
Lets accidentally cause an error with `plus_one()`:
Theres one more detail about expressions and statements: a semicolon takes any
expression, and turns it into a statement. Lets 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`, weve now turned it into a statement, `x + 1;`.
Instead of an expression, `x + 1`, weve 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 dont 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
dont evaluate to a value, yet we want to return an `i32`.
In practice, Rust programmers dont 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.
Theres a trick, however. Remember the `()`s we used when showing off complex bindings?
Functions cannot directly return multiple values. Theres 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 Rusts 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 Rusts 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.
Lets 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.

View File

@ -1,11 +1,12 @@
# Primitive Types
Weve 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 cant re-create them yourself.
There are, of course, many non-primitive types provided by the standard library as well.
Weve 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 cant 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
Youve already seen one primitive type: `i32`.
There are a number of built-in number types in Rust.
Youve already seen one primitive type: `i32`. There are a number of built-in
number types in Rust.
Heres a chart of Rusts integer types:
@ -28,24 +29,24 @@ Heres a chart of Rusts 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: thats a signed number.)
Signed numbers are stored using twos 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: thats a signed
number.) Signed numbers are stored using twos 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 youre on a 32-bit one, theyre 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 youre on a 32-bit one, theyre 32 bits.
So how do you choose from all these options? Well, if you really dont 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.
Well talk more about our first collection, arrays, in just a moment.
So how do you choose from all these options? Well, if you really dont 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. Well 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 weve seen previously is the tuple type.
Tuples have an arity, or size.
We might say “thats a 3-tuple” or “thats a 5-tuple.”
The other type weve seen previously is the tuple type. Tuples have an arity,
or size. We might say “thats a 3-tuple” or “thats 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
Theres 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:
Theres 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
Theres one more type that weve been using, but you havent seen written explicitly.
Functions!
Functions also have a type, and yes, you can even have variables which hold functions!
Heres an example:
Theres one more type that weve been using, but you havent seen written
explicitly. Functions! Functions also have a type, and yes, you can even have
variables which hold functions! Heres 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, lets put them side by side:
As you can see, the type is very similar to the declaration. Here, lets put
them side by side:
```rust,ignore
fn(i32) -> i32 // type
@ -148,7 +148,8 @@ fn(i32) -> i32 {
Its the same! Well, we need to drop that `{` as well.
Finally, if youll notice in that example, we can create a binding with a function in it:
Finally, if youll 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 @@ Lets 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 @@ Thats really all there is to say about that!
## Arrays
So far, weve only represented single values in a binding.
Sometimes, though, its useful to have more than one value.
These kinds of data structures are called collections, and arrays are the ones well learn about first.
Arrays look like this:
So far, weve only represented single values in a binding. Sometimes, though,
its useful to have more than one value. These kinds of data structures are
called collections, and arrays are the ones well learn about first. Arrays
look like this:
```rust
fn main() {
@ -253,7 +255,8 @@ fn main() {
}
```
An arrays type consists of the type of the elements it contains, as well as the length:
An arrays 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.
Heres 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. Heres 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 didnt want a copy, but instead wanted to refer to the first element, whatever its value was, we need a new concept.
Well talk about references in Section 4.
Running this example will show that `first` is still `1`. If we didnt want a
copy, but instead wanted to refer to the first element, whatever its value was,
we need a new concept. Well 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 theres a few other concepts that we havent 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 theres a few other concepts
that we havent 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.
Lets 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. Lets 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 didnt exit successfully: `target/debug/functions` (exit code: 101)
```
It says that our thread panicked, and that our program didnt exit successfully.
Theres also a reason: we had a length of five, but an index of 10.
It says that our thread panicked, and that our program didnt exit
successfully. Theres 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.
Theyre 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. Theyre 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 its greater, it will panic, as something is very wrong.
This is our first example of Rusts 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 its greater, it will panic, as something is very wrong. This is
our first example of Rusts 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.
**Steves note: this next bit might be our first advanced section, on get()?**
### Debug
So far, weve been printing values using `{}`.
If we try that with an array, though...
So far, weve 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 havent 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 havent discussed traits yet, so this is bound to be confusing!
Heres 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 weve seen so far implement `Display`, as theres only one way youd 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 weve seen so far implement `Display`, as
theres only one way youd 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]
```
Youll see this repeated later, with other types.
And well cover traits fully later in the book, Section 9.
Youll see this repeated later, with other types. And well cover traits fully
later in the book, Section 9.
## char
Weve only worked with numbers so far, but what about letters?
Rusts most primitive alphabetic type is the `char`:
Weve only worked with numbers so far, but what about letters? Rusts most
primitive alphabetic type is the `char`:
```rust
fn main() {
@ -428,18 +433,21 @@ fn main() {
}
```
Rusts `char` represents a [Unicode Scalar Value], which means that it can represent a lot more than just ASCII.
“Character” isnt 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.
Rusts `char` represents a [Unicode Scalar Value], which means that it can
represent a lot more than just ASCII. “Character” isnt 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, wed be defining a `&str`. Lets talk about that next!
The single quotes are important: to define a literal single character, we use
single quotes. If we used double quotes, wed be defining a `&str`. Lets talk
about that next!
## str
We can declare literal strings with `"`s. Weve seen them already, with `println!`:
We can declare literal strings with `"`s. Weve 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 well discuss in section 8.
String literals are immutable, and of a fixed length. Rust has a second string
type, `String`, that well discuss in section 8.
`&str`s are UTF-8 encoded.

View File

@ -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.
Lets start with a short example.
The foundation of virtually every program is the ability to store and modify
data. Rust programs are no different. Lets start with a short example.
## The basics of bindings
First, well generate a new project with Cargo.
Open a terminal, and navigate to the directory where youd like to keep your projects.
From there, lets generate a new project:
First, well generate a new project with Cargo. Open a terminal, and navigate
to the directory where youd like to keep your projects. From there, lets
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.
Lets break this down, line by line.
If you see an error instead, double check that you have copied the program
exactly as written. Lets break this down, line by line.
```rust,ignore
fn main() {
```
The `main()` function is the entry point of every Rust program.
Well 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 functions body.
The `main()` function is the entry point of every Rust program. Well 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 functions 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, well see more complex
and powerful patterns as we go along.
Before we do that, though, lets finish investigating this example.
Heres the next line:
Before we do that, though, lets finish investigating this example. Heres the
next line:
```rust,ignore
println!("The value of x is: {}", x);
```
The `println!` macro prints text to the screen.
We can tell that its a macro due to the `!`.
We wont learn how to write macros until much later in the book, but well 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 its a macro
due to the `!`. We wont learn how to write macros until much later in the
book, but well 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. Heres 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. Heres 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 well 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 well 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
Lets try a more complex pattern.
Change our example program to this:
Lets 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
```
Weve created two bindings with one `let`!
Heres our pattern:
Weve created two bindings with one `let`! Heres our pattern:
```text
(x, y)
@ -156,8 +160,8 @@ And heres 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, well figure out which style is better, but its 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, well
figure out which style is better, but its mostly a judgement call.
## Type annotations
You may have noticed that we didnt 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 didnt 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.
Lets 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.
Well talk more about Rusts basic types in section 3.3.
Without any other information, it defaults to `i32`, a thirty-two bit integer
type. Well talk more about Rusts basic types in section 3.3.
Heres 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.
Heres the structure of a `let` statement with a type annotation:
We can add a colon, followed by the type name. Heres 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, heres 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, heres 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
```
Its all good.
This raises a question, though: what if we try to print out a binding before we declare a value?
Heres a program that demonstrates this question:
Its all good. This raises a question, though: what if we try to print out a
binding before we declare a value? Heres 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 wont 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 wont 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.
Heres `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. Heres `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 youre stuck on an error.
The compiler is your friend, and is here to help.
These explanations can really help if youre stuck on an error. The compiler is
your friend, and is here to help.
## Mutable bindings
What about changing the value of a binding? Heres another sample program that asks this question:
What about changing the value of a binding? Heres 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`.
Thats right: bindings are immutable.
But theyre only immutable by default.
In a pattern, when were creating a new name, we can add `mut` in front to make the binding a mutable one.
Heres an example:
The error mentions `re-assigment of immutable variable`. Thats right: bindings
are immutable. But theyre only immutable by default. In a pattern, when were
creating a new name, we can add `mut` in front to make the binding a mutable
one. Heres 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; its 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; its 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);
^
```
Its fine with re-assigning `x`, but not `y`.
The `mut` only applies to the name that follows it, not the whole pattern.
Its 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 havent covered yet: `mut` allows you to mutate _the binding_, but not _what the binding binds to_.
In other words:
There is one subtlety we havent 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.
Its 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.
Well 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. Its 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. Well talk about that in the next
section, when we discuss functions.
## Scope
Variable bindings have a scope in which theyre 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 theyre 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.
Heres 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.
Heres a sample program:
A final thing about bindings: they can shadow previous bindings with the same
name. Heres 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 didnt 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 youd 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 didnt 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 youd
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.
Lets assume that after we calculate `12`, we dont 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. Lets
assume that after we calculate `12`, we dont 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 its very powerful, and works well with immutability.
Shadowing can take some time to get used to, but its very powerful, and works
well with immutability.
There was one more thing we should talk about in the output from compiling our initial program.
Its this part:
There was one more thing we should talk about in the output from compiling our
initial program. Its 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 isnt _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 well discuss in a later section.
More specifically, a warning like this is called a lint, which is an old term for the bits of sheeps wool that you wouldnt want to put in cloth.
Similarly, this lint is telling us that we may have an extra bit of code we dont need.
Our program would work just fine without it.
This isnt _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 well
discuss in a later section. More specifically, a warning like this is called a
lint, which is an old term for the bits of sheeps wool that you wouldnt
want to put in cloth. Similarly, this lint is telling us that we may have an
extra bit of code we dont need. Our program would work just fine without it.
Its 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.
Heres an example program:
Like any binding, a binding that shadows another binding will go away at the
end of a scope. Heres an example program:
```rust
fn main() {