rollup merge of #24273: steveklabnik/even_moar_editing
Three more sections
This commit is contained in:
commit
330466ed96
@ -1,47 +1,45 @@
|
||||
% Comments
|
||||
|
||||
Now that we have some functions, it's a good idea to learn about comments.
|
||||
Now that we have some functions, it’s a good idea to learn about comments.
|
||||
Comments are notes that you leave to other programmers to help explain things
|
||||
about your code. The compiler mostly ignores them.
|
||||
|
||||
Rust has two kinds of comments that you should care about: *line comments*
|
||||
and *doc comments*.
|
||||
|
||||
```{rust}
|
||||
// Line comments are anything after '//' and extend to the end of the line.
|
||||
```rust
|
||||
// Line comments are anything after ‘//’ and extend to the end of the line.
|
||||
|
||||
let x = 5; // this is also a line comment.
|
||||
|
||||
// If you have a long explanation for something, you can put line comments next
|
||||
// to each other. Put a space between the // and your comment so that it's
|
||||
// to each other. Put a space between the // and your comment so that it’s
|
||||
// more readable.
|
||||
```
|
||||
|
||||
The other kind of comment is a doc comment. Doc comments use `///` instead of
|
||||
`//`, and support Markdown notation inside:
|
||||
|
||||
```{rust}
|
||||
/// `hello` is a function that prints a greeting that is personalized based on
|
||||
/// the name given.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the person you'd like to greet.
|
||||
```rust
|
||||
/// Adds one to the number given.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let name = "Steve";
|
||||
/// hello(name); // prints "Hello, Steve!"
|
||||
/// ```
|
||||
fn hello(name: &str) {
|
||||
println!("Hello, {}!", name);
|
||||
/// let five = 5;
|
||||
///
|
||||
/// assert_eq!(6, add_one(5));
|
||||
/// ```
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
When writing doc comments, adding sections for any arguments, return values,
|
||||
and providing some examples of usage is very, very helpful. Don't worry about
|
||||
the `&str`, we'll get to it soon.
|
||||
When writing doc comments, providing some examples of usage is very, very
|
||||
helpful. You’ll notice we’ve used a new macro here: `assert_eq!`. This compares
|
||||
two values, and `panic!`s if they’re not equal to each other. It’s very helpful
|
||||
in documentation. There’s another macro, `assert!`, which `panic!`s if the
|
||||
value passed to it is `false`.
|
||||
|
||||
You can use the [`rustdoc`](documentation.html) tool to generate HTML documentation
|
||||
from these doc comments.
|
||||
from these doc comments, and also to run the code examples as tests!
|
||||
|
@ -1,6 +1,6 @@
|
||||
% Functions
|
||||
|
||||
You've already seen one function so far, the `main` function:
|
||||
Every Rust program has at least one function, the `main` function:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -8,16 +8,16 @@ fn main() {
|
||||
```
|
||||
|
||||
This is the simplest possible function declaration. As we mentioned before,
|
||||
`fn` says "this is a function," followed by the name, some parentheses because
|
||||
`fn` says ‘this is a function’, followed by the name, some parentheses because
|
||||
this function takes no arguments, and then some curly braces to indicate the
|
||||
body. Here's a function named `foo`:
|
||||
body. Here’s a function named `foo`:
|
||||
|
||||
```rust
|
||||
fn foo() {
|
||||
}
|
||||
```
|
||||
|
||||
So, what about taking arguments? Here's a function that prints a number:
|
||||
So, what about taking arguments? Here’s a function that prints a number:
|
||||
|
||||
```rust
|
||||
fn print_number(x: i32) {
|
||||
@ -25,7 +25,7 @@ fn print_number(x: i32) {
|
||||
}
|
||||
```
|
||||
|
||||
Here's a complete program that uses `print_number`:
|
||||
Here’s a complete program that uses `print_number`:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -40,7 +40,7 @@ fn print_number(x: i32) {
|
||||
As you can see, function arguments work very similar to `let` declarations:
|
||||
you add a type to the argument name, after a colon.
|
||||
|
||||
Here's a complete program that adds two numbers together and prints them:
|
||||
Here’s a complete program that adds two numbers together and prints them:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -58,7 +58,7 @@ as when you declare it.
|
||||
Unlike `let`, you _must_ declare the types of function arguments. This does
|
||||
not work:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
fn print_sum(x, y) {
|
||||
println!("sum is: {}", x + y);
|
||||
}
|
||||
@ -67,8 +67,8 @@ fn print_sum(x, y) {
|
||||
You get this error:
|
||||
|
||||
```text
|
||||
hello.rs:5:18: 5:19 expected one of `!`, `:`, or `@`, found `)`
|
||||
hello.rs:5 fn print_number(x, y) {
|
||||
expected one of `!`, `:`, or `@`, found `)`
|
||||
fn print_number(x, y) {
|
||||
```
|
||||
|
||||
This is a deliberate design decision. While full-program inference is possible,
|
||||
@ -77,7 +77,7 @@ types explicitly is a best-practice. We agree that forcing functions to declare
|
||||
types while allowing for inference inside of function bodies is a wonderful
|
||||
sweet spot between full inference and no inference.
|
||||
|
||||
What about returning a value? Here's a function that adds one to an integer:
|
||||
What about returning a value? Here’s a function that adds one to an integer:
|
||||
|
||||
```rust
|
||||
fn add_one(x: i32) -> i32 {
|
||||
@ -86,11 +86,11 @@ fn add_one(x: i32) -> i32 {
|
||||
```
|
||||
|
||||
Rust functions return exactly one value, and you declare the type after an
|
||||
"arrow," which is a dash (`-`) followed by a greater-than sign (`>`).
|
||||
‘arrow’, which is a dash (`-`) followed by a greater-than sign (`>`). The last
|
||||
line of a function determines what it returns. You’ll note the lack of a
|
||||
semicolon here. If we added it in:
|
||||
|
||||
You'll note the lack of a semicolon here. If we added it in:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1;
|
||||
}
|
||||
@ -109,24 +109,79 @@ help: consider removing this semicolon:
|
||||
^
|
||||
```
|
||||
|
||||
Remember our earlier discussions about semicolons and `()`? Our function claims
|
||||
to return an `i32`, but with a semicolon, it would return `()` instead. Rust
|
||||
realizes this probably isn't what we want, and suggests removing the semicolon.
|
||||
This reveals two interesting things about Rust: it is an expression-based
|
||||
language, and semicolons are different from semicolons in other ‘curly brace
|
||||
and semicolon’-based languages. These two things are related.
|
||||
|
||||
This is very much like our `if` statement before: the result of the block
|
||||
(`{}`) is the value of the expression. Other expression-oriented languages,
|
||||
such as Ruby, work like this, but it's a bit unusual in the systems programming
|
||||
world. When people first learn about this, they usually assume that it
|
||||
introduces bugs. But because Rust's type system is so strong, and because unit
|
||||
is its own unique type, we have never seen an issue where adding or removing a
|
||||
semicolon in a return position would cause a bug.
|
||||
## Expressions vs. Statements
|
||||
|
||||
Rust is primarily an expression-based language. There are only two kinds of
|
||||
statements, and everything else is an expression.
|
||||
|
||||
So what's the difference? Expressions return a value, and statements do not.
|
||||
That’s why we end up with ‘not all control paths return a value’ here: the
|
||||
statement `x + 1;` doesn’t return a value. There are two kinds of statements in
|
||||
Rust: ‘declaration statements’ and ‘expression statements’. Everything else is
|
||||
an expression. Let’s talk about declaration statements first.
|
||||
|
||||
In some languages, variable bindings can be written as expressions, not just
|
||||
statements. Like Ruby:
|
||||
|
||||
```ruby
|
||||
x = y = 5
|
||||
```
|
||||
|
||||
In Rust, however, using `let` to introduce a binding is _not_ an expression. The
|
||||
following will produce a compile-time error:
|
||||
|
||||
```ignore
|
||||
let x = (let y = 5); // expected identifier, found keyword `let`
|
||||
```
|
||||
|
||||
The compiler is telling us here that it was expecting to see the beginning of
|
||||
an expression, and a `let` can only begin a statement, not an expression.
|
||||
|
||||
Note that assigning to an already-bound variable (e.g. `y = 5`) is still an
|
||||
expression, although its value is not particularly useful. Unlike other
|
||||
languages where an assignment evaluates to the assigned value (e.g. `5` in the
|
||||
previous example), in Rust the value of an assignment is an empty tuple `()`:
|
||||
|
||||
```
|
||||
let mut y = 5;
|
||||
|
||||
let x = (y = 6); // x has the value `()`, not `6`
|
||||
```
|
||||
|
||||
The second kind of statement in Rust is the *expression statement*. Its
|
||||
purpose is to turn any expression into a statement. In practical terms, Rust's
|
||||
grammar expects statements to follow other statements. This means that you use
|
||||
semicolons to separate expressions from each other. This means that Rust
|
||||
looks a lot like most other languages that require you to use semicolons
|
||||
at the end of every line, and you will see semicolons at the end of almost
|
||||
every line of Rust code you see.
|
||||
|
||||
What is this exception that makes us say "almost"? You saw it already, in this
|
||||
code:
|
||||
|
||||
```rust
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
Our function claims to return an `i32`, but with a semicolon, it would return
|
||||
`()` instead. Rust realizes this probably isn’t what we want, and suggests
|
||||
removing the semicolon in the error we saw before.
|
||||
|
||||
## Early returns
|
||||
|
||||
But what about early returns? Rust does have a keyword for that, `return`:
|
||||
|
||||
```rust
|
||||
fn foo(x: i32) -> i32 {
|
||||
if x < 5 { return x; }
|
||||
return x;
|
||||
|
||||
// we never run this code!
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
@ -136,33 +191,17 @@ style:
|
||||
|
||||
```rust
|
||||
fn foo(x: i32) -> i32 {
|
||||
if x < 5 { return x; }
|
||||
|
||||
return x + 1;
|
||||
}
|
||||
```
|
||||
|
||||
The previous definition without `return` may look a bit strange if you haven't
|
||||
The previous definition without `return` may look a bit strange if you haven’t
|
||||
worked in an expression-based language before, but it becomes intuitive over
|
||||
time. If this were production code, we wouldn't write it in that way anyway,
|
||||
we'd write this:
|
||||
|
||||
```rust
|
||||
fn foo(x: i32) -> i32 {
|
||||
if x < 5 {
|
||||
x
|
||||
} else {
|
||||
x + 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because `if` is an expression, and it's the only expression in this function,
|
||||
the value will be the result of the `if`.
|
||||
time.
|
||||
|
||||
## Diverging functions
|
||||
|
||||
Rust has some special syntax for 'diverging functions', which are functions that
|
||||
Rust has some special syntax for ‘diverging functions’, which are functions that
|
||||
do not return:
|
||||
|
||||
```
|
||||
@ -171,23 +210,18 @@ fn diverges() -> ! {
|
||||
}
|
||||
```
|
||||
|
||||
`panic!` is a macro, similar to `println!()` that we've already seen. Unlike
|
||||
`panic!` is a macro, similar to `println!()` that we’ve already seen. Unlike
|
||||
`println!()`, `panic!()` causes the current thread of execution to crash with
|
||||
the given message.
|
||||
|
||||
Because this function will cause a crash, it will never return, and so it has
|
||||
the type '`!`', which is read "diverges." A diverging function can be used
|
||||
the type ‘`!`’, which is read ‘diverges’. A diverging function can be used
|
||||
as any type:
|
||||
|
||||
```should_panic
|
||||
# fn diverges() -> ! {
|
||||
# panic!("This function never returns!");
|
||||
# }
|
||||
|
||||
let x: i32 = diverges();
|
||||
let x: String = diverges();
|
||||
```
|
||||
|
||||
We don't have a good use for diverging functions yet, because they're used in
|
||||
conjunction with other Rust features. But when you see `-> !` later, you'll
|
||||
know what it's called.
|
||||
|
@ -1,8 +1,15 @@
|
||||
% Structs
|
||||
|
||||
A struct is another form of a *record type*, just like a tuple. There's a
|
||||
difference: structs give each element that they contain a name, called a
|
||||
*field* or a *member*. Check it out:
|
||||
Structs are a way of creating more complex datatypes. For example, if we were
|
||||
doing calculations involving coordinates in 2D space, we would need both an `x`
|
||||
and a `y` value:
|
||||
|
||||
```rust
|
||||
let origin_x = 0;
|
||||
let origin_y = 0;
|
||||
```
|
||||
|
||||
A struct lets us combine these two into a single, unified datatype:
|
||||
|
||||
```rust
|
||||
struct Point {
|
||||
@ -17,7 +24,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
There's a lot going on here, so let's break it down. We declare a struct with
|
||||
There’s a lot going on here, so let’s break it down. We declare a struct with
|
||||
the `struct` keyword, and then with a name. By convention, structs begin with a
|
||||
capital letter and are also camel cased: `PointInSpace`, not `Point_In_Space`.
|
||||
|
||||
@ -31,7 +38,7 @@ notation: `origin.x`.
|
||||
The values in structs are immutable by default, like other bindings in Rust.
|
||||
Use `mut` to make them mutable:
|
||||
|
||||
```{rust}
|
||||
```rust
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
@ -47,3 +54,36 @@ fn main() {
|
||||
```
|
||||
|
||||
This will print `The point is at (5, 0)`.
|
||||
|
||||
Rust does not support field mutability at the language level, so you cannot
|
||||
write something like this:
|
||||
|
||||
```rust,ignore
|
||||
struct Point {
|
||||
mut x: i32,
|
||||
y: i32,
|
||||
}
|
||||
```
|
||||
|
||||
Mutability is a property of the binding, not of the structure itself. If you’re
|
||||
used to field-level mutability, this may seem strange at first, but it
|
||||
significantly simplifies things. It even lets you make things mutable for a short
|
||||
time only:
|
||||
|
||||
|
||||
```rust,ignore
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut point = Point { x: 0, y: 0 };
|
||||
|
||||
point.x = 5;
|
||||
|
||||
let point = point; // this new binding can’t change now
|
||||
|
||||
point.y = 6; // this causes an error
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user