mirror of https://github.com/rust-lang/rust.git
"The Rust Programming Language"
This pulls all of our long-form documentation into a single document, nicknamed "the book" and formally titled "The Rust Programming Language." A few things motivated this change: * People knew of The Guide, but not the individual Guides. This merges them together, helping discoverability. * You can get all of Rust's longform documentation in one place, which is nice. * We now have rustbook in-tree, which can generate this kind of documentation. While its style is basic, the general idea is much better: a table of contents on the left-hand side. * Rather than a almost 10,000-line guide.md, there are now smaller files per section.
This commit is contained in:
parent
2f99a41fe1
commit
16a6ebd1f6
|
@ -6,14 +6,14 @@ documentation.
|
|||
## Quick Start
|
||||
|
||||
1. Download a [binary installer][installer] for your platform.
|
||||
2. Read the [guide].
|
||||
2. Read [The Rust Programming Language][trpl].
|
||||
3. Enjoy!
|
||||
|
||||
> ***Note:*** Windows users can read the detailed
|
||||
> [using Rust on Windows][win-wiki] notes on the wiki.
|
||||
|
||||
[installer]: http://www.rust-lang.org/install.html
|
||||
[guide]: http://doc.rust-lang.org/guide.html
|
||||
[trpl]: http://doc.rust-lang.org/book/index.html
|
||||
[win-wiki]: https://github.com/rust-lang/rust/wiki/Using-Rust-on-Windows
|
||||
|
||||
## Building from Source
|
||||
|
@ -53,7 +53,7 @@ documentation.
|
|||
When complete, `make install` will place several programs into
|
||||
`/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
|
||||
API-documentation tool.
|
||||
3. Read the [guide].
|
||||
3. Read [The Rust Programming Language][trpl].
|
||||
4. Enjoy!
|
||||
|
||||
### Building on Windows
|
||||
|
@ -75,7 +75,7 @@ To easily build on windows we can use [MSYS2](http://sourceforge.net/projects/ms
|
|||
|
||||
[repo]: https://github.com/rust-lang/rust
|
||||
[tarball]: https://static.rust-lang.org/dist/rust-nightly.tar.gz
|
||||
[guide]: http://doc.rust-lang.org/guide.html
|
||||
[trpl]: http://doc.rust-lang.org/book/index.html
|
||||
|
||||
## Notes
|
||||
|
||||
|
|
|
@ -19,8 +19,10 @@ Version 1.0.0-alpha (January 2015)
|
|||
distribution into the Cargo ecosystem so they can evolve
|
||||
separately and don't need to be stabilized as quickly, including
|
||||
'time', 'getopts', 'num', 'regex', and 'term'.
|
||||
* Documentation continues to be expanded with more guides, more
|
||||
API coverage and more examples.
|
||||
* Documentation continues to be expanded with more API coverage, more
|
||||
examples, and more in-depth explanations. The guides have been
|
||||
consolidated into [The Rust Programming Language][trpl].
|
||||
* "Rust By Example" is now maintained by the Rust team.
|
||||
* All official Rust binary installers now come with [Cargo], the
|
||||
Rust package manager.
|
||||
|
||||
|
@ -179,6 +181,7 @@ Version 1.0.0-alpha (January 2015)
|
|||
[objsafe]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md
|
||||
[assoc]: https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md
|
||||
[ints]: https://github.com/rust-lang/rfcs/pull/544#issuecomment-68760871
|
||||
[trpl]: http://doc.rust-lang.org/book/index.html
|
||||
|
||||
Version 0.12.0 (October 2014)
|
||||
-----------------------------
|
||||
|
|
|
@ -1055,18 +1055,6 @@ do
|
|||
make_dir $h/test/debuginfo-gdb
|
||||
make_dir $h/test/debuginfo-lldb
|
||||
make_dir $h/test/codegen
|
||||
make_dir $h/test/doc-guide
|
||||
make_dir $h/test/doc-guide-ffi
|
||||
make_dir $h/test/doc-guide-runtime
|
||||
make_dir $h/test/doc-guide-macros
|
||||
make_dir $h/test/doc-guide-ownership
|
||||
make_dir $h/test/doc-guide-pointers
|
||||
make_dir $h/test/doc-guide-container
|
||||
make_dir $h/test/doc-guide-tasks
|
||||
make_dir $h/test/doc-guide-plugin
|
||||
make_dir $h/test/doc-guide-crates
|
||||
make_dir $h/test/doc-guide-error-handling
|
||||
make_dir $h/test/doc-reference
|
||||
done
|
||||
|
||||
# Configure submodules
|
||||
|
|
|
@ -57,7 +57,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \
|
|||
rustc_trans rustc_back rustc_llvm
|
||||
HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
|
||||
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
|
||||
TOOLS := compiletest rustdoc rustc
|
||||
TOOLS := compiletest rustdoc rustc rustbook
|
||||
|
||||
DEPS_core :=
|
||||
DEPS_libc := core
|
||||
|
@ -99,9 +99,11 @@ DEPS_fmt_macros = std
|
|||
TOOL_DEPS_compiletest := test getopts
|
||||
TOOL_DEPS_rustdoc := rustdoc
|
||||
TOOL_DEPS_rustc := rustc_driver
|
||||
TOOL_DEPS_rustbook := std regex rustdoc
|
||||
TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs
|
||||
TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs
|
||||
TOOL_SOURCE_rustc := $(S)src/driver/driver.rs
|
||||
TOOL_SOURCE_rustbook := $(S)src/rustbook/main.rs
|
||||
|
||||
ONLY_RLIB_core := 1
|
||||
ONLY_RLIB_libc := 1
|
||||
|
|
27
mk/docs.mk
27
mk/docs.mk
|
@ -9,7 +9,7 @@
|
|||
# except according to those terms.
|
||||
|
||||
######################################################################
|
||||
# The various pieces of standalone documentation: guides, manual, etc
|
||||
# The various pieces of standalone documentation.
|
||||
#
|
||||
# The DOCS variable is their names (with no file extension).
|
||||
#
|
||||
|
@ -25,13 +25,11 @@
|
|||
# L10N_LANGS are the languages for which the docs have been
|
||||
# translated.
|
||||
######################################################################
|
||||
DOCS := index intro tutorial guide guide-ffi guide-macros guide-ownership \
|
||||
guide-tasks guide-container guide-pointers guide-testing \
|
||||
guide-plugin guide-crates complement-bugreport guide-error-handling \
|
||||
complement-lang-faq complement-design-faq complement-project-faq \
|
||||
rustdoc guide-unsafe guide-strings reference
|
||||
DOCS := index intro tutorial complement-bugreport \
|
||||
complement-lang-faq complement-design-faq complement-project-faq \
|
||||
rustdoc reference
|
||||
|
||||
PDF_DOCS := guide reference
|
||||
PDF_DOCS := reference
|
||||
|
||||
RUSTDOC_DEPS_reference := doc/full-toc.inc
|
||||
RUSTDOC_FLAGS_reference := --html-in-header=doc/full-toc.inc
|
||||
|
@ -61,9 +59,15 @@ RUSTDOC_EXE = $(HBIN2_H_$(CFG_BUILD))/rustdoc$(X_$(CFG_BUILD))
|
|||
# ./configure
|
||||
RUSTDOC = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTDOC_EXE)
|
||||
|
||||
# The rustbook executable...
|
||||
RUSTBOOK_EXE = $(HBIN2_H_$(CFG_BUILD))/rustbook$(X_$(CFG_BUILD))
|
||||
# ...with rpath included in case --disable-rpath was provided to
|
||||
# ./configure
|
||||
RUSTBOOK = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTBOOK_EXE)
|
||||
|
||||
D := $(S)src/doc
|
||||
|
||||
DOC_TARGETS :=
|
||||
DOC_TARGETS := trpl
|
||||
COMPILER_DOC_TARGETS :=
|
||||
DOC_L10N_TARGETS :=
|
||||
|
||||
|
@ -270,3 +274,10 @@ endif
|
|||
|
||||
docs: $(DOC_TARGETS)
|
||||
compiler-docs: $(COMPILER_DOC_TARGETS)
|
||||
|
||||
trpl: tmp/trpl.ok
|
||||
|
||||
tmp/trpl.ok: $(RUSTBOOK_EXE) $(wildcard $(S)/src/doc/trpl/*.md)
|
||||
$(Q)rm -rf doc/book
|
||||
$(Q)$(RUSTBOOK) build $(S)src/doc/trpl doc/book
|
||||
$(Q)touch $@
|
||||
|
|
|
@ -70,7 +70,7 @@ define PREPARE_MAN
|
|||
$(Q)$(PREPARE_MAN_CMD) $(PREPARE_SOURCE_MAN_DIR)/$(1) $(PREPARE_DEST_MAN_DIR)/$(1)
|
||||
endef
|
||||
|
||||
PREPARE_TOOLS = $(filter-out compiletest, $(TOOLS))
|
||||
PREPARE_TOOLS = $(filter-out compiletest rustbook, $(TOOLS))
|
||||
|
||||
|
||||
# $(1) is tool
|
||||
|
|
29
mk/tests.mk
29
mk/tests.mk
|
@ -147,6 +147,17 @@ else
|
|||
CFG_ADB_TEST_DIR=
|
||||
endif
|
||||
|
||||
# $(1) - name of doc test
|
||||
# $(2) - file of the test
|
||||
define DOCTEST
|
||||
DOC_NAMES := $$(DOC_NAMES) $(1)
|
||||
DOCFILE_$(1) := $(2)
|
||||
endef
|
||||
|
||||
$(foreach doc,$(DOCS), \
|
||||
$(eval $(call DOCTEST,md-$(doc),$(S)src/doc/$(doc).md)))
|
||||
$(foreach file,$(wildcard $(S)src/doc/trpl/src/*), \
|
||||
$(eval $(call DOCTEST,$(file:$(S)src/doc/trpl/src/%.md=trpl-%),$(file))))
|
||||
|
||||
######################################################################
|
||||
# Main test targets
|
||||
|
@ -292,6 +303,7 @@ tidy:
|
|||
| grep '^$(S)src/rust-installer' -v \
|
||||
| xargs $(CFG_PYTHON) $(S)src/etc/check-binaries.py
|
||||
|
||||
|
||||
endif
|
||||
|
||||
|
||||
|
@ -339,8 +351,8 @@ check-stage$(1)-T-$(2)-H-$(3)-doc-crates-exec: \
|
|||
check-stage$(1)-T-$(2)-H-$(3)-doc-crate-$$(crate)-exec)
|
||||
|
||||
check-stage$(1)-T-$(2)-H-$(3)-doc-exec: \
|
||||
$$(foreach docname,$$(DOCS), \
|
||||
check-stage$(1)-T-$(2)-H-$(3)-doc-$$(docname)-exec)
|
||||
$$(foreach docname,$$(DOC_NAMES), \
|
||||
check-stage$(1)-T-$(2)-H-$(3)-doc-$$(docname)-exec) \
|
||||
|
||||
check-stage$(1)-T-$(2)-H-$(3)-pretty-exec: \
|
||||
check-stage$(1)-T-$(2)-H-$(3)-pretty-rpass-exec \
|
||||
|
@ -795,17 +807,18 @@ check-stage$(1)-T-$(2)-H-$(3)-doc-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3)
|
|||
# rustdoc etc.
|
||||
ifeq ($(NO_REBUILD),)
|
||||
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \
|
||||
$$(D)/$(4).md \
|
||||
$$(DOCFILE_$(4)) \
|
||||
$$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \
|
||||
$$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3))
|
||||
else
|
||||
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(D)/$(4).md
|
||||
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(DOCFILE_$(4))
|
||||
endif
|
||||
|
||||
ifeq ($(2),$$(CFG_BUILD))
|
||||
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): $$(DOCTESTDEP_$(1)_$(2)_$(3)_$(4))
|
||||
@$$(call E, run doc-$(4) [$(2)])
|
||||
$$(Q)$$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --cfg dox --test $$< --test-args "$$(TESTARGS)" && touch $$@
|
||||
$$(Q)$$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --cfg dox --test $$< \
|
||||
--test-args "$$(TESTARGS)" && touch $$@
|
||||
else
|
||||
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)):
|
||||
touch $$@
|
||||
|
@ -815,7 +828,7 @@ endef
|
|||
$(foreach host,$(CFG_HOST), \
|
||||
$(foreach target,$(CFG_TARGET), \
|
||||
$(foreach stage,$(STAGES), \
|
||||
$(foreach docname,$(DOCS), \
|
||||
$(foreach docname,$(DOC_NAMES), \
|
||||
$(eval $(call DEF_DOC_TEST,$(stage),$(target),$(host),$(docname)))))))
|
||||
|
||||
# Crates
|
||||
|
@ -877,7 +890,7 @@ TEST_GROUPS = \
|
|||
debuginfo-lldb \
|
||||
codegen \
|
||||
doc \
|
||||
$(foreach docname,$(DOCS),doc-$(docname)) \
|
||||
$(foreach docname,$(DOC_NAMES),doc-$(docname)) \
|
||||
pretty \
|
||||
pretty-rpass \
|
||||
pretty-rpass-valgrind \
|
||||
|
@ -946,7 +959,7 @@ $(foreach stage,$(STAGES), \
|
|||
$(eval $(call DEF_CHECK_FOR_STAGE_AND_HOSTS_AND_GROUP,$(stage),$(host),$(group))))))
|
||||
|
||||
define DEF_CHECK_DOC_FOR_STAGE
|
||||
check-stage$(1)-docs: $$(foreach docname,$$(DOCS), \
|
||||
check-stage$(1)-docs: $$(foreach docname,$$(DOC_NAMES), \
|
||||
check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-$$(docname)) \
|
||||
$$(foreach crate,$$(TEST_DOC_CRATES), \
|
||||
check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-crate-$$(crate))
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
% The Rust Containers and Iterators Guide
|
||||
|
||||
This guide has been removed, with no direct replacement.
|
||||
|
||||
You may enjoy reading the [iterator](std/iter/index.html) and
|
||||
[collections](std/collections/index.html) documentation.
|
|
@ -1,308 +0,0 @@
|
|||
% The Guide to Rust Strings
|
||||
|
||||
Strings are an important concept to master in any programming language. If you
|
||||
come from a managed language background, you may be surprised at the complexity
|
||||
of string handling in a systems programming language. Efficient access and
|
||||
allocation of memory for a dynamically sized structure involves a lot of
|
||||
details. Luckily, Rust has lots of tools to help us here.
|
||||
|
||||
A **string** is a sequence of unicode scalar values encoded as a stream of
|
||||
UTF-8 bytes. All strings are guaranteed to be validly-encoded UTF-8 sequences.
|
||||
Additionally, strings are not null-terminated and can contain null bytes.
|
||||
|
||||
Rust has two main types of strings: `&str` and `String`.
|
||||
|
||||
# &str
|
||||
|
||||
The first kind is a `&str`. This is pronounced a 'string slice'.
|
||||
String literals are of the type `&str`:
|
||||
|
||||
```{rust}
|
||||
let string = "Hello there.";
|
||||
```
|
||||
|
||||
Like any Rust type, string slices have an associated lifetime. A string literal
|
||||
is a `&'static str`. A string slice can be written without an explicit
|
||||
lifetime in many cases, such as in function arguments. In these cases the
|
||||
lifetime will be inferred:
|
||||
|
||||
```{rust}
|
||||
fn takes_slice(slice: &str) {
|
||||
println!("Got: {}", slice);
|
||||
}
|
||||
```
|
||||
|
||||
Like vector slices, string slices are simply a pointer plus a length. This
|
||||
means that they're a 'view' into an already-allocated string, such as a
|
||||
`&'static str` or a `String`.
|
||||
|
||||
# String
|
||||
|
||||
A `String` is a heap-allocated string. This string is growable, and is also
|
||||
guaranteed to be UTF-8.
|
||||
|
||||
```{rust}
|
||||
let mut s = "Hello".to_string();
|
||||
println!("{}", s);
|
||||
|
||||
s.push_str(", world.");
|
||||
println!("{}", s);
|
||||
```
|
||||
|
||||
You can coerce a `String` into a `&str` with the `as_slice()` method:
|
||||
|
||||
```{rust}
|
||||
fn takes_slice(slice: &str) {
|
||||
println!("Got: {}", slice);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = "Hello".to_string();
|
||||
takes_slice(s.as_slice());
|
||||
}
|
||||
```
|
||||
|
||||
You can also get a `&str` from a stack-allocated array of bytes:
|
||||
|
||||
```{rust}
|
||||
use std::str;
|
||||
|
||||
let x: &[u8] = &[b'a', b'b'];
|
||||
let stack_str: &str = str::from_utf8(x).unwrap();
|
||||
```
|
||||
|
||||
# Best Practices
|
||||
|
||||
## `String` vs. `&str`
|
||||
|
||||
In general, you should prefer `String` when you need ownership, and `&str` when
|
||||
you just need to borrow a string. This is very similar to using `Vec<T>` vs. `&[T]`,
|
||||
and `T` vs `&T` in general.
|
||||
|
||||
This means starting off with this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn foo(s: &str) {
|
||||
```
|
||||
|
||||
and only moving to this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn foo(s: String) {
|
||||
```
|
||||
|
||||
If you have good reason. It's not polite to hold on to ownership you don't
|
||||
need, and it can make your lifetimes more complex.
|
||||
|
||||
## Generic functions
|
||||
|
||||
To write a function that's generic over types of strings, use `&str`.
|
||||
|
||||
```{rust}
|
||||
fn some_string_length(x: &str) -> uint {
|
||||
x.len()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = "Hello, world";
|
||||
|
||||
println!("{}", some_string_length(s));
|
||||
|
||||
let s = "Hello, world".to_string();
|
||||
|
||||
println!("{}", some_string_length(s.as_slice()));
|
||||
}
|
||||
```
|
||||
|
||||
Both of these lines will print `12`.
|
||||
|
||||
## Comparisons
|
||||
|
||||
To compare a String to a constant string, prefer `as_slice()`...
|
||||
|
||||
```{rust}
|
||||
fn compare(x: String) {
|
||||
if x.as_slice() == "Hello" {
|
||||
println!("yes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
... over `to_string()`:
|
||||
|
||||
```{rust}
|
||||
fn compare(x: String) {
|
||||
if x == "Hello".to_string() {
|
||||
println!("yes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Converting a `String` to a `&str` is cheap, but converting the `&str` to a
|
||||
`String` involves an allocation.
|
||||
|
||||
## Indexing strings
|
||||
|
||||
You may be tempted to try to access a certain character of a `String`, like
|
||||
this:
|
||||
|
||||
```{rust,ignore}
|
||||
let s = "hello".to_string();
|
||||
|
||||
println!("{}", s[0]);
|
||||
```
|
||||
|
||||
This does not compile. This is on purpose. In the world of UTF-8, direct
|
||||
indexing is basically never what you want to do. The reason is that each
|
||||
character can be a variable number of bytes. This means that you have to iterate
|
||||
through the characters anyway, which is an O(n) operation.
|
||||
|
||||
There's 3 basic levels of unicode (and its encodings):
|
||||
|
||||
- code units, the underlying data type used to store everything
|
||||
- code points/unicode scalar values (char)
|
||||
- graphemes (visible characters)
|
||||
|
||||
Rust provides iterators for each of these situations:
|
||||
|
||||
- `.bytes()` will iterate over the underlying bytes
|
||||
- `.chars()` will iterate over the code points
|
||||
- `.graphemes()` will iterate over each grapheme
|
||||
|
||||
Usually, the `graphemes()` method on `&str` is what you want:
|
||||
|
||||
```{rust}
|
||||
let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
|
||||
|
||||
for l in s.graphemes(true) {
|
||||
println!("{}", l);
|
||||
}
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
```{text}
|
||||
u͔
|
||||
n͈̰̎
|
||||
i̙̮͚̦
|
||||
c͚̉
|
||||
o̼̩̰͗
|
||||
d͔̆̓ͥ
|
||||
é
|
||||
```
|
||||
|
||||
Note that `l` has the type `&str` here, since a single grapheme can consist of
|
||||
multiple codepoints, so a `char` wouldn't be appropriate.
|
||||
|
||||
This will print out each visible character in turn, as you'd expect: first "u͔", then
|
||||
"n͈̰̎", etc. If you wanted each individual codepoint of each grapheme, you can use `.chars()`:
|
||||
|
||||
```{rust}
|
||||
let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
|
||||
|
||||
for l in s.chars() {
|
||||
println!("{}", l);
|
||||
}
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
```{text}
|
||||
u
|
||||
͔
|
||||
n
|
||||
̎
|
||||
͈
|
||||
̰
|
||||
i
|
||||
̙
|
||||
̮
|
||||
͚
|
||||
̦
|
||||
c
|
||||
̉
|
||||
͚
|
||||
o
|
||||
͗
|
||||
̼
|
||||
̩
|
||||
̰
|
||||
d
|
||||
̆
|
||||
̓
|
||||
ͥ
|
||||
͔
|
||||
e
|
||||
́
|
||||
```
|
||||
|
||||
You can see how some of them are combining characters, and therefore the output
|
||||
looks a bit odd.
|
||||
|
||||
If you want the individual byte representation of each codepoint, you can use
|
||||
`.bytes()`:
|
||||
|
||||
```{rust}
|
||||
let s = "u͔n͈̰̎i̙̮͚̦c͚̉o̼̩̰͗d͔̆̓ͥé";
|
||||
|
||||
for l in s.bytes() {
|
||||
println!("{}", l);
|
||||
}
|
||||
```
|
||||
|
||||
This will print:
|
||||
|
||||
```{text}
|
||||
117
|
||||
205
|
||||
148
|
||||
110
|
||||
204
|
||||
142
|
||||
205
|
||||
136
|
||||
204
|
||||
176
|
||||
105
|
||||
204
|
||||
153
|
||||
204
|
||||
174
|
||||
205
|
||||
154
|
||||
204
|
||||
166
|
||||
99
|
||||
204
|
||||
137
|
||||
205
|
||||
154
|
||||
111
|
||||
205
|
||||
151
|
||||
204
|
||||
188
|
||||
204
|
||||
169
|
||||
204
|
||||
176
|
||||
100
|
||||
204
|
||||
134
|
||||
205
|
||||
131
|
||||
205
|
||||
165
|
||||
205
|
||||
148
|
||||
101
|
||||
204
|
||||
129
|
||||
```
|
||||
|
||||
Many more bytes than graphemes!
|
||||
|
||||
# Other Documentation
|
||||
|
||||
* [the `&str` API documentation](std/str/index.html)
|
||||
* [the `String` API documentation](std/string/index.html)
|
5520
src/doc/guide.md
5520
src/doc/guide.md
File diff suppressed because it is too large
Load Diff
|
@ -9,10 +9,11 @@ If you haven't seen Rust at all yet, the first thing you should read is the [30
|
|||
minute intro](intro.html). It will give you an overview of the basic ideas of Rust
|
||||
at a high level.
|
||||
|
||||
Once you know you really want to learn Rust, the next step is reading [the
|
||||
guide](guide.html). It is a lengthy explanation of Rust, its syntax, and its
|
||||
concepts. Upon completing the guide, you'll be an intermediate Rust developer,
|
||||
and will have a good grasp of the fundamental ideas behind Rust.
|
||||
Once you know you really want to learn Rust, the next step is reading [The
|
||||
Rust Programming Language](book/index.html). It is a lengthy explanation of
|
||||
Rust, its syntax, and its concepts. Upon completing the book, you'll be an
|
||||
intermediate Rust developer, and will have a good grasp of the fundamental
|
||||
ideas behind Rust.
|
||||
|
||||
# Community & Getting Help
|
||||
|
||||
|
@ -48,24 +49,6 @@ development of Rust itself is discussed.
|
|||
Rust does not have an exact specification, but an effort to describe as much of
|
||||
the language in as much detail as possible is in [the reference](reference.html).
|
||||
|
||||
# Guides
|
||||
|
||||
Rust Guides are in-depth looks at a particular topic that's relevant to Rust
|
||||
development. If you're trying to figure out how to do something, there may be
|
||||
a guide that can help you out:
|
||||
|
||||
* [Ownership](guide-ownership.html)
|
||||
* [Strings](guide-strings.html)
|
||||
* [Pointers](guide-pointers.html)
|
||||
* [Crates and modules](guide-crates.html)
|
||||
* [Threads and Communication](guide-tasks.html)
|
||||
* [Error Handling](guide-error-handling.html)
|
||||
* [Foreign Function Interface](guide-ffi.html)
|
||||
* [Writing Unsafe and Low-Level Code](guide-unsafe.html)
|
||||
* [Macros](guide-macros.html)
|
||||
* [Testing](guide-testing.html)
|
||||
* [Compiler Plugins](guide-plugin.html)
|
||||
|
||||
# Tools
|
||||
|
||||
Rust's still a young language, so there isn't a ton of tooling yet, but the
|
||||
|
|
|
@ -7,8 +7,8 @@ This introduction will give you a rough idea of what Rust is like, eliding many
|
|||
details. It does not require prior experience with systems programming, but you
|
||||
may find the syntax easier if you've used a 'curly brace' programming language
|
||||
before, like C or JavaScript. The concepts are more important than the syntax,
|
||||
so don't worry if you don't get every last detail: you can read [the
|
||||
Guide](guide.html) to get a more complete explanation.
|
||||
so don't worry if you don't get every last detail: you can read [The
|
||||
Rust Programming Language](book/index.html) to get a more complete explanation.
|
||||
|
||||
Because this is about high-level concepts, you don't need to actually install
|
||||
Rust to follow along. If you'd like to anyway, check out [the
|
||||
|
@ -587,5 +587,6 @@ the type system helps you find bugs, how Rust can help you write correct
|
|||
concurrent code, and how you don't have to pay a speed cost for much of this
|
||||
safety.
|
||||
|
||||
To continue your Rustic education, read [the guide](guide.html) for a more
|
||||
in-depth exploration of Rust's syntax and concepts.
|
||||
To continue your Rustic education, read [The Rust Programming
|
||||
Language](book/index.html) for a more in-depth exploration of Rust's syntax and
|
||||
concepts.
|
||||
|
|
|
@ -14,7 +14,7 @@ provides three kinds of material:
|
|||
influenced the design.
|
||||
|
||||
This document does not serve as an introduction to the language. Background
|
||||
familiarity with the language is assumed. A separate [guide] is available to
|
||||
familiarity with the language is assumed. A separate [book] is available to
|
||||
help acquire such background familiarity.
|
||||
|
||||
This document also does not serve as a reference to the [standard] library
|
||||
|
@ -23,7 +23,7 @@ separately by extracting documentation attributes from their source code. Many
|
|||
of the features that one might expect to be language features are library
|
||||
features in Rust, so what you're looking for may be there, not here.
|
||||
|
||||
[guide]: guide.html
|
||||
[book]: book/index.html
|
||||
[standard]: std/index.html
|
||||
|
||||
# Notation
|
||||
|
@ -647,10 +647,10 @@ All of the above extensions are expressions with values.
|
|||
|
||||
Users of `rustc` can define new syntax extensions in two ways:
|
||||
|
||||
* [Compiler plugins](guide-plugin.html#syntax-extensions) can include arbitrary
|
||||
* [Compiler plugins](book/syntax-extensions.html) can include arbitrary
|
||||
Rust code that manipulates syntax trees at compile time.
|
||||
|
||||
* [Macros](guide-macros.html) define new syntax in a higher-level,
|
||||
* [Macros](book/macros.html) define new syntax in a higher-level,
|
||||
declarative way.
|
||||
|
||||
## Macros
|
||||
|
@ -2076,7 +2076,7 @@ On `struct`s:
|
|||
list of names `#[macro_use(foo, bar)]` restricts the import to just those
|
||||
macros named. The `extern crate` must appear at the crate root, not inside
|
||||
`mod`, which ensures proper function of the [`$crate` macro
|
||||
variable](guide-macros.html#the-variable-$crate).
|
||||
variable](book/macros.html#the-variable-$crate).
|
||||
|
||||
- `macro_reexport` on an `extern crate` — re-export the named macros.
|
||||
|
||||
|
@ -2090,8 +2090,9 @@ On `struct`s:
|
|||
- `no_link` on an `extern crate` — even if we load this crate for macros or
|
||||
compiler plugins, don't link it into the output.
|
||||
|
||||
See the [macros guide](guide-macros.html#scoping-and-macro-import/export) for
|
||||
more information on macro scope.
|
||||
See the [macros section of the
|
||||
book](book/macros.html#scoping-and-macro-import/export) for more information on
|
||||
macro scope.
|
||||
|
||||
|
||||
### Miscellaneous attributes
|
||||
|
@ -2193,7 +2194,7 @@ For any lint check `C`:
|
|||
|
||||
The lint checks supported by the compiler can be found via `rustc -W help`,
|
||||
along with their default settings. [Compiler
|
||||
plugins](guide-plugin.html#lint-plugins) can provide additional lint checks.
|
||||
plugins](book/plugin.html#lint-plugins) can provide additional lint checks.
|
||||
|
||||
```{.ignore}
|
||||
mod m1 {
|
||||
|
@ -4227,7 +4228,7 @@ communication facilities.
|
|||
The Rust compiler supports various methods to link crates together both
|
||||
statically and dynamically. This section will explore the various methods to
|
||||
link Rust crates together, and more information about native libraries can be
|
||||
found in the [ffi guide][ffi].
|
||||
found in the [ffi section of the book][ffi].
|
||||
|
||||
In one session of compilation, the compiler can generate multiple artifacts
|
||||
through the usage of either command line flags or the `crate_type` attribute.
|
||||
|
@ -4359,5 +4360,5 @@ that have since been removed):
|
|||
* [Unicode Annex #31](http://www.unicode.org/reports/tr31/): identifier and
|
||||
pattern syntax
|
||||
|
||||
[ffi]: guide-ffi.html
|
||||
[plugin]: guide-plugin.html
|
||||
[ffi]: book/ffi.html
|
||||
[plugin]: book/plugin.html
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
% The Rust Programming Language
|
||||
|
||||
Welcome! This book will teach you about [the Rust Programming
|
||||
Language](http://www.rust-lang.org/). Rust is a modern systems programming
|
||||
language focusing on safety and speed. It accomplishes these goals by being
|
||||
memory safe without using garbage collection.
|
||||
|
||||
"The Rust Programming Language" is split into three sections, which you can
|
||||
navigate through the menu on the left.
|
||||
|
||||
## Basics
|
||||
|
||||
This section is a linear introduction to the basic syntax and semantics of
|
||||
Rust. It has individual sections on each part of Rust's syntax, and cumulates
|
||||
in a small project: a guessing game.
|
||||
|
||||
After reading "Basics," you will have a good foundation to learn more about
|
||||
Rust, and can write very simple programs.
|
||||
|
||||
## Intermediate
|
||||
|
||||
This section contains individual chapters, which are self-contained. They focus
|
||||
on specific topics, and can be read in any order.
|
||||
|
||||
After reading "Intermediate," you will have a solid understanding of Rust,
|
||||
and will be able to understand most Rust code and write more complex programs.
|
||||
|
||||
## Advanced
|
||||
|
||||
In a similar fashion to "Intermediate," this setion is full of individual,
|
||||
deep-dive chapters, which stand alone and can be read in any order. These
|
||||
chapters focus on the most complex features, as well as some things that
|
||||
are only available in upcoming versions of Rust.
|
||||
|
||||
After reading "Advanced," you'll be a Rust expert!
|
|
@ -0,0 +1,35 @@
|
|||
# Summary
|
||||
|
||||
* [I: The Basics](src/basic.md)
|
||||
* [Installing Rust](src/installing-rust.md)
|
||||
* [Hello, world!](src/hello-world.md)
|
||||
* [Hello, Cargo!](src/hello-cargo.md)
|
||||
* [Variable Bindings](src/variable-bindings.md)
|
||||
* [If](src/if.md)
|
||||
* [Functions](src/functions.md)
|
||||
* [Comments](src/comments.md)
|
||||
* [Compound Data Types](src/compound-data-types.md)
|
||||
* [Match](src/match.md)
|
||||
* [Looping](src/looping.md)
|
||||
* [Strings](src/strings.md)
|
||||
* [Arrays, Vectors, and Slices](src/arrays-vectors-and-slices.md)
|
||||
* [Standard Input](src/standard-input.md)
|
||||
* [Guessing Game](src/guessing-game.md)
|
||||
* [II: Intermedite Rust](src/intermediate.md)
|
||||
* [Crates and Modules](src/crates-and-modules.md)
|
||||
* [Testing](src/testing.md)
|
||||
* [Pointers](src/pointers.md)
|
||||
* [Patterns](src/patterns.md)
|
||||
* [Method Syntax](src/method-syntax.md)
|
||||
* [Closures](src/closures.md)
|
||||
* [Iterators](src/iterators.md)
|
||||
* [Generics](src/generics.md)
|
||||
* [Traits](src/traits.md)
|
||||
* [Tasks](src/tasks.md)
|
||||
* [Error Handling](src/error-handling.md)
|
||||
* [III: Advanced Topics](src/advanced.md)
|
||||
* [FFI](src/ffi.md)
|
||||
* [Unsafe Code](src/unsafe.md)
|
||||
* [Macros](src/macros.md)
|
||||
* [Compiler Plugins](src/plugins.md)
|
||||
* [Conclusion](src/conclusion.md)
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
@import url("//static.rust-lang.org/doc/master/rust.css");
|
||||
|
||||
body {
|
||||
max-width:none;
|
||||
}
|
||||
|
||||
#toc {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
width: 250px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.07);
|
||||
padding: 10px 10px;
|
||||
font-size: 16px;
|
||||
background: none repeat scroll 0% 0% #FFF;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
position: absolute;
|
||||
overflow-y: auto;
|
||||
left: 260px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
box-sizing: border-box;
|
||||
background: none repeat scroll 0% 0% #FFF;
|
||||
}
|
||||
|
||||
#page {
|
||||
margin-left: auto;
|
||||
margin-right:auto;
|
||||
width: 750px;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-left: 0px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-left: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.section li {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chapter li a {
|
||||
color: #000000;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
% Advanced
|
||||
|
||||
In a similar fashion to "Intermediate," this setion is full of individual,
|
||||
deep-dive chapters, which stand alone and can be read in any order. These
|
||||
chapters focus on the most complex features, as well as some things that
|
||||
are only available in upcoming versions of Rust.
|
||||
|
||||
After reading "Advanced," you'll be a Rust expert!
|
|
@ -0,0 +1,99 @@
|
|||
# Arrays, Vectors, and Slices
|
||||
|
||||
Like many programming languages, Rust has list types to represent a sequence of
|
||||
things. The most basic is the **array**, a fixed-size list of elements of the
|
||||
same type. By default, arrays are immutable.
|
||||
|
||||
```{rust}
|
||||
let a = [1, 2, 3]; // a: [i32; 3]
|
||||
let mut m = [1, 2, 3]; // mut m: [i32; 3]
|
||||
```
|
||||
|
||||
There's a shorthand for initializing each element of an array to the same
|
||||
value. In this example, each element of `a` will be initialized to `0`:
|
||||
|
||||
```{rust}
|
||||
let a = [0; 20]; // a: [i32; 20]
|
||||
```
|
||||
|
||||
Arrays have type `[T; N]`. We'll talk about this `T` notation later, when we
|
||||
cover generics.
|
||||
|
||||
You can get the number of elements in an array `a` with `a.len()`, and use
|
||||
`a.iter()` to iterate over them with a for loop. This code will print each
|
||||
number in order:
|
||||
|
||||
```{rust}
|
||||
let a = [1, 2, 3];
|
||||
|
||||
println!("a has {} elements", a.len());
|
||||
for e in a.iter() {
|
||||
println!("{}", e);
|
||||
}
|
||||
```
|
||||
|
||||
You can access a particular element of an array with **subscript notation**:
|
||||
|
||||
```{rust}
|
||||
let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3]
|
||||
|
||||
println!("The second name is: {}", names[1]);
|
||||
```
|
||||
|
||||
Subscripts start at zero, like in most programming languages, so the first name
|
||||
is `names[0]` and the second name is `names[1]`. The above example prints
|
||||
`The second name is: Brian`. If you try to use a subscript that is not in the
|
||||
array, you will get an error: array access is bounds-checked at run-time. Such
|
||||
errant access is the source of many bugs in other systems programming
|
||||
languages.
|
||||
|
||||
A **vector** is a dynamic or "growable" array, implemented as the standard
|
||||
library type [`Vec<T>`](std/vec/) (we'll talk about what the `<T>` means
|
||||
later). Vectors are to arrays what `String` is to `&str`. You can create them
|
||||
with the `vec!` macro:
|
||||
|
||||
```{rust}
|
||||
let v = vec![1, 2, 3]; // v: Vec<i32>
|
||||
```
|
||||
|
||||
(Notice that unlike the `println!` macro we've used in the past, we use square
|
||||
brackets `[]` with `vec!`. Rust allows you to use either in either situation,
|
||||
this is just convention.)
|
||||
|
||||
You can get the length of, iterate over, and subscript vectors just like
|
||||
arrays. In addition, (mutable) vectors can grow automatically:
|
||||
|
||||
```{rust}
|
||||
let mut nums = vec![1, 2, 3]; // mut nums: Vec<i32>
|
||||
|
||||
nums.push(4);
|
||||
|
||||
println!("The length of nums is now {}", nums.len()); // Prints 4
|
||||
```
|
||||
|
||||
Vectors have many more useful methods.
|
||||
|
||||
A **slice** is a reference to (or "view" into) an array. They are useful for
|
||||
allowing safe, efficient access to a portion of an array without copying. For
|
||||
example, you might want to reference just one line of a file read into memory.
|
||||
By nature, a slice is not created directly, but from an existing variable.
|
||||
Slices have a length, can be mutable or not, and in many ways behave like
|
||||
arrays:
|
||||
|
||||
```{rust}
|
||||
let a = [0, 1, 2, 3, 4];
|
||||
let middle = a.slice(1, 4); // A slice of a: just the elements [1,2,3]
|
||||
|
||||
for e in middle.iter() {
|
||||
println!("{}", e); // Prints 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
You can also take a slice of a vector, `String`, or `&str`, because they are
|
||||
backed by arrays. Slices have type `&[T]`, which we'll talk about when we cover
|
||||
generics.
|
||||
|
||||
We have now learned all of the most basic Rust concepts. We're ready to start
|
||||
building our guessing game, we just need to know one last thing: how to get
|
||||
input from the keyboard. You can't have a guessing game without the ability to
|
||||
guess!
|
|
@ -0,0 +1,8 @@
|
|||
% Basics
|
||||
|
||||
This section is a linear introduction to the basic syntax and semantics of
|
||||
Rust. It has individual sections on each part of Rust's syntax, and cumulates
|
||||
in a small project: a guessing game.
|
||||
|
||||
After reading "Basics," you will have a good foundation to learn more about
|
||||
Rust, and can write very simple programs.
|
|
@ -0,0 +1,185 @@
|
|||
# Closures
|
||||
|
||||
So far, we've made lots of functions in Rust, but we've given them all names.
|
||||
Rust also allows us to create anonymous functions. Rust's anonymous
|
||||
functions are called **closure**s. By themselves, closures aren't all that
|
||||
interesting, but when you combine them with functions that take closures as
|
||||
arguments, really powerful things are possible.
|
||||
|
||||
Let's make a closure:
|
||||
|
||||
```{rust}
|
||||
let add_one = |&: x| { 1 + x };
|
||||
|
||||
println!("The sum of 5 plus 1 is {}.", add_one(5));
|
||||
```
|
||||
|
||||
We create a closure using the `|...| { ... }` syntax, and then we create a
|
||||
binding so we can use it later. Note that we call the function using the
|
||||
binding name and two parentheses, just like we would for a named function.
|
||||
|
||||
Let's compare syntax. The two are pretty close:
|
||||
|
||||
```{rust}
|
||||
let add_one = |&: x: i32| -> i32 { 1 + x };
|
||||
fn add_one (x: i32) -> i32 { 1 + x }
|
||||
```
|
||||
|
||||
As you may have noticed, closures infer their argument and return types, so you
|
||||
don't need to declare one. This is different from named functions, which
|
||||
default to returning unit (`()`).
|
||||
|
||||
There's one big difference between a closure and named functions, and it's in
|
||||
the name: a closure "closes over its environment." What does that mean? It means
|
||||
this:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
let x: i32 = 5;
|
||||
|
||||
let printer = |&:| { println!("x is: {}", x); };
|
||||
|
||||
printer(); // prints "x is: 5"
|
||||
}
|
||||
```
|
||||
|
||||
The `||` syntax means this is an anonymous closure that takes no arguments.
|
||||
Without it, we'd just have a block of code in `{}`s.
|
||||
|
||||
In other words, a closure has access to variables in the scope where it's
|
||||
defined. The closure borrows any variables it uses, so this will error:
|
||||
|
||||
```{rust,ignore}
|
||||
fn main() {
|
||||
let mut x = 5;
|
||||
|
||||
let printer = |&:| { println!("x is: {}", x); };
|
||||
|
||||
x = 6; // error: cannot assign to `x` because it is borrowed
|
||||
}
|
||||
```
|
||||
|
||||
## Moving closures
|
||||
|
||||
Rust has a second type of closure, called a **moving closure**. Moving
|
||||
closures are indicated using the `move` keyword (e.g., `move || x *
|
||||
x`). The difference between a moving closure and an ordinary closure
|
||||
is that a moving closure always takes ownership of all variables that
|
||||
it uses. Ordinary closures, in contrast, just create a reference into
|
||||
the enclosing stack frame. Moving closures are most useful with Rust's
|
||||
concurrency features, and so we'll just leave it at this for
|
||||
now. We'll talk about them more in the "Threads" section of the guide.
|
||||
|
||||
## Accepting closures as arguments
|
||||
|
||||
Closures are most useful as an argument to another function. Here's an example:
|
||||
|
||||
```{rust}
|
||||
fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
|
||||
f(x) + f(x)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let square = |&: x: i32| { x * x };
|
||||
|
||||
twice(5, square); // evaluates to 50
|
||||
}
|
||||
```
|
||||
|
||||
Let's break the example down, starting with `main`:
|
||||
|
||||
```{rust}
|
||||
let square = |&: x: i32| { x * x };
|
||||
```
|
||||
|
||||
We've seen this before. We make a closure that takes an integer, and returns
|
||||
its square.
|
||||
|
||||
```{rust}
|
||||
# fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 { f(x) + f(x) }
|
||||
# let square = |&: x: i32| { x * x };
|
||||
twice(5, square); // evaluates to 50
|
||||
```
|
||||
|
||||
This line is more interesting. Here, we call our function, `twice`, and we pass
|
||||
it two arguments: an integer, `5`, and our closure, `square`. This is just like
|
||||
passing any other two variable bindings to a function, but if you've never
|
||||
worked with closures before, it can seem a little complex. Just think: "I'm
|
||||
passing two variables: one is an i32, and one is a function."
|
||||
|
||||
Next, let's look at how `twice` is defined:
|
||||
|
||||
```{rust,ignore}
|
||||
fn twice(x: i32, f: |i32| -> i32) -> i32 {
|
||||
```
|
||||
|
||||
`twice` takes two arguments, `x` and `f`. That's why we called it with two
|
||||
arguments. `x` is an `i32`, we've done that a ton of times. `f` is a function,
|
||||
though, and that function takes an `i32` and returns an `i32`. Notice
|
||||
how the `|i32| -> i32` syntax looks a lot like our definition of `square`
|
||||
above, if we added the return type in:
|
||||
|
||||
```{rust}
|
||||
let square = |&: x: i32| -> i32 { x * x };
|
||||
// |i32| -> i32
|
||||
```
|
||||
|
||||
This function takes an `i32` and returns an `i32`.
|
||||
|
||||
This is the most complicated function signature we've seen yet! Give it a read
|
||||
a few times until you can see how it works. It takes a teeny bit of practice, and
|
||||
then it's easy.
|
||||
|
||||
Finally, `twice` returns an `i32` as well.
|
||||
|
||||
Okay, let's look at the body of `twice`:
|
||||
|
||||
```{rust}
|
||||
fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
|
||||
f(x) + f(x)
|
||||
}
|
||||
```
|
||||
|
||||
Since our closure is named `f`, we can call it just like we called our closures
|
||||
before, and we pass in our `x` argument to each one, hence the name `twice`.
|
||||
|
||||
If you do the math, `(5 * 5) + (5 * 5) == 50`, so that's the output we get.
|
||||
|
||||
Play around with this concept until you're comfortable with it. Rust's standard
|
||||
library uses lots of closures where appropriate, so you'll be using
|
||||
this technique a lot.
|
||||
|
||||
If we didn't want to give `square` a name, we could just define it inline.
|
||||
This example is the same as the previous one:
|
||||
|
||||
```{rust}
|
||||
fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
|
||||
f(x) + f(x)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
twice(5, |x: i32| { x * x }); // evaluates to 50
|
||||
}
|
||||
```
|
||||
|
||||
A named function's name can be used wherever you'd use a closure. Another
|
||||
way of writing the previous example:
|
||||
|
||||
```{rust}
|
||||
fn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
|
||||
f(x) + f(x)
|
||||
}
|
||||
|
||||
fn square(x: i32) -> i32 { x * x }
|
||||
|
||||
fn main() {
|
||||
twice(5, square); // evaluates to 50
|
||||
}
|
||||
```
|
||||
|
||||
Doing this is not particularly common, but it's useful every once in a while.
|
||||
|
||||
That's all you need to get the hang of closures! Closures are a little bit
|
||||
strange at first, but once you're used to them, you'll miss them
|
||||
in other languages. Passing functions to other functions is
|
||||
incredibly powerful, as you will see in the following chapter about iterators.
|
|
@ -0,0 +1,46 @@
|
|||
# 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 comment**s
|
||||
and **doc comment**s.
|
||||
|
||||
```{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
|
||||
// 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.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let name = "Steve";
|
||||
/// hello(name); // prints "Hello, Steve!"
|
||||
/// ```
|
||||
fn hello(name: &str) {
|
||||
println!("Hello, {}!", name);
|
||||
}
|
||||
```
|
||||
|
||||
When writing doc comments, adding sections for any arguments, return values,
|
||||
and providing some examples of usage is very, very helpful.
|
||||
|
||||
You can use the [`rustdoc`](rustdoc.html) tool to generate HTML documentation
|
||||
from these doc comments.
|
|
@ -0,0 +1,353 @@
|
|||
# Compound Data Types
|
||||
|
||||
Rust, like many programming languages, has a number of different data types
|
||||
that are built-in. You've already done some simple work with integers and
|
||||
strings, but next, let's talk about some more complicated ways of storing data.
|
||||
|
||||
## Tuples
|
||||
|
||||
The first compound data type we're going to talk about are called **tuple**s.
|
||||
Tuples are an ordered list of a fixed size. Like this:
|
||||
|
||||
```rust
|
||||
let x = (1, "hello");
|
||||
```
|
||||
|
||||
The parentheses and commas form this two-length tuple. Here's the same code, but
|
||||
with the type annotated:
|
||||
|
||||
```rust
|
||||
let x: (i32, &str) = (1, "hello");
|
||||
```
|
||||
|
||||
As you can see, the type of a tuple looks just like the tuple, but with each
|
||||
position having a type name rather than the value. Careful readers will also
|
||||
note that tuples are heterogeneous: we have an `i32` and a `&str` in this tuple.
|
||||
You haven't seen `&str` as a type before, and we'll discuss the details of
|
||||
strings later. In systems programming languages, strings are a bit more complex
|
||||
than in other languages. For now, just read `&str` as "a string slice," and
|
||||
we'll learn more soon.
|
||||
|
||||
You can access the fields in a tuple through a **destructuring let**. Here's
|
||||
an example:
|
||||
|
||||
```rust
|
||||
let (x, y, z) = (1, 2, 3);
|
||||
|
||||
println!("x is {}", x);
|
||||
```
|
||||
|
||||
Remember before when I said the left-hand side of a `let` statement was more
|
||||
powerful than just assigning a binding? Here we are. We can put a pattern on
|
||||
the left-hand side of the `let`, and if it matches up to the right-hand side,
|
||||
we can assign multiple bindings at once. In this case, `let` 'destructures,'
|
||||
or 'breaks up,' the tuple, and assigns the bits to three bindings.
|
||||
|
||||
This pattern is very powerful, and we'll see it repeated more later.
|
||||
|
||||
There are also a few things you can do with a tuple as a whole, without
|
||||
destructuring. You can assign one tuple into another, if they have the same
|
||||
arity and contained types.
|
||||
|
||||
```rust
|
||||
let mut x = (1, 2); // x: (i32, i32)
|
||||
let y = (2, 3); // y: (i32, i32)
|
||||
|
||||
x = y;
|
||||
```
|
||||
|
||||
You can also check for equality with `==`. Again, this will only compile if the
|
||||
tuples have the same type.
|
||||
|
||||
```rust
|
||||
let x = (1, 2, 3);
|
||||
let y = (2, 2, 4);
|
||||
|
||||
if x == y {
|
||||
println!("yes");
|
||||
} else {
|
||||
println!("no");
|
||||
}
|
||||
```
|
||||
|
||||
This will print `no`, because some of the values aren't equal.
|
||||
|
||||
One other use of tuples is to return multiple values from a function:
|
||||
|
||||
```rust
|
||||
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }
|
||||
|
||||
fn main() {
|
||||
let (x, y) = next_two(5);
|
||||
println!("x, y = {}, {}", x, y);
|
||||
}
|
||||
```
|
||||
|
||||
Even though Rust functions can only return one value, a tuple _is_ one value,
|
||||
that happens to be made up of more than one value. You can also see in this example how you
|
||||
can destructure a pattern returned by a function, as well.
|
||||
|
||||
Tuples are a very simple data structure, and so are not often what you want.
|
||||
Let's move on to their bigger sibling, structs.
|
||||
|
||||
## 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:
|
||||
|
||||
```rust
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let origin = Point { x: 0, y: 0 }; // origin: Point
|
||||
|
||||
println!("The origin is at ({}, {})", origin.x, origin.y);
|
||||
}
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
We can create an instance of our struct via `let`, as usual, but we use a `key:
|
||||
value` style syntax to set each field. The order doesn't need to be the same as
|
||||
in the original declaration.
|
||||
|
||||
Finally, because fields have names, we can access the field through dot
|
||||
notation: `origin.x`.
|
||||
|
||||
The values in structs are immutable by default, like other bindings in Rust.
|
||||
Use `mut` to make them mutable:
|
||||
|
||||
```{rust}
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut point = Point { x: 0, y: 0 };
|
||||
|
||||
point.x = 5;
|
||||
|
||||
println!("The point is at ({}, {})", point.x, point.y);
|
||||
}
|
||||
```
|
||||
|
||||
This will print `The point is at (5, 0)`.
|
||||
|
||||
## Tuple Structs and Newtypes
|
||||
|
||||
Rust has another data type that's like a hybrid between a tuple and a struct,
|
||||
called a **tuple struct**. Tuple structs do have a name, but their fields
|
||||
don't:
|
||||
|
||||
|
||||
```{rust}
|
||||
struct Color(i32, i32, i32);
|
||||
struct Point(i32, i32, i32);
|
||||
```
|
||||
|
||||
These two will not be equal, even if they have the same values:
|
||||
|
||||
```{rust}
|
||||
# struct Color(i32, i32, i32);
|
||||
# struct Point(i32, i32, i32);
|
||||
let black = Color(0, 0, 0);
|
||||
let origin = Point(0, 0, 0);
|
||||
```
|
||||
|
||||
It is almost always better to use a struct than a tuple struct. We would write
|
||||
`Color` and `Point` like this instead:
|
||||
|
||||
```{rust}
|
||||
struct Color {
|
||||
red: i32,
|
||||
blue: i32,
|
||||
green: i32,
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
```
|
||||
|
||||
Now, we have actual names, rather than positions. Good names are important,
|
||||
and with a struct, we have actual names.
|
||||
|
||||
There _is_ one case when a tuple struct is very useful, though, and that's a
|
||||
tuple struct with only one element. We call this a 'newtype,' because it lets
|
||||
you create a new type that's a synonym for another one:
|
||||
|
||||
```{rust}
|
||||
struct Inches(i32);
|
||||
|
||||
let length = Inches(10);
|
||||
|
||||
let Inches(integer_length) = length;
|
||||
println!("length is {} inches", integer_length);
|
||||
```
|
||||
|
||||
As you can see here, you can extract the inner integer type through a
|
||||
destructuring `let`.
|
||||
|
||||
## Enums
|
||||
|
||||
Finally, Rust has a "sum type", an **enum**. Enums are an incredibly useful
|
||||
feature of Rust, and are used throughout the standard library. This is an enum
|
||||
that is provided by the Rust standard library:
|
||||
|
||||
```{rust}
|
||||
enum Ordering {
|
||||
Less,
|
||||
Equal,
|
||||
Greater,
|
||||
}
|
||||
```
|
||||
|
||||
An `Ordering` can only be _one_ of `Less`, `Equal`, or `Greater` at any given
|
||||
time.
|
||||
|
||||
Because `Ordering` is provided by the standard library, we can use the `use`
|
||||
keyword to use it in our code. We'll learn more about `use` later, but it's
|
||||
used to bring names into scope.
|
||||
|
||||
Here's an example of how to use `Ordering`:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
let ordering = cmp(x, y); // ordering: Ordering
|
||||
|
||||
if ordering == Ordering::Less {
|
||||
println!("less");
|
||||
} else if ordering == Ordering::Greater {
|
||||
println!("greater");
|
||||
} else if ordering == Ordering::Equal {
|
||||
println!("equal");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There's a symbol here we haven't seen before: the double colon (`::`).
|
||||
This is used to indicate a namespace. In this case, `Ordering` lives in
|
||||
the `cmp` submodule of the `std` module. We'll talk more about modules
|
||||
later in the guide. For now, all you need to know is that you can `use`
|
||||
things from the standard library if you need them.
|
||||
|
||||
Okay, let's talk about the actual code in the example. `cmp` is a function that
|
||||
compares two things, and returns an `Ordering`. We return either
|
||||
`Ordering::Less`, `Ordering::Greater`, or `Ordering::Equal`, depending on if
|
||||
the two values are greater, less, or equal. Note that each variant of the
|
||||
`enum` is namespaced under the `enum` itself: it's `Ordering::Greater` not
|
||||
`Greater`.
|
||||
|
||||
The `ordering` variable has the type `Ordering`, and so contains one of the
|
||||
three values. We can then do a bunch of `if`/`else` comparisons to check which
|
||||
one it is. However, repeated `if`/`else` comparisons get quite tedious. Rust
|
||||
has a feature that not only makes them nicer to read, but also makes sure that
|
||||
you never miss a case. Before we get to that, though, let's talk about another
|
||||
kind of enum: one with values.
|
||||
|
||||
This enum has two variants, one of which has a value:
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(i32),
|
||||
Missing,
|
||||
}
|
||||
```
|
||||
|
||||
This enum represents an `i32` that we may or may not have. In the `Missing`
|
||||
case, we have no value, but in the `Value` case, we do. This enum is specific
|
||||
to `i32`s, though. We can make it usable by any type, but we haven't quite
|
||||
gotten there yet!
|
||||
|
||||
You can also have any number of values in an enum:
|
||||
|
||||
```{rust}
|
||||
enum OptionalColor {
|
||||
Color(i32, i32, i32),
|
||||
Missing,
|
||||
}
|
||||
```
|
||||
|
||||
And you can also have something like this:
|
||||
|
||||
```{rust}
|
||||
enum StringResult {
|
||||
StringOK(String),
|
||||
ErrorReason(String),
|
||||
}
|
||||
```
|
||||
Where a `StringResult` is either a `StringResult::StringOK`, with the result of
|
||||
a computation, or an `StringResult::ErrorReason` with a `String` explaining
|
||||
what caused the computation to fail. These kinds of `enum`s are actually very
|
||||
useful and are even part of the standard library.
|
||||
|
||||
Here is an example of using our `StringResult`:
|
||||
|
||||
```rust
|
||||
enum StringResult {
|
||||
StringOK(String),
|
||||
ErrorReason(String),
|
||||
}
|
||||
|
||||
fn respond(greeting: &str) -> StringResult {
|
||||
if greeting == "Hello" {
|
||||
StringResult::StringOK("Good morning!".to_string())
|
||||
} else {
|
||||
StringResult::ErrorReason("I didn't understand you!".to_string())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's a lot of typing! We can use the `use` keyword to make it shorter:
|
||||
|
||||
```rust
|
||||
use StringResult::StringOK;
|
||||
use StringResult::ErrorReason;
|
||||
|
||||
enum StringResult {
|
||||
StringOK(String),
|
||||
ErrorReason(String),
|
||||
}
|
||||
|
||||
# fn main() {}
|
||||
|
||||
fn respond(greeting: &str) -> StringResult {
|
||||
if greeting == "Hello" {
|
||||
StringOK("Good morning!".to_string())
|
||||
} else {
|
||||
ErrorReason("I didn't understand you!".to_string())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`use` declarations must come before anything else, which looks a little strange in this example,
|
||||
since we `use` the variants before we define them. Anyway, in the body of `respond`, we can just
|
||||
say `StringOK` now, rather than the full `StringResult::StringOK`. Importing variants can be
|
||||
convenient, but can also cause name conflicts, so do this with caution. It's considered good style
|
||||
to rarely import variants for this reason.
|
||||
|
||||
As you can see, `enum`s with values are quite a powerful tool for data representation,
|
||||
and can be even more useful when they're generic across types. Before we get to generics,
|
||||
though, let's talk about how to use them with pattern matching, a tool that will
|
||||
let us deconstruct this sum type (the type theory term for enums) in a very elegant
|
||||
way and avoid all these messy `if`/`else`s.
|
|
@ -0,0 +1,10 @@
|
|||
% Conclusion
|
||||
|
||||
We covered a lot of ground here. When you've mastered everything in this Guide,
|
||||
you will have a firm grasp of basic Rust development. There's a whole lot more
|
||||
out there, we've just covered the surface. There's tons of topics that you can
|
||||
dig deeper into, and we've built specialized guides for many of them. To learn
|
||||
more, dig into the [full documentation
|
||||
index](index.html).
|
||||
|
||||
Happy hacking!
|
|
@ -0,0 +1,146 @@
|
|||
# Functions
|
||||
|
||||
You've already seen one function so far, the `main` function:
|
||||
|
||||
```{rust}
|
||||
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
|
||||
this function takes no arguments, and then some curly braces to indicate the
|
||||
body. Here's a function named `foo`:
|
||||
|
||||
```{rust}
|
||||
fn foo() {
|
||||
}
|
||||
```
|
||||
|
||||
So, what about taking arguments? Here's a function that prints a number:
|
||||
|
||||
```{rust}
|
||||
fn print_number(x: i32) {
|
||||
println!("x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
Here's a complete program that uses `print_number`:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
print_number(5);
|
||||
}
|
||||
|
||||
fn print_number(x: i32) {
|
||||
println!("x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
print_sum(5, 6);
|
||||
}
|
||||
|
||||
fn print_sum(x: i32, y: i32) {
|
||||
println!("sum is: {}", x + y);
|
||||
}
|
||||
```
|
||||
|
||||
You separate arguments with a comma, both when you call the function, as well
|
||||
as when you declare it.
|
||||
|
||||
Unlike `let`, you _must_ declare the types of function arguments. This does
|
||||
not work:
|
||||
|
||||
```{ignore}
|
||||
fn print_number(x, y) {
|
||||
println!("x is: {}", x + y);
|
||||
}
|
||||
```
|
||||
|
||||
You get this error:
|
||||
|
||||
```text
|
||||
hello.rs:5:18: 5:19 error: expected `:` but found `,`
|
||||
hello.rs:5 fn print_number(x, y) {
|
||||
```
|
||||
|
||||
This is a deliberate design decision. While full-program inference is possible,
|
||||
languages which have it, like Haskell, often suggest that documenting your
|
||||
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:
|
||||
|
||||
```{rust}
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
Rust functions return exactly one value, and you declare the type after an
|
||||
'arrow', which is a dash (`-`) followed by a greater-than sign (`>`).
|
||||
|
||||
You'll note the lack of a semicolon here. If we added it in:
|
||||
|
||||
```{ignore}
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1;
|
||||
}
|
||||
```
|
||||
|
||||
We would get an error:
|
||||
|
||||
```text
|
||||
error: not all control paths return a value
|
||||
fn add_one(x: i32) -> i32 {
|
||||
x + 1;
|
||||
}
|
||||
|
||||
help: consider removing this semicolon:
|
||||
x + 1;
|
||||
^
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
But what about early returns? Rust does have a keyword for that, `return`:
|
||||
|
||||
```{rust}
|
||||
fn foo(x: i32) -> i32 {
|
||||
if x < 5 { return x; }
|
||||
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
Using a `return` as the last line of a function works, but is considered poor
|
||||
style:
|
||||
|
||||
```{rust}
|
||||
fn foo(x: i32) -> i32 {
|
||||
if x < 5 { return x; }
|
||||
|
||||
return x + 1;
|
||||
}
|
||||
```
|
||||
|
||||
There are some additional ways to define functions, but they involve features
|
||||
that we haven't learned about yet, so let's just leave it at that for now.
|
|
@ -0,0 +1,177 @@
|
|||
% Generics
|
||||
|
||||
Sometimes, when writing a function or data type, we may want it to work for
|
||||
multiple types of arguments. For example, remember our `OptionalInt` type?
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(int),
|
||||
Missing,
|
||||
}
|
||||
```
|
||||
|
||||
If we wanted to also have an `OptionalFloat64`, we would need a new enum:
|
||||
|
||||
```{rust}
|
||||
enum OptionalFloat64 {
|
||||
Valuef64(f64),
|
||||
Missingf64,
|
||||
}
|
||||
```
|
||||
|
||||
This is really unfortunate. Luckily, Rust has a feature that gives us a better
|
||||
way: generics. Generics are called **parametric polymorphism** in type theory,
|
||||
which means that they are types or functions that have multiple forms ("poly"
|
||||
is multiple, "morph" is form) over a given parameter ("parametric").
|
||||
|
||||
Anyway, enough with type theory declarations, let's check out the generic form
|
||||
of `OptionalInt`. It is actually provided by Rust itself, and looks like this:
|
||||
|
||||
```rust
|
||||
enum Option<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
```
|
||||
|
||||
The `<T>` part, which you've seen a few times before, indicates that this is
|
||||
a generic data type. Inside the declaration of our enum, wherever we see a `T`,
|
||||
we substitute that type for the same type used in the generic. Here's an
|
||||
example of using `Option<T>`, with some extra type annotations:
|
||||
|
||||
```{rust}
|
||||
let x: Option<int> = Some(5i);
|
||||
```
|
||||
|
||||
In the type declaration, we say `Option<int>`. Note how similar this looks to
|
||||
`Option<T>`. So, in this particular `Option`, `T` has the value of `int`. On
|
||||
the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5i`.
|
||||
Since that's an `int`, the two sides match, and Rust is happy. If they didn't
|
||||
match, we'd get an error:
|
||||
|
||||
```{rust,ignore}
|
||||
let x: Option<f64> = Some(5i);
|
||||
// error: mismatched types: expected `core::option::Option<f64>`
|
||||
// but found `core::option::Option<int>` (expected f64 but found int)
|
||||
```
|
||||
|
||||
That doesn't mean we can't make `Option<T>`s that hold an `f64`! They just have to
|
||||
match up:
|
||||
|
||||
```{rust}
|
||||
let x: Option<int> = Some(5i);
|
||||
let y: Option<f64> = Some(5.0f64);
|
||||
```
|
||||
|
||||
This is just fine. One definition, multiple uses.
|
||||
|
||||
Generics don't have to only be generic over one type. Consider Rust's built-in
|
||||
`Result<T, E>` type:
|
||||
|
||||
```{rust}
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
}
|
||||
```
|
||||
|
||||
This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
|
||||
can be any letter you'd like. We could define `Result<T, E>` as:
|
||||
|
||||
```{rust}
|
||||
enum Result<H, N> {
|
||||
Ok(H),
|
||||
Err(N),
|
||||
}
|
||||
```
|
||||
|
||||
if we wanted to. Convention says that the first generic parameter should be
|
||||
`T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however.
|
||||
|
||||
The `Result<T, E>` type is intended to
|
||||
be used to return the result of a computation, and to have the ability to
|
||||
return an error if it didn't work out. Here's an example:
|
||||
|
||||
```{rust}
|
||||
let x: Result<f64, String> = Ok(2.3f64);
|
||||
let y: Result<f64, String> = Err("There was an error.".to_string());
|
||||
```
|
||||
|
||||
This particular Result will return an `f64` if there's a success, and a
|
||||
`String` if there's a failure. Let's write a function that uses `Result<T, E>`:
|
||||
|
||||
```{rust}
|
||||
fn inverse(x: f64) -> Result<f64, String> {
|
||||
if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0f64 / x)
|
||||
}
|
||||
```
|
||||
|
||||
We don't want to take the inverse of zero, so we check to make sure that we
|
||||
weren't passed zero. If we were, then we return an `Err`, with a message. If
|
||||
it's okay, we return an `Ok`, with the answer.
|
||||
|
||||
Why does this matter? Well, remember how `match` does exhaustive matches?
|
||||
Here's how this function gets used:
|
||||
|
||||
```{rust}
|
||||
# fn inverse(x: f64) -> Result<f64, String> {
|
||||
# if x == 0.0f64 { return Err("x cannot be zero!".to_string()); }
|
||||
# Ok(1.0f64 / x)
|
||||
# }
|
||||
let x = inverse(25.0f64);
|
||||
|
||||
match x {
|
||||
Ok(x) => println!("The inverse of 25 is {}", x),
|
||||
Err(msg) => println!("Error: {}", msg),
|
||||
}
|
||||
```
|
||||
|
||||
The `match` enforces that we handle the `Err` case. In addition, because the
|
||||
answer is wrapped up in an `Ok`, we can't just use the result without doing
|
||||
the match:
|
||||
|
||||
```{rust,ignore}
|
||||
let x = inverse(25.0f64);
|
||||
println!("{}", x + 2.0f64); // error: binary operation `+` cannot be applied
|
||||
// to type `core::result::Result<f64,collections::string::String>`
|
||||
```
|
||||
|
||||
This function is great, but there's one other problem: it only works for 64 bit
|
||||
floating point values. What if we wanted to handle 32 bit floating point as
|
||||
well? We'd have to write this:
|
||||
|
||||
```{rust}
|
||||
fn inverse32(x: f32) -> Result<f32, String> {
|
||||
if x == 0.0f32 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0f32 / x)
|
||||
}
|
||||
```
|
||||
|
||||
Bummer. What we need is a **generic function**. Luckily, we can write one!
|
||||
However, it won't _quite_ work yet. Before we get into that, let's talk syntax.
|
||||
A generic version of `inverse` would look something like this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn inverse<T>(x: T) -> Result<T, String> {
|
||||
if x == 0.0 { return Err("x cannot be zero!".to_string()); }
|
||||
|
||||
Ok(1.0 / x)
|
||||
}
|
||||
```
|
||||
|
||||
Just like how we had `Option<T>`, we use a similar syntax for `inverse<T>`.
|
||||
We can then use `T` inside the rest of the signature: `x` has type `T`, and half
|
||||
of the `Result` has type `T`. However, if we try to compile that example, we'll get
|
||||
an error:
|
||||
|
||||
```text
|
||||
error: binary operation `==` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
Because `T` can be _any_ type, it may be a type that doesn't implement `==`,
|
||||
and therefore, the first line would be wrong. What do we do?
|
||||
|
||||
To fix this example, we need to learn about another Rust feature: traits.
|
|
@ -0,0 +1,891 @@
|
|||
# Guessing Game
|
||||
|
||||
Okay! We've got the basics of Rust down. Let's write a bigger program.
|
||||
|
||||
For our first project, we'll implement a classic beginner programming problem:
|
||||
the guessing game. Here's how it works: Our program will generate a random
|
||||
integer between one and a hundred. It will then prompt us to enter a guess.
|
||||
Upon entering our guess, it will tell us if we're too low or too high. Once we
|
||||
guess correctly, it will congratulate us. Sound good?
|
||||
|
||||
## Set up
|
||||
|
||||
Let's set up a new project. Go to your projects directory. Remember how we
|
||||
had to create our directory structure and a `Cargo.toml` for `hello_world`? Cargo
|
||||
has a command that does that for us. Let's give it a shot:
|
||||
|
||||
```{bash}
|
||||
$ cd ~/projects
|
||||
$ cargo new guessing_game --bin
|
||||
$ cd guessing_game
|
||||
```
|
||||
|
||||
We pass the name of our project to `cargo new`, and then the `--bin` flag,
|
||||
since we're making a binary, rather than a library.
|
||||
|
||||
Check out the generated `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
|
||||
name = "guessing_game"
|
||||
version = "0.0.1"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
```
|
||||
|
||||
Cargo gets this information from your environment. If it's not correct, go ahead
|
||||
and fix that.
|
||||
|
||||
Finally, Cargo generated a "Hello, world!" for us. Check out `src/main.rs`:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
println!("Hello, world!")
|
||||
}
|
||||
```
|
||||
|
||||
Let's try compiling what Cargo gave us:
|
||||
|
||||
```{bash}
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
```
|
||||
|
||||
Excellent! Open up your `src/main.rs` again. We'll be writing all of
|
||||
our code in this file. We'll talk about multiple-file projects later on in the
|
||||
guide.
|
||||
|
||||
Before we move on, let me show you one more Cargo command: `run`. `cargo run`
|
||||
is kind of like `cargo build`, but it also then runs the produced executable.
|
||||
Try it out:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Great! The `run` command comes in handy when you need to rapidly iterate on a project.
|
||||
Our game is just such a project, we need to quickly test each iteration before moving on to the next one.
|
||||
|
||||
## Processing a Guess
|
||||
|
||||
Let's get to it! The first thing we need to do for our guessing game is
|
||||
allow our player to input a guess. Put this in your `src/main.rs`:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
You've seen this code before, when we talked about standard input. We
|
||||
import the `std::io` module with `use`, and then our `main` function contains
|
||||
our program's logic. We print a little message announcing the game, ask the
|
||||
user to input a guess, get their input, and then print it out.
|
||||
|
||||
Because we talked about this in the section on standard I/O, I won't go into
|
||||
more details here. If you need a refresher, go re-read that section.
|
||||
|
||||
## Generating a secret number
|
||||
|
||||
Next, we need to generate a secret number. To do that, we need to use Rust's
|
||||
random number generation, which we haven't talked about yet. Rust includes a
|
||||
bunch of interesting functions in its standard library. If you need a bit of
|
||||
code, it's possible that it's already been written for you! In this case,
|
||||
we do know that Rust has random number generation, but we don't know how to
|
||||
use it.
|
||||
|
||||
Enter the docs. Rust has a page specifically to document the standard library.
|
||||
You can find that page [here](std/index.html). There's a lot of information on
|
||||
that page, but the best part is the search bar. Right up at the top, there's
|
||||
a box that you can enter in a search term. The search is pretty primitive
|
||||
right now, but is getting better all the time. If you type 'random' in that
|
||||
box, the page will update to [this one](std/index.html?search=random). The very
|
||||
first result is a link to [`std::rand::random`](std/rand/fn.random.html). If we
|
||||
click on that result, we'll be taken to its documentation page.
|
||||
|
||||
This page shows us a few things: the type signature of the function, some
|
||||
explanatory text, and then an example. Let's try to modify our code to add in the
|
||||
`random` function and see what happens:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random() % 100) + 1; // secret_number: i32
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
The first thing we changed was to `use std::rand`, as the docs
|
||||
explained. We then added in a `let` expression to create a variable binding
|
||||
named `secret_number`, and we printed out its result.
|
||||
|
||||
Also, you may wonder why we are using `%` on the result of `rand::random()`.
|
||||
This operator is called 'modulo', and it returns the remainder of a division.
|
||||
By taking the modulo of the result of `rand::random()`, we're limiting the
|
||||
values to be between 0 and 99. Then, we add one to the result, making it from 1
|
||||
to 100. Using modulo can give you a very, very small bias in the result, but
|
||||
for this example, it is not important.
|
||||
|
||||
Let's try to compile this using `cargo build`:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:7:26: 7:34 error: the type of this value must be known in this context
|
||||
src/main.rs:7 let secret_number = (rand::random() % 100) + 1;
|
||||
^~~~~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
It didn't work! Rust says "the type of this value must be known in this
|
||||
context." What's up with that? Well, as it turns out, `rand::random()` can
|
||||
generate many kinds of random values, not just integers. And in this case, Rust
|
||||
isn't sure what kind of value `random()` should generate. So we have to help
|
||||
it. With number literals, we can just add an `i32` onto the end to tell Rust they're
|
||||
integers, but that does not work with functions. There's a different syntax,
|
||||
and it looks like this:
|
||||
|
||||
```{rust,ignore}
|
||||
rand::random::<i32>();
|
||||
```
|
||||
|
||||
This says "please give me a random `i32` value." We can change our code to use
|
||||
this hint:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<i32>() % 100) + 1;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
Try running our new program a few times:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
Please input your guess.
|
||||
4
|
||||
You guessed: 4
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: 83
|
||||
Please input your guess.
|
||||
5
|
||||
You guessed: 5
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: -29
|
||||
Please input your guess.
|
||||
42
|
||||
You guessed: 42
|
||||
```
|
||||
|
||||
Wait. Negative 29? We wanted a number between one and a hundred! We have two
|
||||
options here: we can either ask `random()` to generate an unsigned integer, which
|
||||
can only be positive, or we can use the `abs()` function. Let's go with the
|
||||
unsigned integer approach. If we want a random positive number, we should ask for
|
||||
a random positive number. Our code looks like this now:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
}
|
||||
```
|
||||
|
||||
And trying it out:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 57
|
||||
Please input your guess.
|
||||
3
|
||||
You guessed: 3
|
||||
```
|
||||
|
||||
Great! Next up: let's compare our guess to the secret guess.
|
||||
|
||||
## Comparing guesses
|
||||
|
||||
If you remember, earlier in the guide, we made a `cmp` function that compared
|
||||
two numbers. Let's add that in, along with a `match` statement to compare our
|
||||
guess to the secret number:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
|
||||
match cmp(input, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
If we try to compile, we'll get some errors:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:20:15: 20:20 error: mismatched types: expected `i32` but found `collections::string::String` (expected i32 but found struct collections::string::String)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~
|
||||
src/main.rs:20:22: 20:35 error: mismatched types: expected `i32` but found `uint` (expected i32 but found uint)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~~~~~~~~~
|
||||
error: aborting due to 2 previous errors
|
||||
```
|
||||
|
||||
This often happens when writing Rust programs, and is one of Rust's greatest
|
||||
strengths. You try out some code, see if it compiles, and Rust tells you that
|
||||
you've done something wrong. In this case, our `cmp` function works on integers,
|
||||
but we've given it unsigned integers. In this case, the fix is easy, because
|
||||
we wrote the `cmp` function! Let's change it to take `uint`s:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
|
||||
|
||||
println!("You guessed: {}", input);
|
||||
|
||||
match cmp(input, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
And try compiling again:
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:20:15: 20:20 error: mismatched types: expected `uint` but found `collections::string::String` (expected uint but found struct collections::string::String)
|
||||
src/main.rs:20 match cmp(input, secret_number) {
|
||||
^~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
This error is similar to the last one: we expected to get a `uint`, but we got
|
||||
a `String` instead! That's because our `input` variable is coming from the
|
||||
standard input, and you can guess anything. Try it:
|
||||
|
||||
```bash
|
||||
$ ./target/guessing_game
|
||||
Guess the number!
|
||||
The secret number is: 73
|
||||
Please input your guess.
|
||||
hello
|
||||
You guessed: hello
|
||||
```
|
||||
|
||||
Oops! Also, you'll note that we just ran our program even though it didn't compile.
|
||||
This works because the older version we did successfully compile was still lying
|
||||
around. Gotta be careful!
|
||||
|
||||
Anyway, we have a `String`, but we need a `uint`. What to do? Well, there's
|
||||
a function for that:
|
||||
|
||||
```{rust,ignore}
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.parse();
|
||||
```
|
||||
|
||||
The `parse` function takes in a `&str` value and converts it into something.
|
||||
We tell it what kind of something with a type hint. Remember our type hint with
|
||||
`random()`? It looked like this:
|
||||
|
||||
```{rust,ignore}
|
||||
rand::random::<uint>();
|
||||
```
|
||||
|
||||
There's an alternate way of providing a hint too, and that's declaring the type
|
||||
in a `let`:
|
||||
|
||||
```{rust,ignore}
|
||||
let x: uint = rand::random();
|
||||
```
|
||||
|
||||
In this case, we say `x` is a `uint` explicitly, so Rust is able to properly
|
||||
tell `random()` what to generate. In a similar fashion, both of these work:
|
||||
|
||||
```{rust,ignore}
|
||||
let input_num = "5".parse::<uint>(); // input_num: Option<uint>
|
||||
let input_num: Option<uint> = "5".parse(); // input_num: Option<uint>
|
||||
```
|
||||
|
||||
Anyway, with us now converting our input to a number, our code looks like this:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.parse();
|
||||
|
||||
println!("You guessed: {}", input_num);
|
||||
|
||||
match cmp(input_num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Let's try it out!
|
||||
|
||||
```bash
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
src/main.rs:22:15: 22:24 error: mismatched types: expected `uint` but found `core::option::Option<uint>` (expected uint but found enum core::option::Option)
|
||||
src/main.rs:22 match cmp(input_num, secret_number) {
|
||||
^~~~~~~~~
|
||||
error: aborting due to previous error
|
||||
```
|
||||
|
||||
Oh yeah! Our `input_num` has the type `Option<uint>`, rather than `uint`. We
|
||||
need to unwrap the Option. If you remember from before, `match` is a great way
|
||||
to do that. Try this code:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
We use a `match` to either give us the `uint` inside of the `Option`, or else
|
||||
print an error message and return. Let's give this a shot:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 17
|
||||
Please input your guess.
|
||||
5
|
||||
Please input a number!
|
||||
```
|
||||
|
||||
Uh, what? But we did!
|
||||
|
||||
... actually, we didn't. See, when you get a line of input from `stdin()`,
|
||||
you get all the input. Including the `\n` character from you pressing Enter.
|
||||
Therefore, `parse()` sees the string `"5\n"` and says "nope, that's not a
|
||||
number; there's non-number stuff in there!" Luckily for us, `&str`s have an easy
|
||||
method we can use defined on them: `trim()`. One small modification, and our
|
||||
code looks like this:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Let's try it!
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
Please input your guess.
|
||||
76
|
||||
You guessed: 76
|
||||
Too big!
|
||||
```
|
||||
|
||||
Nice! You can see I even added spaces before my guess, and it still figured
|
||||
out that I guessed 76. Run the program a few times, and verify that guessing
|
||||
the number works, as well as guessing a number too small.
|
||||
|
||||
The Rust compiler helped us out quite a bit there! This technique is called
|
||||
"lean on the compiler", and it's often useful when working on some code. Let
|
||||
the error messages help guide you towards the correct types.
|
||||
|
||||
Now we've got most of the game working, but we can only make one guess. Let's
|
||||
change that by adding loops!
|
||||
|
||||
## Looping
|
||||
|
||||
As we already discussed, the `loop` keyword gives us an infinite loop.
|
||||
Let's add that in:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
And try it out. But wait, didn't we just add an infinite loop? Yup. Remember
|
||||
that `return`? If we give a non-number answer, we'll `return` and quit. Observe:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 59
|
||||
Please input your guess.
|
||||
45
|
||||
You guessed: 45
|
||||
Too small!
|
||||
Please input your guess.
|
||||
60
|
||||
You guessed: 60
|
||||
Too big!
|
||||
Please input your guess.
|
||||
59
|
||||
You guessed: 59
|
||||
You win!
|
||||
Please input your guess.
|
||||
quit
|
||||
Please input a number!
|
||||
```
|
||||
|
||||
Ha! `quit` actually quits. As does any other non-number input. Well, this is
|
||||
suboptimal to say the least. First, let's actually quit when you win the game:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
By adding the `return` line after the `You win!`, we'll exit the program when
|
||||
we win. We have just one more tweak to make: when someone inputs a non-number,
|
||||
we don't want to quit, we just want to ignore it. Change that `return` to
|
||||
`continue`:
|
||||
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
println!("The secret number is: {}", secret_number);
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
Now we should be good! Let's try:
|
||||
|
||||
```bash
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 61
|
||||
Please input your guess.
|
||||
10
|
||||
You guessed: 10
|
||||
Too small!
|
||||
Please input your guess.
|
||||
99
|
||||
You guessed: 99
|
||||
Too big!
|
||||
Please input your guess.
|
||||
foo
|
||||
Please input a number!
|
||||
Please input your guess.
|
||||
61
|
||||
You guessed: 61
|
||||
You win!
|
||||
```
|
||||
|
||||
Awesome! With one tiny last tweak, we have finished the guessing game. Can you
|
||||
think of what it is? That's right, we don't want to print out the secret number.
|
||||
It was good for testing, but it kind of ruins the game. Here's our final source:
|
||||
|
||||
```{rust,no_run}
|
||||
use std::io;
|
||||
use std::rand;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn main() {
|
||||
println!("Guess the number!");
|
||||
|
||||
let secret_number = (rand::random::<uint>() % 100u) + 1u;
|
||||
|
||||
loop {
|
||||
|
||||
println!("Please input your guess.");
|
||||
|
||||
let input = io::stdin().read_line()
|
||||
.ok()
|
||||
.expect("Failed to read line");
|
||||
let input_num: Option<uint> = input.trim().parse();
|
||||
|
||||
let num = match input_num {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
println!("Please input a number!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
println!("You guessed: {}", num);
|
||||
|
||||
match cmp(num, secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(a: uint, b: uint) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
```
|
||||
|
||||
## Complete!
|
||||
|
||||
At this point, you have successfully built the Guessing Game! Congratulations!
|
||||
|
||||
You've now learned the basic syntax of Rust. All of this is relatively close to
|
||||
various other programming languages you have used in the past. These
|
||||
fundamental syntactical and semantic elements will form the foundation for the
|
||||
rest of your Rust education.
|
||||
|
||||
Now that you're an expert at the basics, it's time to learn about some of
|
||||
Rust's more unique features.
|
|
@ -0,0 +1,565 @@
|
|||
% The Rust References and Lifetimes Guide
|
||||
|
||||
# Introduction
|
||||
|
||||
References are one of the more flexible and powerful tools available in
|
||||
Rust. They can point anywhere: into the heap, stack, and even into the
|
||||
interior of another data structure. A reference is as flexible as a C pointer
|
||||
or C++ reference.
|
||||
|
||||
Unlike C and C++ compilers, the Rust compiler includes special static
|
||||
checks that ensure that programs use references safely.
|
||||
|
||||
Despite their complete safety, a reference's representation at runtime
|
||||
is the same as that of an ordinary pointer in a C program. They introduce zero
|
||||
overhead. The compiler does all safety checks at compile time.
|
||||
|
||||
Although references have rather elaborate theoretical underpinnings
|
||||
(e.g. region pointers), the core concepts will be familiar to anyone
|
||||
who has worked with C or C++. The best way to explain how they are
|
||||
used—and their limitations—is probably just to work through several examples.
|
||||
|
||||
# By example
|
||||
|
||||
References, sometimes known as *borrowed pointers*, are only valid for
|
||||
a limited duration. References never claim any kind of ownership
|
||||
over the data that they point to. Instead, they are used for cases
|
||||
where you would like to use data for a short time.
|
||||
|
||||
Consider a simple struct type `Point`:
|
||||
|
||||
~~~
|
||||
struct Point {x: f64, y: f64}
|
||||
~~~
|
||||
|
||||
We can use this simple definition to allocate points in many different ways. For
|
||||
example, in this code, each of these local variables contains a point,
|
||||
but allocated in a different place:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
let on_the_stack : Point = Point {x: 3.0, y: 4.0};
|
||||
let on_the_heap : Box<Point> = box Point {x: 7.0, y: 9.0};
|
||||
~~~
|
||||
|
||||
Suppose we wanted to write a procedure that computed the distance between any
|
||||
two points, no matter where they were stored. One option is to define a function
|
||||
that takes two arguments of type `Point`—that is, it takes the points by value.
|
||||
But if we define it this way, calling the function will cause the points to be
|
||||
copied. For points, this is probably not so bad, but often copies are
|
||||
expensive. So we'd like to define a function that takes the points just as
|
||||
a reference.
|
||||
|
||||
~~~
|
||||
# use std::num::Float;
|
||||
# struct Point {x: f64, y: f64}
|
||||
# fn sqrt(f: f64) -> f64 { 0.0 }
|
||||
fn compute_distance(p1: &Point, p2: &Point) -> f64 {
|
||||
let x_d = p1.x - p2.x;
|
||||
let y_d = p1.y - p2.y;
|
||||
(x_d * x_d + y_d * y_d).sqrt()
|
||||
}
|
||||
~~~
|
||||
|
||||
Now we can call `compute_distance()`:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
# let on_the_stack : Point = Point{x: 3.0, y: 4.0};
|
||||
# let on_the_heap : Box<Point> = box Point{x: 7.0, y: 9.0};
|
||||
# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 }
|
||||
compute_distance(&on_the_stack, &*on_the_heap);
|
||||
~~~
|
||||
|
||||
Here, the `&` operator takes the address of the variable
|
||||
`on_the_stack`; this is because `on_the_stack` has the type `Point`
|
||||
(that is, a struct value) and we have to take its address to get a
|
||||
value. We also call this _borrowing_ the local variable
|
||||
`on_the_stack`, because we have created an alias: that is, another
|
||||
name for the same data.
|
||||
|
||||
Likewise, in the case of `on_the_heap`,
|
||||
the `&` operator is used in conjunction with the `*` operator
|
||||
to take a reference to the contents of the box.
|
||||
|
||||
Whenever a caller lends data to a callee, there are some limitations on what
|
||||
the caller can do with the original. For example, if the contents of a
|
||||
variable have been lent out, you cannot send that variable to another task. In
|
||||
addition, the compiler will reject any code that might cause the borrowed
|
||||
value to be freed or overwrite its component fields with values of different
|
||||
types (I'll get into what kinds of actions those are shortly). This rule
|
||||
should make intuitive sense: you must wait for a borrower to return the value
|
||||
that you lent it (that is, wait for the reference to go out of scope)
|
||||
before you can make full use of it again.
|
||||
|
||||
# Other uses for the & operator
|
||||
|
||||
In the previous example, the value `on_the_stack` was defined like so:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
let on_the_stack: Point = Point {x: 3.0, y: 4.0};
|
||||
~~~
|
||||
|
||||
This declaration means that code can only pass `Point` by value to other
|
||||
functions. As a consequence, we had to explicitly take the address of
|
||||
`on_the_stack` to get a reference. Sometimes however it is more
|
||||
convenient to move the & operator into the definition of `on_the_stack`:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
let on_the_stack2: &Point = &Point {x: 3.0, y: 4.0};
|
||||
~~~
|
||||
|
||||
Applying `&` to an rvalue (non-assignable location) is just a convenient
|
||||
shorthand for creating a temporary and taking its address. A more verbose
|
||||
way to write the same code is:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
let tmp = Point {x: 3.0, y: 4.0};
|
||||
let on_the_stack2 : &Point = &tmp;
|
||||
~~~
|
||||
|
||||
# Taking the address of fields
|
||||
|
||||
The `&` operator is not limited to taking the address of
|
||||
local variables. It can also take the address of fields or
|
||||
individual array elements. For example, consider this type definition
|
||||
for `Rectangle`:
|
||||
|
||||
~~~
|
||||
struct Point {x: f64, y: f64} // as before
|
||||
struct Size {w: f64, h: f64} // as before
|
||||
struct Rectangle {origin: Point, size: Size}
|
||||
~~~
|
||||
|
||||
Now, as before, we can define rectangles in a few different ways:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}
|
||||
# struct Size {w: f64, h: f64} // as before
|
||||
# struct Rectangle {origin: Point, size: Size}
|
||||
let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0},
|
||||
size: Size {w: 3.0, h: 4.0}};
|
||||
let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0},
|
||||
size: Size {w: 3.0, h: 4.0}};
|
||||
~~~
|
||||
|
||||
In each case, we can extract out individual subcomponents with the `&`
|
||||
operator. For example, I could write:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64} // as before
|
||||
# struct Size {w: f64, h: f64} // as before
|
||||
# struct Rectangle {origin: Point, size: Size}
|
||||
# let rect_stack = &Rectangle {origin: Point {x: 1.0, y: 2.0}, size: Size {w: 3.0, h: 4.0}};
|
||||
# let rect_heap = box Rectangle {origin: Point {x: 5.0, y: 6.0}, size: Size {w: 3.0, h: 4.0}};
|
||||
# fn compute_distance(p1: &Point, p2: &Point) -> f64 { 0.0 }
|
||||
compute_distance(&rect_stack.origin, &rect_heap.origin);
|
||||
~~~
|
||||
|
||||
which would borrow the field `origin` from the rectangle on the stack
|
||||
as well as from the owned box, and then compute the distance between them.
|
||||
|
||||
# Lifetimes
|
||||
|
||||
We’ve seen a few examples of borrowing data. To this point, we’ve glossed
|
||||
over issues of safety. As stated in the introduction, at runtime a reference
|
||||
is simply a pointer, nothing more. Therefore, avoiding C's problems with
|
||||
dangling pointers requires a compile-time safety check.
|
||||
|
||||
The basis for the check is the notion of _lifetimes_. A lifetime is a
|
||||
static approximation of the span of execution during which the pointer
|
||||
is valid: it always corresponds to some expression or block within the
|
||||
program.
|
||||
|
||||
The compiler will only allow a borrow *if it can guarantee that the data will
|
||||
not be reassigned or moved for the lifetime of the pointer*. This does not
|
||||
necessarily mean that the data is stored in immutable memory. For example,
|
||||
the following function is legal:
|
||||
|
||||
~~~
|
||||
# fn some_condition() -> bool { true }
|
||||
# struct Foo { f: int }
|
||||
fn example3() -> int {
|
||||
let mut x = box Foo {f: 3};
|
||||
if some_condition() {
|
||||
let y = &x.f; // -+ L
|
||||
return *y; // |
|
||||
} // -+
|
||||
x = box Foo {f: 4};
|
||||
// ...
|
||||
# return 0;
|
||||
}
|
||||
~~~
|
||||
|
||||
Here, the interior of the variable `x` is being borrowed
|
||||
and `x` is declared as mutable. However, the compiler can prove that
|
||||
`x` is not assigned anywhere in the lifetime L of the variable
|
||||
`y`. Therefore, it accepts the function, even though `x` is mutable
|
||||
and in fact is mutated later in the function.
|
||||
|
||||
It may not be clear why we are so concerned about mutating a borrowed
|
||||
variable. The reason is that the runtime system frees any box
|
||||
_as soon as its owning reference changes or goes out of
|
||||
scope_. Therefore, a program like this is illegal (and would be
|
||||
rejected by the compiler):
|
||||
|
||||
~~~ {.ignore}
|
||||
fn example3() -> int {
|
||||
let mut x = box X {f: 3};
|
||||
let y = &x.f;
|
||||
x = box X {f: 4}; // Error reported here.
|
||||
*y
|
||||
}
|
||||
~~~
|
||||
|
||||
To make this clearer, consider this diagram showing the state of
|
||||
memory immediately before the re-assignment of `x`:
|
||||
|
||||
~~~ {.text}
|
||||
Stack Exchange Heap
|
||||
|
||||
x +-------------+
|
||||
| box {f:int} | ----+
|
||||
y +-------------+ |
|
||||
| &int | ----+
|
||||
+-------------+ | +---------+
|
||||
+--> | f: 3 |
|
||||
+---------+
|
||||
~~~
|
||||
|
||||
Once the reassignment occurs, the memory will look like this:
|
||||
|
||||
~~~ {.text}
|
||||
Stack Exchange Heap
|
||||
|
||||
x +-------------+ +---------+
|
||||
| box {f:int} | -------> | f: 4 |
|
||||
y +-------------+ +---------+
|
||||
| &int | ----+
|
||||
+-------------+ | +---------+
|
||||
+--> | (freed) |
|
||||
+---------+
|
||||
~~~
|
||||
|
||||
Here you can see that the variable `y` still points at the old `f`
|
||||
property of Foo, which has been freed.
|
||||
|
||||
In fact, the compiler can apply the same kind of reasoning to any
|
||||
memory that is (uniquely) owned by the stack frame. So we could
|
||||
modify the previous example to introduce additional owned pointers
|
||||
and structs, and the compiler will still be able to detect possible
|
||||
mutations. This time, we'll use an analogy to illustrate the concept.
|
||||
|
||||
~~~ {.ignore}
|
||||
fn example3() -> int {
|
||||
struct House { owner: Box<Person> }
|
||||
struct Person { age: int }
|
||||
|
||||
let mut house = box House {
|
||||
owner: box Person {age: 30}
|
||||
};
|
||||
|
||||
let owner_age = &house.owner.age;
|
||||
house = box House {owner: box Person {age: 40}}; // Error reported here.
|
||||
house.owner = box Person {age: 50}; // Error reported here.
|
||||
*owner_age
|
||||
}
|
||||
~~~
|
||||
|
||||
In this case, two errors are reported, one when the variable `house` is
|
||||
modified and another when `house.owner` is modified. Either modification would
|
||||
invalidate the pointer `owner_age`.
|
||||
|
||||
# Borrowing and enums
|
||||
|
||||
The previous example showed that the type system forbids any mutations
|
||||
of owned boxed values while they are being borrowed. In general, the type
|
||||
system also forbids borrowing a value as mutable if it is already being
|
||||
borrowed - either as a mutable reference or an immutable one. This restriction
|
||||
prevents pointers from pointing into freed memory. There is one other
|
||||
case where the compiler must be very careful to ensure that pointers
|
||||
remain valid: pointers into the interior of an `enum`.
|
||||
|
||||
Let’s look at the following `shape` type that can represent both rectangles
|
||||
and circles:
|
||||
|
||||
~~~
|
||||
struct Point {x: f64, y: f64}; // as before
|
||||
struct Size {w: f64, h: f64}; // as before
|
||||
enum Shape {
|
||||
Circle(Point, f64), // origin, radius
|
||||
Rectangle(Point, Size) // upper-left, dimensions
|
||||
}
|
||||
~~~
|
||||
|
||||
Now we might write a function to compute the area of a shape. This
|
||||
function takes a reference to a shape, to avoid the need for
|
||||
copying.
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}; // as before
|
||||
# struct Size {w: f64, h: f64}; // as before
|
||||
# enum Shape {
|
||||
# Circle(Point, f64), // origin, radius
|
||||
# Rectangle(Point, Size) // upper-left, dimensions
|
||||
# }
|
||||
fn compute_area(shape: &Shape) -> f64 {
|
||||
match *shape {
|
||||
Shape::Circle(_, radius) => std::f64::consts::PI * radius * radius,
|
||||
Shape::Rectangle(_, ref size) => size.w * size.h
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
The first case matches against circles. Here, the pattern extracts the
|
||||
radius from the shape variant and the action uses it to compute the
|
||||
area of the circle.
|
||||
|
||||
The second match is more interesting. Here we match against a
|
||||
rectangle and extract its size: but rather than copy the `size`
|
||||
struct, we use a by-reference binding to create a pointer to it. In
|
||||
other words, a pattern binding like `ref size` binds the name `size`
|
||||
to a pointer of type `&size` into the _interior of the enum_.
|
||||
|
||||
To make this more clear, let's look at a diagram of memory layout in
|
||||
the case where `shape` points at a rectangle:
|
||||
|
||||
~~~ {.text}
|
||||
Stack Memory
|
||||
|
||||
+-------+ +---------------+
|
||||
| shape | ------> | rectangle( |
|
||||
+-------+ | {x: f64, |
|
||||
| size | -+ | y: f64}, |
|
||||
+-------+ +----> | {w: f64, |
|
||||
| h: f64}) |
|
||||
+---------------+
|
||||
~~~
|
||||
|
||||
Here you can see that rectangular shapes are composed of five words of
|
||||
memory. The first is a tag indicating which variant this enum is
|
||||
(`rectangle`, in this case). The next two words are the `x` and `y`
|
||||
fields for the point and the remaining two are the `w` and `h` fields
|
||||
for the size. The binding `size` is then a pointer into the inside of
|
||||
the shape.
|
||||
|
||||
Perhaps you can see where the danger lies: if the shape were somehow
|
||||
to be reassigned, perhaps to a circle, then although the memory used
|
||||
to store that shape value would still be valid, _it would have a
|
||||
different type_! The following diagram shows what memory would look
|
||||
like if code overwrote `shape` with a circle:
|
||||
|
||||
~~~ {.text}
|
||||
Stack Memory
|
||||
|
||||
+-------+ +---------------+
|
||||
| shape | ------> | circle( |
|
||||
+-------+ | {x: f64, |
|
||||
| size | -+ | y: f64}, |
|
||||
+-------+ +----> | f64) |
|
||||
| |
|
||||
+---------------+
|
||||
~~~
|
||||
|
||||
As you can see, the `size` pointer would be pointing at a `f64`
|
||||
instead of a struct. This is not good: dereferencing the second field
|
||||
of a `f64` as if it were a struct with two fields would be a memory
|
||||
safety violation.
|
||||
|
||||
So, in fact, for every `ref` binding, the compiler will impose the
|
||||
same rules as the ones we saw for borrowing the interior of an owned
|
||||
box: it must be able to guarantee that the `enum` will not be
|
||||
overwritten for the duration of the borrow. In fact, the compiler
|
||||
would accept the example we gave earlier. The example is safe because
|
||||
the shape pointer has type `&Shape`, which means "reference to
|
||||
immutable memory containing a `shape`". If, however, the type of that
|
||||
pointer were `&mut Shape`, then the ref binding would be ill-typed.
|
||||
Just as with owned boxes, the compiler will permit `ref` bindings
|
||||
into data owned by the stack frame even if the data are mutable,
|
||||
but otherwise it requires that the data reside in immutable memory.
|
||||
|
||||
# Returning references
|
||||
|
||||
So far, all of the examples we have looked at, use references in a
|
||||
“downward” direction. That is, a method or code block creates a
|
||||
reference, then uses it within the same scope. It is also
|
||||
possible to return references as the result of a function, but
|
||||
as we'll see, doing so requires some explicit annotation.
|
||||
|
||||
We could write a subroutine like this:
|
||||
|
||||
~~~
|
||||
struct Point {x: f64, y: f64}
|
||||
fn get_x<'r>(p: &'r Point) -> &'r f64 { &p.x }
|
||||
~~~
|
||||
|
||||
Here, the function `get_x()` returns a pointer into the structure it
|
||||
was given. The type of the parameter (`&'r Point`) and return type
|
||||
(`&'r f64`) both use a new syntactic form that we have not seen so
|
||||
far. Here the identifier `r` names the lifetime of the pointer
|
||||
explicitly. So in effect, this function declares that it takes a
|
||||
pointer with lifetime `r` and returns a pointer with that same
|
||||
lifetime.
|
||||
|
||||
In general, it is only possible to return references if they
|
||||
are derived from a parameter to the procedure. In that case, the
|
||||
pointer result will always have the same lifetime as one of the
|
||||
parameters; named lifetimes indicate which parameter that
|
||||
is.
|
||||
|
||||
In the previous code samples, function parameter types did not include a
|
||||
lifetime name. The compiler simply creates a fresh name for the lifetime
|
||||
automatically: that is, the lifetime name is guaranteed to refer to a distinct
|
||||
lifetime from the lifetimes of all other parameters.
|
||||
|
||||
Named lifetimes that appear in function signatures are conceptually
|
||||
the same as the other lifetimes we have seen before, but they are a bit
|
||||
abstract: they don’t refer to a specific expression within `get_x()`,
|
||||
but rather to some expression within the *caller of `get_x()`*. The
|
||||
lifetime `r` is actually a kind of *lifetime parameter*: it is defined
|
||||
by the caller to `get_x()`, just as the value for the parameter `p` is
|
||||
defined by that caller.
|
||||
|
||||
In any case, whatever the lifetime of `r` is, the pointer produced by
|
||||
`&p.x` always has the same lifetime as `p` itself: a pointer to a
|
||||
field of a struct is valid as long as the struct is valid. Therefore,
|
||||
the compiler accepts the function `get_x()`.
|
||||
|
||||
In general, if you borrow a struct or box to create a
|
||||
reference, it will only be valid within the function
|
||||
and cannot be returned. This is why the typical way to return references
|
||||
is to take references as input (the only other case in
|
||||
which it can be legal to return a reference is if it
|
||||
points at a static constant).
|
||||
|
||||
# Named lifetimes
|
||||
|
||||
Lifetimes can be named and referenced. For example, the special lifetime
|
||||
`'static`, which does not go out of scope, can be used to create global
|
||||
variables and communicate between tasks (see the manual for use cases).
|
||||
|
||||
## Parameter Lifetimes
|
||||
|
||||
Named lifetimes allow for grouping of parameters by lifetime.
|
||||
For example, consider this function:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}; // as before
|
||||
# struct Size {w: f64, h: f64}; // as before
|
||||
# enum Shape {
|
||||
# Circle(Point, f64), // origin, radius
|
||||
# Rectangle(Point, Size) // upper-left, dimensions
|
||||
# }
|
||||
# fn compute_area(shape: &Shape) -> f64 { 0.0 }
|
||||
fn select<'r, T>(shape: &'r Shape, threshold: f64,
|
||||
a: &'r T, b: &'r T) -> &'r T {
|
||||
if compute_area(shape) > threshold {a} else {b}
|
||||
}
|
||||
~~~
|
||||
|
||||
This function takes three references and assigns each the same
|
||||
lifetime `r`. In practice, this means that, in the caller, the
|
||||
lifetime `r` will be the *intersection of the lifetime of the three
|
||||
region parameters*. This may be overly conservative, as in this
|
||||
example:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}; // as before
|
||||
# struct Size {w: f64, h: f64}; // as before
|
||||
# enum Shape {
|
||||
# Circle(Point, f64), // origin, radius
|
||||
# Rectangle(Point, Size) // upper-left, dimensions
|
||||
# }
|
||||
# fn compute_area(shape: &Shape) -> f64 { 0.0 }
|
||||
# fn select<'r, T>(shape: &Shape, threshold: f64,
|
||||
# a: &'r T, b: &'r T) -> &'r T {
|
||||
# if compute_area(shape) > threshold {a} else {b}
|
||||
# }
|
||||
// -+ r
|
||||
fn select_based_on_unit_circle<'r, T>( // |-+ B
|
||||
threshold: f64, a: &'r T, b: &'r T) -> &'r T { // | |
|
||||
// | |
|
||||
let shape = Shape::Circle(Point {x: 0., y: 0.}, 1.); // | |
|
||||
select(&shape, threshold, a, b) // | |
|
||||
} // |-+
|
||||
// -+
|
||||
~~~
|
||||
|
||||
In this call to `select()`, the lifetime of the first parameter shape
|
||||
is B, the function body. Both of the second two parameters `a` and `b`
|
||||
share the same lifetime, `r`, which is a lifetime parameter of
|
||||
`select_based_on_unit_circle()`. The caller will infer the
|
||||
intersection of these two lifetimes as the lifetime of the returned
|
||||
value, and hence the return value of `select()` will be assigned a
|
||||
lifetime of B. This will in turn lead to a compilation error, because
|
||||
`select_based_on_unit_circle()` is supposed to return a value with the
|
||||
lifetime `r`.
|
||||
|
||||
To address this, we can modify the definition of `select()` to
|
||||
distinguish the lifetime of the first parameter from the lifetime of
|
||||
the latter two. After all, the first parameter is not being
|
||||
returned. Here is how the new `select()` might look:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}; // as before
|
||||
# struct Size {w: f64, h: f64}; // as before
|
||||
# enum Shape {
|
||||
# Circle(Point, f64), // origin, radius
|
||||
# Rectangle(Point, Size) // upper-left, dimensions
|
||||
# }
|
||||
# fn compute_area(shape: &Shape) -> f64 { 0.0 }
|
||||
fn select<'r, 'tmp, T>(shape: &'tmp Shape, threshold: f64,
|
||||
a: &'r T, b: &'r T) -> &'r T {
|
||||
if compute_area(shape) > threshold {a} else {b}
|
||||
}
|
||||
~~~
|
||||
|
||||
Here you can see that `shape`'s lifetime is now named `tmp`. The
|
||||
parameters `a`, `b`, and the return value all have the lifetime `r`.
|
||||
However, since the lifetime `tmp` is not returned, it would be more
|
||||
concise to just omit the named lifetime for `shape` altogether:
|
||||
|
||||
~~~
|
||||
# struct Point {x: f64, y: f64}; // as before
|
||||
# struct Size {w: f64, h: f64}; // as before
|
||||
# enum Shape {
|
||||
# Circle(Point, f64), // origin, radius
|
||||
# Rectangle(Point, Size) // upper-left, dimensions
|
||||
# }
|
||||
# fn compute_area(shape: &Shape) -> f64 { 0.0 }
|
||||
fn select<'r, T>(shape: &Shape, threshold: f64,
|
||||
a: &'r T, b: &'r T) -> &'r T {
|
||||
if compute_area(shape) > threshold {a} else {b}
|
||||
}
|
||||
~~~
|
||||
|
||||
This is equivalent to the previous definition.
|
||||
|
||||
## Labeled Control Structures
|
||||
|
||||
Named lifetime notation can also be used to control the flow of execution:
|
||||
|
||||
~~~
|
||||
'h: for i in range(0u, 10) {
|
||||
'g: loop {
|
||||
if i % 2 == 0 { continue 'h; }
|
||||
if i == 9 { break 'h; }
|
||||
break 'g;
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
> *Note:* Labelled breaks are not currently supported within `while` loops.
|
||||
|
||||
Named labels are hygienic and can be used safely within macros.
|
||||
See the macros guide section on hygiene for more details.
|
||||
|
||||
# Conclusion
|
||||
|
||||
So there you have it: a (relatively) brief tour of the lifetime
|
||||
system. For more details, we refer to the (yet to be written) reference
|
||||
document on references, which will explain the full notation
|
||||
and give more examples.
|
|
@ -0,0 +1,108 @@
|
|||
# Hello, Cargo!
|
||||
|
||||
[Cargo](http://crates.io) is a tool that Rustaceans use to help manage their
|
||||
Rust projects. Cargo is currently in an alpha state, just like Rust, and so it
|
||||
is still a work in progress. However, it is already good enough to use for many
|
||||
Rust projects, and so it is assumed that Rust projects will use Cargo from the
|
||||
beginning.
|
||||
|
||||
Cargo manages three things: building your code, downloading the dependencies
|
||||
your code needs, and building the dependencies your code needs. At first, your
|
||||
program doesn't have any dependencies, so we'll only be using the first part of
|
||||
its functionality. Eventually, we'll add more. Since we started off by using
|
||||
Cargo, it'll be easy to add later.
|
||||
|
||||
If you installed Rust via the official installers you will also have
|
||||
Cargo. If you installed Rust some other way, you may want to [check
|
||||
the Cargo
|
||||
README](https://github.com/rust-lang/cargo#installing-cargo-from-nightlies)
|
||||
for specific instructions about installing it.
|
||||
|
||||
Let's convert Hello World to Cargo.
|
||||
|
||||
To Cargo-ify our project, we need to do two things: Make a `Cargo.toml`
|
||||
configuration file, and put our source file in the right place. Let's
|
||||
do that part first:
|
||||
|
||||
```{bash}
|
||||
$ mkdir src
|
||||
$ mv main.rs src/main.rs
|
||||
```
|
||||
|
||||
Cargo expects your source files to live inside a `src` directory. That leaves
|
||||
the top level for other things, like READMEs, license information, and anything
|
||||
not related to your code. Cargo helps us keep our projects nice and tidy. A
|
||||
place for everything, and everything in its place.
|
||||
|
||||
Next, our configuration file:
|
||||
|
||||
```{bash}
|
||||
$ editor Cargo.toml
|
||||
```
|
||||
|
||||
Make sure to get this name right: you need the capital `C`!
|
||||
|
||||
Put this inside:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
|
||||
name = "hello_world"
|
||||
version = "0.0.1"
|
||||
authors = [ "Your name <you@example.com>" ]
|
||||
|
||||
[[bin]]
|
||||
|
||||
name = "hello_world"
|
||||
```
|
||||
|
||||
This file is in the [TOML](https://github.com/toml-lang/toml) format. Let's let
|
||||
it explain itself to you:
|
||||
|
||||
> TOML aims to be a minimal configuration file format that's easy to read due
|
||||
> to obvious semantics. TOML is designed to map unambiguously to a hash table.
|
||||
> TOML should be easy to parse into data structures in a wide variety of
|
||||
> languages.
|
||||
|
||||
TOML is very similar to INI, but with some extra goodies.
|
||||
|
||||
Anyway, there are two **table**s in this file: `package` and `bin`. The first
|
||||
tells Cargo metadata about your package. The second tells Cargo that we're
|
||||
interested in building a binary, not a library (though we could do both!), as
|
||||
well as what it is named.
|
||||
|
||||
Once you have this file in place, we should be ready to build! Try this:
|
||||
|
||||
```{bash}
|
||||
$ cargo build
|
||||
Compiling hello_world v0.0.1 (file:///home/yourname/projects/hello_world)
|
||||
$ ./target/hello_world
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Bam! We build our project with `cargo build`, and run it with
|
||||
`./target/hello_world`. This hasn't bought us a whole lot over our simple use
|
||||
of `rustc`, but think about the future: when our project has more than one
|
||||
file, we would need to call `rustc` more than once, and pass it a bunch of options to
|
||||
tell it to build everything together. With Cargo, as our project grows, we can
|
||||
just `cargo build` and it'll work the right way.
|
||||
|
||||
You'll also notice that Cargo has created a new file: `Cargo.lock`.
|
||||
|
||||
```toml
|
||||
[root]
|
||||
name = "hello_world"
|
||||
version = "0.0.1"
|
||||
```
|
||||
|
||||
This file is used by Cargo to keep track of dependencies in your application.
|
||||
Right now, we don't have any, so it's a bit sparse. You won't ever need
|
||||
to touch this file yourself, just let Cargo handle it.
|
||||
|
||||
That's it! We've successfully built `hello_world` with Cargo. Even though our
|
||||
program is simple, it's using much of the real tooling that you'll use for the
|
||||
rest of your Rust career.
|
||||
|
||||
Now that you've got the tools down, let's actually learn more about the Rust
|
||||
language itself. These are the basics that will serve you well through the rest
|
||||
of your time with Rust.
|
|
@ -0,0 +1,164 @@
|
|||
# Hello, world!
|
||||
|
||||
Now that you have Rust installed, let's write your first Rust program. It's
|
||||
traditional to make your first program in any new language one that prints the
|
||||
text "Hello, world!" to the screen. The nice thing about starting with such a
|
||||
simple program is that you can verify that your compiler isn't just installed,
|
||||
but also working properly. And printing information to the screen is a pretty
|
||||
common thing to do.
|
||||
|
||||
The first thing that we need to do is make a file to put our code in. I like
|
||||
to make a `projects` directory in my home directory, and keep all my projects
|
||||
there. Rust does not care where your code lives.
|
||||
|
||||
This actually leads to one other concern we should address: this guide will
|
||||
assume that you have basic familiarity with the command line. Rust does not
|
||||
require that you know a whole ton about the command line, but until the
|
||||
language is in a more finished state, IDE support is spotty. Rust makes no
|
||||
specific demands on your editing tooling, or where your code lives.
|
||||
|
||||
With that said, let's make a directory in our projects directory.
|
||||
|
||||
```{bash}
|
||||
$ mkdir ~/projects
|
||||
$ cd ~/projects
|
||||
$ mkdir hello_world
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
If you're on Windows and not using PowerShell, the `~` may not work. Consult
|
||||
the documentation for your shell for more details.
|
||||
|
||||
Let's make a new source file next. I'm going to use the syntax `editor
|
||||
filename` to represent editing a file in these examples, but you should use
|
||||
whatever method you want. We'll call our file `main.rs`:
|
||||
|
||||
```{bash}
|
||||
$ editor main.rs
|
||||
```
|
||||
|
||||
Rust files always end in a `.rs` extension. If you're using more than one word
|
||||
in your filename, use an underscore. `hello_world.rs` rather than
|
||||
`helloworld.rs`.
|
||||
|
||||
Now that you've got your file open, type this in:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
Save the file, and then type this into your terminal window:
|
||||
|
||||
```{bash}
|
||||
$ rustc main.rs
|
||||
$ ./main # or main.exe on Windows
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
You can also run these examples on [play.rust-lang.org](http://play.rust-lang.org/) by clicking on the arrow that appears in the upper right of the example when you mouse over the code.
|
||||
|
||||
Success! Let's go over what just happened in detail.
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
These lines define a **function** in Rust. The `main` function is special:
|
||||
it's the beginning of every Rust program. The first line says "I'm declaring a
|
||||
function named `main`, which takes no arguments and returns nothing." If there
|
||||
were arguments, they would go inside the parentheses (`(` and `)`), and because
|
||||
we aren't returning anything from this function, we've dropped that notation
|
||||
entirely. We'll get to it later.
|
||||
|
||||
You'll also note that the function is wrapped in curly braces (`{` and `}`).
|
||||
Rust requires these around all function bodies. It is also considered good
|
||||
style to put the opening curly brace on the same line as the function
|
||||
declaration, with one space in between.
|
||||
|
||||
Next up is this line:
|
||||
|
||||
```{rust}
|
||||
println!("Hello, world!");
|
||||
```
|
||||
|
||||
This line does all of the work in our little program. There are a number of
|
||||
details that are important here. The first is that it's indented with four
|
||||
spaces, not tabs. Please configure your editor of choice to insert four spaces
|
||||
with the tab key. We provide some [sample configurations for various
|
||||
editors](https://github.com/rust-lang/rust/tree/master/src/etc).
|
||||
|
||||
The second point is the `println!()` part. This is calling a Rust **macro**,
|
||||
which is how metaprogramming is done in Rust. If it were a function instead, it
|
||||
would look like this: `println()`. For our purposes, we don't need to worry
|
||||
about this difference. Just know that sometimes, you'll see a `!`, and that
|
||||
means that you're calling a macro instead of a normal function. Rust implements
|
||||
`println!` as a macro rather than a function for good reasons, but that's a
|
||||
very advanced topic. You'll learn more when we talk about macros later. One
|
||||
last thing to mention: Rust's macros are significantly different from C macros,
|
||||
if you've used those. Don't be scared of using macros. We'll get to the details
|
||||
eventually, you'll just have to trust us for now.
|
||||
|
||||
Next, `"Hello, world!"` is a **string**. Strings are a surprisingly complicated
|
||||
topic in a systems programming language, and this is a **statically allocated**
|
||||
string. We will talk more about different kinds of allocation later. We pass
|
||||
this string as an argument to `println!`, which prints the string to the
|
||||
screen. Easy enough!
|
||||
|
||||
Finally, the line ends with a semicolon (`;`). Rust is an **expression
|
||||
oriented** language, which means that most things are expressions. The `;` is
|
||||
used to indicate that this expression is over, and the next one is ready to
|
||||
begin. Most lines of Rust code end with a `;`. We will cover this in-depth
|
||||
later in the guide.
|
||||
|
||||
Finally, actually **compiling** and **running** our program. We can compile
|
||||
with our compiler, `rustc`, by passing it the name of our source file:
|
||||
|
||||
```{bash}
|
||||
$ rustc main.rs
|
||||
```
|
||||
|
||||
This is similar to `gcc` or `clang`, if you come from a C or C++ background. Rust
|
||||
will output a binary executable. You can see it with `ls`:
|
||||
|
||||
```{bash}
|
||||
$ ls
|
||||
main main.rs
|
||||
```
|
||||
|
||||
Or on Windows:
|
||||
|
||||
```{bash}
|
||||
$ dir
|
||||
main.exe main.rs
|
||||
```
|
||||
|
||||
There are now two files: our source code, with the `.rs` extension, and the
|
||||
executable (`main.exe` on Windows, `main` everywhere else)
|
||||
|
||||
```{bash}
|
||||
$ ./main # or main.exe on Windows
|
||||
```
|
||||
|
||||
This prints out our `Hello, world!` text to our terminal.
|
||||
|
||||
If you come from a dynamically typed language like Ruby, Python, or JavaScript,
|
||||
you may not be used to these two steps being separate. Rust is an
|
||||
**ahead-of-time compiled language**, which means that you can compile a
|
||||
program, give it to someone else, and they don't need to have Rust installed.
|
||||
If you give someone a `.rb` or `.py` or `.js` file, they need to have
|
||||
Ruby/Python/JavaScript installed, but you just need one command to both compile
|
||||
and run your program. Everything is a tradeoff in language design, and Rust has
|
||||
made its choice.
|
||||
|
||||
Congratulations! You have officially written a Rust program. That makes you a
|
||||
Rust programmer! Welcome.
|
||||
|
||||
Next, I'd like to introduce you to another tool, Cargo, which is used to write
|
||||
real-world Rust programs. Just using `rustc` is nice for simple things, but as
|
||||
your project grows, you'll want something to help you manage all of the options
|
||||
that it has, and to make it easy to share your code with other people and
|
||||
projects.
|
|
@ -0,0 +1,141 @@
|
|||
# `if`
|
||||
|
||||
Rust's take on `if` is not particularly complex, but it's much more like the
|
||||
`if` you'll find in a dynamically typed language than in a more traditional
|
||||
systems language. So let's talk about it, to make sure you grasp the nuances.
|
||||
|
||||
`if` is a specific form of a more general concept, the 'branch.' The name comes
|
||||
from a branch in a tree: a decision point, where depending on a choice,
|
||||
multiple paths can be taken.
|
||||
|
||||
In the case of `if`, there is one choice that leads down two paths:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
|
||||
if x == 5 {
|
||||
println!("x is five!");
|
||||
}
|
||||
```
|
||||
|
||||
If we changed the value of `x` to something else, this line would not print.
|
||||
More specifically, if the expression after the `if` evaluates to `true`, then
|
||||
the block is executed. If it's `false`, then it is not.
|
||||
|
||||
If you want something to happen in the `false` case, use an `else`:
|
||||
|
||||
```{rust}
|
||||
let x = 5;
|
||||
|
||||
if x == 5 {
|
||||
println!("x is five!");
|
||||
} else {
|
||||
println!("x is not five :(");
|
||||
}
|
||||
```
|
||||
|
||||
This is all pretty standard. However, you can also do this:
|
||||
|
||||
|
||||
```{rust}
|
||||
let x = 5;
|
||||
|
||||
let y = if x == 5 {
|
||||
10
|
||||
} else {
|
||||
15
|
||||
}; // y: i32
|
||||
```
|
||||
|
||||
Which we can (and probably should) write like this:
|
||||
|
||||
```{rust}
|
||||
let x = 5;
|
||||
|
||||
let y = if x == 5 { 10 } else { 15 }; // y: i32
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
In many languages, `if` is a statement, and therefore, `let x = if ...` would
|
||||
make no sense. But in Rust, `if` is an expression, which means that it returns
|
||||
a value. We can then use this value to initialize the binding.
|
||||
|
||||
Speaking of which, bindings are a kind of the first of Rust's two statements.
|
||||
The proper name is a **declaration statement**. So far, `let` is the only kind
|
||||
of declaration statement we've seen. Let's talk about that some more.
|
||||
|
||||
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 C, where an
|
||||
assignment evaluates to the assigned value (e.g. `5` in the previous example),
|
||||
in Rust the value of an assignment is the unit type `()` (which we'll cover later).
|
||||
|
||||
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}
|
||||
let x = 5;
|
||||
|
||||
let y: i32 = if x == 5 { 10 } else { 15 };
|
||||
```
|
||||
|
||||
Note that I've added the type annotation to `y`, to specify explicitly that I
|
||||
want `y` to be an integer.
|
||||
|
||||
This is not the same as this, which won't compile:
|
||||
|
||||
```{ignore}
|
||||
let x = 5;
|
||||
|
||||
let y: i32 = if x == 5 { 10; } else { 15; };
|
||||
```
|
||||
|
||||
Note the semicolons after the 10 and 15. Rust will give us the following error:
|
||||
|
||||
```text
|
||||
error: mismatched types: expected `i32` but found `()` (expected i32 but found ())
|
||||
```
|
||||
|
||||
We expected an integer, but we got `()`. `()` is pronounced 'unit', and is a
|
||||
special type in Rust's type system. In Rust, `()` is _not_ a valid value for a
|
||||
variable of type `i32`. It's only a valid value for variables of the type `()`,
|
||||
which aren't very useful. Remember how we said statements don't return a value?
|
||||
Well, that's the purpose of unit in this case. The semicolon turns any
|
||||
expression into a statement by throwing away its value and returning unit
|
||||
instead.
|
||||
|
||||
There's one more time in which you won't see a semicolon at the end of a line
|
||||
of Rust code. For that, we'll need our next concept: functions.
|
|
@ -0,0 +1,89 @@
|
|||
# Installing Rust
|
||||
|
||||
The first step to using Rust is to install it! There are a number of ways to
|
||||
install Rust, but the easiest is to use the `rustup` script. If you're on
|
||||
Linux or a Mac, all you need to do is this (note that you don't need to type
|
||||
in the `$`s, they just indicate the start of each command):
|
||||
|
||||
```bash
|
||||
$ curl -L https://static.rust-lang.org/rustup.sh | sudo sh
|
||||
```
|
||||
|
||||
If you're concerned about the [potential insecurity](http://curlpipesh.tumblr.com/) of using `curl | sudo sh`,
|
||||
please keep reading and see our disclaimer below. And feel free to use a two-step version of the installation and examine our installation script:
|
||||
|
||||
```bash
|
||||
$ curl -L https://static.rust-lang.org/rustup.sh -O
|
||||
$ sudo sh rustup.sh
|
||||
```
|
||||
|
||||
If you're on Windows, please download either the [32-bit
|
||||
installer](https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe)
|
||||
or the [64-bit
|
||||
installer](https://static.rust-lang.org/dist/rust-nightly-x86_64-pc-windows-gnu.exe)
|
||||
and run it.
|
||||
|
||||
If you decide you don't want Rust anymore, we'll be a bit sad, but that's okay.
|
||||
Not every programming language is great for everyone. Just pass an argument to
|
||||
the script:
|
||||
|
||||
```bash
|
||||
$ curl -s https://static.rust-lang.org/rustup.sh | sudo sh -s -- --uninstall
|
||||
```
|
||||
|
||||
If you used the Windows installer, just re-run the `.exe` and it will give you
|
||||
an uninstall option.
|
||||
|
||||
You can re-run this script any time you want to update Rust. Which, at this
|
||||
point, is often. Rust is still pre-1.0, and so people assume that you're using
|
||||
a very recent Rust.
|
||||
|
||||
This brings me to one other point: some people, and somewhat rightfully so, get
|
||||
very upset when we tell you to `curl | sudo sh`. And they should be! Basically,
|
||||
when you do this, you are trusting that the good people who maintain Rust
|
||||
aren't going to hack your computer and do bad things. That's a good instinct!
|
||||
If you're one of those people, please check out the documentation on [building
|
||||
Rust from Source](https://github.com/rust-lang/rust#building-from-source), or
|
||||
[the official binary downloads](http://www.rust-lang.org/install.html). And we
|
||||
promise that this method will not be the way to install Rust forever: it's just
|
||||
the easiest way to keep people updated while Rust is in its alpha state.
|
||||
|
||||
Oh, we should also mention the officially supported platforms:
|
||||
|
||||
* Windows (7, 8, Server 2008 R2)
|
||||
* Linux (2.6.18 or later, various distributions), x86 and x86-64
|
||||
* OSX 10.7 (Lion) or greater, x86 and x86-64
|
||||
|
||||
We extensively test Rust on these platforms, and a few others, too, like
|
||||
Android. But these are the ones most likely to work, as they have the most
|
||||
testing.
|
||||
|
||||
Finally, a comment about Windows. Rust considers Windows to be a first-class
|
||||
platform upon release, but if we're honest, the Windows experience isn't as
|
||||
integrated as the Linux/OS X experience is. We're working on it! If anything
|
||||
does not work, it is a bug. Please let us know if that happens. Each and every
|
||||
commit is tested against Windows just like any other platform.
|
||||
|
||||
If you've got Rust installed, you can open up a shell, and type this:
|
||||
|
||||
```bash
|
||||
$ rustc --version
|
||||
```
|
||||
|
||||
You should see some output that looks something like this:
|
||||
|
||||
```bash
|
||||
rustc 1.0.0-nightly (f11f3e7ba 2015-01-04 20:02:14 +0000)
|
||||
```
|
||||
|
||||
If you did, Rust has been installed successfully! Congrats!
|
||||
|
||||
If not, there are a number of places where you can get help. The easiest is
|
||||
[the #rust IRC channel on irc.mozilla.org](irc://irc.mozilla.org/#rust), which
|
||||
you can access through
|
||||
[Mibbit](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust). Click
|
||||
that link, and you'll be chatting with other Rustaceans (a silly nickname we
|
||||
call ourselves), and we can help you out. Other great resources include [our
|
||||
forum](http://discuss.rust-lang.org/), [the /r/rust
|
||||
subreddit](http://www.reddit.com/r/rust), and [Stack
|
||||
Overflow](http://stackoverflow.com/questions/tagged/rust).
|
|
@ -0,0 +1,7 @@
|
|||
% Intermediate
|
||||
|
||||
This section contains individual chapters, which are self-contained. They focus
|
||||
on specific topics, and can be read in any order.
|
||||
|
||||
After reading "Intermediate," you will have a solid understanding of Rust,
|
||||
and will be able to understand most Rust code and write more complex programs.
|
|
@ -0,0 +1,339 @@
|
|||
% Iterators
|
||||
|
||||
Let's talk about loops.
|
||||
|
||||
Remember Rust's `for` loop? Here's an example:
|
||||
|
||||
```{rust}
|
||||
for x in range(0i, 10i) {
|
||||
println!("{}", x);
|
||||
}
|
||||
```
|
||||
|
||||
Now that you know more Rust, we can talk in detail about how this works. The
|
||||
`range` function returns an **iterator**. An iterator is something that we can
|
||||
call the `.next()` method on repeatedly, and it gives us a sequence of things.
|
||||
|
||||
Like this:
|
||||
|
||||
```{rust}
|
||||
let mut range = range(0i, 10i);
|
||||
|
||||
loop {
|
||||
match range.next() {
|
||||
Some(x) => {
|
||||
println!("{}", x);
|
||||
},
|
||||
None => { break }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We make a mutable binding to the return value of `range`, which is our iterator.
|
||||
We then `loop`, with an inner `match`. This `match` is used on the result of
|
||||
`range.next()`, which gives us a reference to the next value of the iterator.
|
||||
`next` returns an `Option<int>`, in this case, which will be `Some(int)` when
|
||||
we have a value and `None` once we run out. If we get `Some(int)`, we print it
|
||||
out, and if we get `None`, we `break` out of the loop.
|
||||
|
||||
This code sample is basically the same as our `for` loop version. The `for`
|
||||
loop is just a handy way to write this `loop`/`match`/`break` construct.
|
||||
|
||||
`for` loops aren't the only thing that uses iterators, however. Writing your
|
||||
own iterator involves implementing the `Iterator` trait. While doing that is
|
||||
outside of the scope of this guide, Rust provides a number of useful iterators
|
||||
to accomplish various tasks. Before we talk about those, we should talk about a
|
||||
Rust anti-pattern. And that's `range`.
|
||||
|
||||
Yes, we just talked about how `range` is cool. But `range` is also very
|
||||
primitive. For example, if you needed to iterate over the contents of
|
||||
a vector, you may be tempted to write this:
|
||||
|
||||
```{rust}
|
||||
let nums = vec![1i, 2i, 3i];
|
||||
|
||||
for i in range(0u, nums.len()) {
|
||||
println!("{}", nums[i]);
|
||||
}
|
||||
```
|
||||
|
||||
This is strictly worse than using an actual iterator. The `.iter()` method on
|
||||
vectors returns an iterator which iterates through a reference to each element
|
||||
of the vector in turn. So write this:
|
||||
|
||||
```{rust}
|
||||
let nums = vec![1i, 2i, 3i];
|
||||
|
||||
for num in nums.iter() {
|
||||
println!("{}", num);
|
||||
}
|
||||
```
|
||||
|
||||
There are two reasons for this. First, this more directly expresses what we
|
||||
mean. We iterate through the entire vector, rather than iterating through
|
||||
indexes, and then indexing the vector. Second, this version is more efficient:
|
||||
the first version will have extra bounds checking because it used indexing,
|
||||
`nums[i]`. But since we yield a reference to each element of the vector in turn
|
||||
with the iterator, there's no bounds checking in the second example. This is
|
||||
very common with iterators: we can ignore unnecessary bounds checks, but still
|
||||
know that we're safe.
|
||||
|
||||
There's another detail here that's not 100% clear because of how `println!`
|
||||
works. `num` is actually of type `&int`. That is, it's a reference to an `int`,
|
||||
not an `int` itself. `println!` handles the dereferencing for us, so we don't
|
||||
see it. This code works fine too:
|
||||
|
||||
```{rust}
|
||||
let nums = vec![1i, 2i, 3i];
|
||||
|
||||
for num in nums.iter() {
|
||||
println!("{}", *num);
|
||||
}
|
||||
```
|
||||
|
||||
Now we're explicitly dereferencing `num`. Why does `iter()` give us references?
|
||||
Well, if it gave us the data itself, we would have to be its owner, which would
|
||||
involve making a copy of the data and giving us the copy. With references,
|
||||
we're just borrowing a reference to the data, and so it's just passing
|
||||
a reference, without needing to do the copy.
|
||||
|
||||
So, now that we've established that `range` is often not what you want, let's
|
||||
talk about what you do want instead.
|
||||
|
||||
There are three broad classes of things that are relevant here: iterators,
|
||||
**iterator adapters**, and **consumers**. Here's some definitions:
|
||||
|
||||
* 'iterators' give you a sequence of values.
|
||||
* 'iterator adapters' operate on an iterator, producing a new iterator with a
|
||||
different output sequence.
|
||||
* 'consumers' operate on an iterator, producing some final set of values.
|
||||
|
||||
Let's talk about consumers first, since you've already seen an iterator,
|
||||
`range`.
|
||||
|
||||
## Consumers
|
||||
|
||||
A 'consumer' operates on an iterator, returning some kind of value or values.
|
||||
The most common consumer is `collect()`. This code doesn't quite compile,
|
||||
but it shows the intention:
|
||||
|
||||
```{rust,ignore}
|
||||
let one_to_one_hundred = range(1i, 101i).collect();
|
||||
```
|
||||
|
||||
As you can see, we call `collect()` on our iterator. `collect()` takes
|
||||
as many values as the iterator will give it, and returns a collection
|
||||
of the results. So why won't this compile? Rust can't determine what
|
||||
type of things you want to collect, and so you need to let it know.
|
||||
Here's the version that does compile:
|
||||
|
||||
```{rust}
|
||||
let one_to_one_hundred = range(1i, 101i).collect::<Vec<int>>();
|
||||
```
|
||||
|
||||
If you remember, the `::<>` syntax allows us to give a type hint,
|
||||
and so we tell it that we want a vector of integers.
|
||||
|
||||
`collect()` is the most common consumer, but there are others too. `find()`
|
||||
is one:
|
||||
|
||||
```{rust}
|
||||
let greater_than_forty_two = range(0i, 100i)
|
||||
.find(|x| *x > 42);
|
||||
|
||||
match greater_than_forty_two {
|
||||
Some(_) => println!("We got some numbers!"),
|
||||
None => println!("No numbers found :("),
|
||||
}
|
||||
```
|
||||
|
||||
`find` takes a closure, and works on a reference to each element of an
|
||||
iterator. This closure returns `true` if the element is the element we're
|
||||
looking for, and `false` otherwise. Because we might not find a matching
|
||||
element, `find` returns an `Option` rather than the element itself.
|
||||
|
||||
Another important consumer is `fold`. Here's what it looks like:
|
||||
|
||||
```{rust}
|
||||
let sum = range(1i, 4i)
|
||||
.fold(0i, |sum, x| sum + x);
|
||||
```
|
||||
|
||||
`fold()` is a consumer that looks like this:
|
||||
`fold(base, |accumulator, element| ...)`. It takes two arguments: the first
|
||||
is an element called the "base". The second is a closure that itself takes two
|
||||
arguments: the first is called the "accumulator," and the second is an
|
||||
"element." Upon each iteration, the closure is called, and the result is the
|
||||
value of the accumulator on the next iteration. On the first iteration, the
|
||||
base is the value of the accumulator.
|
||||
|
||||
Okay, that's a bit confusing. Let's examine the values of all of these things
|
||||
in this iterator:
|
||||
|
||||
| base | accumulator | element | closure result |
|
||||
|------|-------------|---------|----------------|
|
||||
| 0i | 0i | 1i | 1i |
|
||||
| 0i | 1i | 2i | 3i |
|
||||
| 0i | 3i | 3i | 6i |
|
||||
|
||||
We called `fold()` with these arguments:
|
||||
|
||||
```{rust}
|
||||
# range(1i, 4i)
|
||||
.fold(0i, |sum, x| sum + x);
|
||||
```
|
||||
|
||||
So, `0i` is our base, `sum` is our accumulator, and `x` is our element. On the
|
||||
first iteration, we set `sum` to `0i`, and `x` is the first element of `nums`,
|
||||
`1i`. We then add `sum` and `x`, which gives us `0i + 1i = 1i`. On the second
|
||||
iteration, that value becomes our accumulator, `sum`, and the element is
|
||||
the second element of the array, `2i`. `1i + 2i = 3i`, and so that becomes
|
||||
the value of the accumulator for the last iteration. On that iteration,
|
||||
`x` is the last element, `3i`, and `3i + 3i = 6i`, which is our final
|
||||
result for our sum. `1 + 2 + 3 = 6`, and that's the result we got.
|
||||
|
||||
Whew. `fold` can be a bit strange the first few times you see it, but once it
|
||||
clicks, you can use it all over the place. Any time you have a list of things,
|
||||
and you want a single result, `fold` is appropriate.
|
||||
|
||||
Consumers are important due to one additional property of iterators we haven't
|
||||
talked about yet: laziness. Let's talk some more about iterators, and you'll
|
||||
see why consumers matter.
|
||||
|
||||
## Iterators
|
||||
|
||||
As we've said before, an iterator is something that we can call the
|
||||
`.next()` method on repeatedly, and it gives us a sequence of things.
|
||||
Because you need to call the method, this means that iterators
|
||||
are **lazy** and don't need to generate all of the values upfront.
|
||||
This code, for example, does not actually generate the numbers
|
||||
`1-100`, and just creates a value that represents the sequence:
|
||||
|
||||
```{rust}
|
||||
let nums = range(1i, 100i);
|
||||
```
|
||||
|
||||
Since we didn't do anything with the range, it didn't generate the sequence.
|
||||
Let's add the consumer:
|
||||
|
||||
```{rust}
|
||||
let nums = range(1i, 100i).collect::<Vec<int>>();
|
||||
```
|
||||
|
||||
Now, `collect()` will require that `range()` give it some numbers, and so
|
||||
it will do the work of generating the sequence.
|
||||
|
||||
`range` is one of two basic iterators that you'll see. The other is `iter()`,
|
||||
which you've used before. `iter()` can turn a vector into a simple iterator
|
||||
that gives you each element in turn:
|
||||
|
||||
```{rust}
|
||||
let nums = [1i, 2i, 3i];
|
||||
|
||||
for num in nums.iter() {
|
||||
println!("{}", num);
|
||||
}
|
||||
```
|
||||
|
||||
These two basic iterators should serve you well. There are some more
|
||||
advanced iterators, including ones that are infinite. Like `count`:
|
||||
|
||||
```{rust}
|
||||
std::iter::count(1i, 5i);
|
||||
```
|
||||
|
||||
This iterator counts up from one, adding five each time. It will give
|
||||
you a new integer every time, forever (well, technically, until it reaches the
|
||||
maximum number representable by an `int`). But since iterators are lazy,
|
||||
that's okay! You probably don't want to use `collect()` on it, though...
|
||||
|
||||
That's enough about iterators. Iterator adapters are the last concept
|
||||
we need to talk about with regards to iterators. Let's get to it!
|
||||
|
||||
## Iterator adapters
|
||||
|
||||
"Iterator adapters" take an iterator and modify it somehow, producing
|
||||
a new iterator. The simplest one is called `map`:
|
||||
|
||||
```{rust,ignore}
|
||||
range(1i, 100i).map(|x| x + 1i);
|
||||
```
|
||||
|
||||
`map` is called upon another iterator, and produces a new iterator where each
|
||||
element reference has the closure it's been given as an argument called on it.
|
||||
So this would give us the numbers from `2-100`. Well, almost! If you
|
||||
compile the example, you'll get a warning:
|
||||
|
||||
```{notrust,ignore}
|
||||
warning: unused result which must be used: iterator adaptors are lazy and
|
||||
do nothing unless consumed, #[warn(unused_must_use)] on by default
|
||||
range(1i, 100i).map(|x| x + 1i);
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
Laziness strikes again! That closure will never execute. This example
|
||||
doesn't print any numbers:
|
||||
|
||||
```{rust,ignore}
|
||||
range(1i, 100i).map(|x| println!("{}", x));
|
||||
```
|
||||
|
||||
If you are trying to execute a closure on an iterator for its side effects,
|
||||
just use `for` instead.
|
||||
|
||||
There are tons of interesting iterator adapters. `take(n)` will return an
|
||||
iterator over the next `n` elements of the original iterator, note that this
|
||||
has no side effect on the original iterator. Let's try it out with our infinite
|
||||
iterator from before, `count()`:
|
||||
|
||||
```{rust}
|
||||
for i in std::iter::count(1i, 5i).take(5) {
|
||||
println!("{}", i);
|
||||
}
|
||||
```
|
||||
|
||||
This will print
|
||||
|
||||
```{notrust,ignore}
|
||||
1
|
||||
6
|
||||
11
|
||||
16
|
||||
21
|
||||
```
|
||||
|
||||
`filter()` is an adapter that takes a closure as an argument. This closure
|
||||
returns `true` or `false`. The new iterator `filter()` produces
|
||||
only the elements that that closure returns `true` for:
|
||||
|
||||
```{rust}
|
||||
for i in range(1i, 100i).filter(|&x| x % 2 == 0) {
|
||||
println!("{}", i);
|
||||
}
|
||||
```
|
||||
|
||||
This will print all of the even numbers between one and a hundred.
|
||||
(Note that because `filter` doesn't consume the elements that are
|
||||
being iterated over, it is passed a reference to each element, and
|
||||
thus the filter predicate uses the `&x` pattern to extract the integer
|
||||
itself.)
|
||||
|
||||
You can chain all three things together: start with an iterator, adapt it
|
||||
a few times, and then consume the result. Check it out:
|
||||
|
||||
```{rust}
|
||||
range(1i, 1000i)
|
||||
.filter(|&x| x % 2 == 0)
|
||||
.filter(|&x| x % 3 == 0)
|
||||
.take(5)
|
||||
.collect::<Vec<int>>();
|
||||
```
|
||||
|
||||
This will give you a vector containing `6`, `12`, `18`, `24`, and `30`.
|
||||
|
||||
This is just a small taste of what iterators, iterator adapters, and consumers
|
||||
can help you with. There are a number of really useful iterators, and you can
|
||||
write your own as well. Iterators provide a safe, efficient way to manipulate
|
||||
all kinds of lists. They're a little unusual at first, but if you play with
|
||||
them, you'll get hooked. For a full list of the different iterators and
|
||||
consumers, check out the [iterator module documentation](std/iter/index.html).
|
|
@ -0,0 +1,133 @@
|
|||
# Looping
|
||||
|
||||
Looping is the last basic construct that we haven't learned yet in Rust. Rust has
|
||||
two main looping constructs: `for` and `while`.
|
||||
|
||||
## `for`
|
||||
|
||||
The `for` loop is used to loop a particular number of times. Rust's `for` loops
|
||||
work a bit differently than in other systems languages, however. Rust's `for`
|
||||
loop doesn't look like this "C-style" `for` loop:
|
||||
|
||||
```{c}
|
||||
for (x = 0; x < 10; x++) {
|
||||
printf( "%d\n", x );
|
||||
}
|
||||
```
|
||||
|
||||
Instead, it looks like this:
|
||||
|
||||
```{rust}
|
||||
for x in range(0, 10) {
|
||||
println!("{}", x); // x: i32
|
||||
}
|
||||
```
|
||||
|
||||
In slightly more abstract terms,
|
||||
|
||||
```{ignore}
|
||||
for var in expression {
|
||||
code
|
||||
}
|
||||
```
|
||||
|
||||
The expression is an iterator, which we will discuss in more depth later in the
|
||||
guide. The iterator gives back a series of elements. Each element is one
|
||||
iteration of the loop. That value is then bound to the name `var`, which is
|
||||
valid for the loop body. Once the body is over, the next value is fetched from
|
||||
the iterator, and we loop another time. When there are no more values, the
|
||||
`for` loop is over.
|
||||
|
||||
In our example, `range` is a function that takes a start and an end position,
|
||||
and gives an iterator over those values. The upper bound is exclusive, though,
|
||||
so our loop will print `0` through `9`, not `10`.
|
||||
|
||||
Rust does not have the "C-style" `for` loop on purpose. Manually controlling
|
||||
each element of the loop is complicated and error prone, even for experienced C
|
||||
developers.
|
||||
|
||||
We'll talk more about `for` when we cover **iterator**s, later in the Guide.
|
||||
|
||||
## `while`
|
||||
|
||||
The other kind of looping construct in Rust is the `while` loop. It looks like
|
||||
this:
|
||||
|
||||
```{rust}
|
||||
let mut x = 5u; // mut x: uint
|
||||
let mut done = false; // mut done: bool
|
||||
|
||||
while !done {
|
||||
x += x - 3;
|
||||
println!("{}", x);
|
||||
if x % 5 == 0 { done = true; }
|
||||
}
|
||||
```
|
||||
|
||||
`while` loops are the correct choice when you're not sure how many times
|
||||
you need to loop.
|
||||
|
||||
If you need an infinite loop, you may be tempted to write this:
|
||||
|
||||
```{rust,ignore}
|
||||
while true {
|
||||
```
|
||||
|
||||
However, Rust has a dedicated keyword, `loop`, to handle this case:
|
||||
|
||||
```{rust,ignore}
|
||||
loop {
|
||||
```
|
||||
|
||||
Rust's control-flow analysis treats this construct differently than a
|
||||
`while true`, since we know that it will always loop. The details of what
|
||||
that _means_ aren't super important to understand at this stage, but in
|
||||
general, the more information we can give to the compiler, the better it
|
||||
can do with safety and code generation, so you should always prefer
|
||||
`loop` when you plan to loop infinitely.
|
||||
|
||||
## Ending iteration early
|
||||
|
||||
Let's take a look at that `while` loop we had earlier:
|
||||
|
||||
```{rust}
|
||||
let mut x = 5u;
|
||||
let mut done = false;
|
||||
|
||||
while !done {
|
||||
x += x - 3;
|
||||
println!("{}", x);
|
||||
if x % 5 == 0 { done = true; }
|
||||
}
|
||||
```
|
||||
|
||||
We had to keep a dedicated `mut` boolean variable binding, `done`, to know
|
||||
when we should exit out of the loop. Rust has two keywords to help us with
|
||||
modifying iteration: `break` and `continue`.
|
||||
|
||||
In this case, we can write the loop in a better way with `break`:
|
||||
|
||||
```{rust}
|
||||
let mut x = 5u;
|
||||
|
||||
loop {
|
||||
x += x - 3;
|
||||
println!("{}", x);
|
||||
if x % 5 == 0 { break; }
|
||||
}
|
||||
```
|
||||
|
||||
We now loop forever with `loop` and use `break` to break out early.
|
||||
|
||||
`continue` is similar, but instead of ending the loop, goes to the next
|
||||
iteration. This will only print the odd numbers:
|
||||
|
||||
```{rust}
|
||||
for x in range(0, 10) {
|
||||
if x % 2 == 0 { continue; }
|
||||
|
||||
println!("{}", x);
|
||||
}
|
||||
```
|
||||
|
||||
Both `continue` and `break` are valid in both kinds of loops.
|
|
@ -0,0 +1,156 @@
|
|||
# Match
|
||||
|
||||
Often, a simple `if`/`else` isn't enough, because you have more than two
|
||||
possible options. Also, `else` conditions can get incredibly complicated, so
|
||||
what's the solution?
|
||||
|
||||
Rust has a keyword, `match`, that allows you to replace complicated `if`/`else`
|
||||
groupings with something more powerful. Check it out:
|
||||
|
||||
```{rust}
|
||||
let x = 5;
|
||||
|
||||
match x {
|
||||
1 => println!("one"),
|
||||
2 => println!("two"),
|
||||
3 => println!("three"),
|
||||
4 => println!("four"),
|
||||
5 => println!("five"),
|
||||
_ => println!("something else"),
|
||||
}
|
||||
```
|
||||
|
||||
`match` takes an expression and then branches based on its value. Each 'arm' of
|
||||
the branch is of the form `val => expression`. When the value matches, that arm's
|
||||
expression will be evaluated. It's called `match` because of the term 'pattern
|
||||
matching', which `match` is an implementation of.
|
||||
|
||||
So what's the big advantage here? Well, there are a few. First of all, `match`
|
||||
enforces 'exhaustiveness checking'. Do you see that last arm, the one with the
|
||||
underscore (`_`)? If we remove that arm, Rust will give us an error:
|
||||
|
||||
```text
|
||||
error: non-exhaustive patterns: `_` not covered
|
||||
```
|
||||
|
||||
In other words, Rust is trying to tell us we forgot a value. Because `x` is an
|
||||
integer, Rust knows that it can have a number of different values – for example,
|
||||
`6`. Without the `_`, however, there is no arm that could match, and so Rust refuses
|
||||
to compile. `_` acts like a 'catch-all arm'. If none of the other arms match,
|
||||
the arm with `_` will, and since we have this catch-all arm, we now have an arm
|
||||
for every possible value of `x`, and so our program will compile successfully.
|
||||
|
||||
`match` statements also destructure enums, as well. Remember this code from the
|
||||
section on enums?
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
let ordering = cmp(x, y);
|
||||
|
||||
if ordering == Ordering::Less {
|
||||
println!("less");
|
||||
} else if ordering == Ordering::Greater {
|
||||
println!("greater");
|
||||
} else if ordering == Ordering::Equal {
|
||||
println!("equal");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can re-write this as a `match`:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
match cmp(x, y) {
|
||||
Ordering::Less => println!("less"),
|
||||
Ordering::Greater => println!("greater"),
|
||||
Ordering::Equal => println!("equal"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This version has way less noise, and it also checks exhaustively to make sure
|
||||
that we have covered all possible variants of `Ordering`. With our `if`/`else`
|
||||
version, if we had forgotten the `Greater` case, for example, our program would
|
||||
have happily compiled. If we forget in the `match`, it will not. Rust helps us
|
||||
make sure to cover all of our bases.
|
||||
|
||||
`match` expressions also allow us to get the values contained in an `enum`
|
||||
(also known as destructuring) as follows:
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(i32),
|
||||
Missing,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = OptionalInt::Value(5);
|
||||
let y = OptionalInt::Missing;
|
||||
|
||||
match x {
|
||||
OptionalInt::Value(n) => println!("x is {}", n),
|
||||
OptionalInt::Missing => println!("x is missing!"),
|
||||
}
|
||||
|
||||
match y {
|
||||
OptionalInt::Value(n) => println!("y is {}", n),
|
||||
OptionalInt::Missing => println!("y is missing!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That is how you can get and use the values contained in `enum`s.
|
||||
It can also allow us to handle errors or unexpected computations; for example, a
|
||||
function that is not guaranteed to be able to compute a result (an `i32` here)
|
||||
could return an `OptionalInt`, and we would handle that value with a `match`.
|
||||
As you can see, `enum` and `match` used together are quite useful!
|
||||
|
||||
`match` is also an expression, which means we can use it on the right-hand
|
||||
side of a `let` binding or directly where an expression is used. We could
|
||||
also implement the previous example like this:
|
||||
|
||||
```{rust}
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn cmp(a: i32, b: i32) -> Ordering {
|
||||
if a < b { Ordering::Less }
|
||||
else if a > b { Ordering::Greater }
|
||||
else { Ordering::Equal }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
println!("{}", match cmp(x, y) {
|
||||
Ordering::Less => "less",
|
||||
Ordering::Greater => "greater",
|
||||
Ordering::Equal => "equal",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes, it's a nice pattern.
|
|
@ -0,0 +1,88 @@
|
|||
% Method Syntax
|
||||
|
||||
Functions are great, but if you want to call a bunch of them on some data, it
|
||||
can be awkward. Consider this code:
|
||||
|
||||
```{rust,ignore}
|
||||
baz(bar(foo(x)));
|
||||
```
|
||||
|
||||
We would read this left-to right, and so we see 'baz bar foo.' But this isn't the
|
||||
order that the functions would get called in, that's inside-out: 'foo bar baz.'
|
||||
Wouldn't it be nice if we could do this instead?
|
||||
|
||||
```{rust,ignore}
|
||||
x.foo().bar().baz();
|
||||
```
|
||||
|
||||
Luckily, as you may have guessed with the leading question, you can! Rust provides
|
||||
the ability to use this **method call syntax** via the `impl` keyword.
|
||||
|
||||
Here's how it works:
|
||||
|
||||
```{rust}
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
fn area(&self) -> f64 {
|
||||
std::f64::consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
|
||||
println!("{}", c.area());
|
||||
}
|
||||
```
|
||||
|
||||
This will print `12.566371`.
|
||||
|
||||
We've made a struct that represents a circle. We then write an `impl` block,
|
||||
and inside it, define a method, `area`. Methods take a special first
|
||||
parameter, `&self`. There are three variants: `self`, `&self`, and `&mut self`.
|
||||
You can think of this first parameter as being the `x` in `x.foo()`. The three
|
||||
variants correspond to the three kinds of thing `x` could be: `self` if it's
|
||||
just a value on the stack, `&self` if it's a reference, and `&mut self` if it's
|
||||
a mutable reference. We should default to using `&self`, as it's the most
|
||||
common.
|
||||
|
||||
Finally, as you may remember, the value of the area of a circle is `π*r²`.
|
||||
Because we took the `&self` parameter to `area`, we can use it just like any
|
||||
other parameter. Because we know it's a `Circle`, we can access the `radius`
|
||||
just like we would with any other struct. An import of π and some
|
||||
multiplications later, and we have our area.
|
||||
|
||||
You can also define methods that do not take a `self` parameter. Here's a
|
||||
pattern that's very common in Rust code:
|
||||
|
||||
```{rust}
|
||||
# #![allow(non_shorthand_field_patterns)]
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
fn new(x: f64, y: f64, radius: f64) -> Circle {
|
||||
Circle {
|
||||
x: x,
|
||||
y: y,
|
||||
radius: radius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Circle::new(0.0, 0.0, 2.0);
|
||||
}
|
||||
```
|
||||
|
||||
This **static method** builds a new `Circle` for us. Note that static methods
|
||||
are called with the `Struct::method()` syntax, rather than the `ref.method()`
|
||||
syntax.
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
% Patterns
|
||||
|
||||
We've made use of patterns a few times in the guide: first with `let` bindings,
|
||||
then with `match` statements. Let's go on a whirlwind tour of all of the things
|
||||
patterns can do!
|
||||
|
||||
A quick refresher: you can match against literals directly, and `_` acts as an
|
||||
'any' case:
|
||||
|
||||
```{rust}
|
||||
let x = 1i;
|
||||
|
||||
match x {
|
||||
1 => println!("one"),
|
||||
2 => println!("two"),
|
||||
3 => println!("three"),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
|
||||
You can match multiple patterns with `|`:
|
||||
|
||||
```{rust}
|
||||
let x = 1i;
|
||||
|
||||
match x {
|
||||
1 | 2 => println!("one or two"),
|
||||
3 => println!("three"),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
|
||||
You can match a range of values with `...`:
|
||||
|
||||
```{rust}
|
||||
let x = 1i;
|
||||
|
||||
match x {
|
||||
1 ... 5 => println!("one through five"),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
|
||||
Ranges are mostly used with integers and single characters.
|
||||
|
||||
If you're matching multiple things, via a `|` or a `...`, you can bind
|
||||
the value to a name with `@`:
|
||||
|
||||
```{rust}
|
||||
let x = 1i;
|
||||
|
||||
match x {
|
||||
e @ 1 ... 5 => println!("got a range element {}", e),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
|
||||
If you're matching on an enum which has variants, you can use `..` to
|
||||
ignore the value and type in the variant:
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(int),
|
||||
Missing,
|
||||
}
|
||||
|
||||
let x = OptionalInt::Value(5i);
|
||||
|
||||
match x {
|
||||
OptionalInt::Value(..) => println!("Got an int!"),
|
||||
OptionalInt::Missing => println!("No such luck."),
|
||||
}
|
||||
```
|
||||
|
||||
You can introduce **match guards** with `if`:
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(int),
|
||||
Missing,
|
||||
}
|
||||
|
||||
let x = OptionalInt::Value(5i);
|
||||
|
||||
match x {
|
||||
OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
|
||||
OptionalInt::Value(..) => println!("Got an int!"),
|
||||
OptionalInt::Missing => println!("No such luck."),
|
||||
}
|
||||
```
|
||||
|
||||
If you're matching on a pointer, you can use the same syntax as you declared it
|
||||
with. First, `&`:
|
||||
|
||||
```{rust}
|
||||
let x = &5i;
|
||||
|
||||
match x {
|
||||
&val => println!("Got a value: {}", val),
|
||||
}
|
||||
```
|
||||
|
||||
Here, the `val` inside the `match` has type `int`. In other words, the left-hand
|
||||
side of the pattern destructures the value. If we have `&5i`, then in `&val`, `val`
|
||||
would be `5i`.
|
||||
|
||||
If you want to get a reference, use the `ref` keyword:
|
||||
|
||||
```{rust}
|
||||
let x = 5i;
|
||||
|
||||
match x {
|
||||
ref r => println!("Got a reference to {}", r),
|
||||
}
|
||||
```
|
||||
|
||||
Here, the `r` inside the `match` has the type `&int`. In other words, the `ref`
|
||||
keyword _creates_ a reference, for use in the pattern. If you need a mutable
|
||||
reference, `ref mut` will work in the same way:
|
||||
|
||||
```{rust}
|
||||
let mut x = 5i;
|
||||
|
||||
match x {
|
||||
ref mut mr => println!("Got a mutable reference to {}", mr),
|
||||
}
|
||||
```
|
||||
|
||||
If you have a struct, you can destructure it inside of a pattern:
|
||||
|
||||
```{rust}
|
||||
# #![allow(non_shorthand_field_patterns)]
|
||||
struct Point {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
let origin = Point { x: 0i, y: 0i };
|
||||
|
||||
match origin {
|
||||
Point { x: x, y: y } => println!("({},{})", x, y),
|
||||
}
|
||||
```
|
||||
|
||||
If we only care about some of the values, we don't have to give them all names:
|
||||
|
||||
```{rust}
|
||||
# #![allow(non_shorthand_field_patterns)]
|
||||
struct Point {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
let origin = Point { x: 0i, y: 0i };
|
||||
|
||||
match origin {
|
||||
Point { x: x, .. } => println!("x is {}", x),
|
||||
}
|
||||
```
|
||||
|
||||
You can do this kind of match on any member, not just the first:
|
||||
|
||||
```{rust}
|
||||
# #![allow(non_shorthand_field_patterns)]
|
||||
struct Point {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
let origin = Point { x: 0i, y: 0i };
|
||||
|
||||
match origin {
|
||||
Point { y: y, .. } => println!("y is {}", y),
|
||||
}
|
||||
```
|
||||
|
||||
If you want to match against a slice or array, you can use `[]`:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
let v = vec!["match_this", "1"];
|
||||
|
||||
match v.as_slice() {
|
||||
["match_this", second] => println!("The second element is {}", second),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Whew! That's a lot of different ways to match things, and they can all be
|
||||
mixed and matched, depending on what you're doing:
|
||||
|
||||
```{rust,ignore}
|
||||
match x {
|
||||
Foo { x: Some(ref name), y: None } => ...
|
||||
}
|
||||
```
|
||||
|
||||
Patterns are very powerful. Make good use of them.
|
|
@ -0,0 +1,159 @@
|
|||
# Standard Input
|
||||
|
||||
Getting input from the keyboard is pretty easy, but uses some things
|
||||
we haven't seen before. Here's a simple program that reads some input,
|
||||
and then prints it back out:
|
||||
|
||||
```{rust,ignore}
|
||||
fn main() {
|
||||
println!("Type something!");
|
||||
|
||||
let input = std::io::stdin().read_line().ok().expect("Failed to read line");
|
||||
|
||||
println!("{}", input);
|
||||
}
|
||||
```
|
||||
|
||||
Let's go over these chunks, one by one:
|
||||
|
||||
```{rust,ignore}
|
||||
std::io::stdin();
|
||||
```
|
||||
|
||||
This calls a function, `stdin()`, that lives inside the `std::io` module. As
|
||||
you can imagine, everything in `std` is provided by Rust, the 'standard
|
||||
library.' We'll talk more about the module system later.
|
||||
|
||||
Since writing the fully qualified name all the time is annoying, we can use
|
||||
the `use` statement to import it in:
|
||||
|
||||
```{rust}
|
||||
use std::io::stdin;
|
||||
|
||||
stdin();
|
||||
```
|
||||
|
||||
However, it's considered better practice to not import individual functions, but
|
||||
to import the module, and only use one level of qualification:
|
||||
|
||||
```{rust}
|
||||
use std::io;
|
||||
|
||||
io::stdin();
|
||||
```
|
||||
|
||||
Let's update our example to use this style:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Type something!");
|
||||
|
||||
let input = io::stdin().read_line().ok().expect("Failed to read line");
|
||||
|
||||
println!("{}", input);
|
||||
}
|
||||
```
|
||||
|
||||
Next up:
|
||||
|
||||
```{rust,ignore}
|
||||
.read_line()
|
||||
```
|
||||
|
||||
The `read_line()` method can be called on the result of `stdin()` to return
|
||||
a full line of input. Nice and easy.
|
||||
|
||||
```{rust,ignore}
|
||||
.ok().expect("Failed to read line");
|
||||
```
|
||||
|
||||
Do you remember this code?
|
||||
|
||||
```{rust}
|
||||
enum OptionalInt {
|
||||
Value(i32),
|
||||
Missing,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = OptionalInt::Value(5);
|
||||
let y = OptionalInt::Missing;
|
||||
|
||||
match x {
|
||||
OptionalInt::Value(n) => println!("x is {}", n),
|
||||
OptionalInt::Missing => println!("x is missing!"),
|
||||
}
|
||||
|
||||
match y {
|
||||
OptionalInt::Value(n) => println!("y is {}", n),
|
||||
OptionalInt::Missing => println!("y is missing!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We had to match each time to see if we had a value or not. In this case,
|
||||
though, we _know_ that `x` has a `Value`, but `match` forces us to handle
|
||||
the `missing` case. This is what we want 99% of the time, but sometimes, we
|
||||
know better than the compiler.
|
||||
|
||||
Likewise, `read_line()` does not return a line of input. It _might_ return a
|
||||
line of input, though it might also fail to do so. This could happen if our program
|
||||
isn't running in a terminal, but as part of a cron job, or some other context
|
||||
where there's no standard input. Because of this, `read_line` returns a type
|
||||
very similar to our `OptionalInt`: an `IoResult<T>`. We haven't talked about
|
||||
`IoResult<T>` yet because it is the **generic** form of our `OptionalInt`.
|
||||
Until then, you can think of it as being the same thing, just for any type –
|
||||
not just `i32`s.
|
||||
|
||||
Rust provides a method on these `IoResult<T>`s called `ok()`, which does the
|
||||
same thing as our `match` statement but assumes that we have a valid value.
|
||||
We then call `expect()` on the result, which will terminate our program if we
|
||||
don't have a valid value. In this case, if we can't get input, our program
|
||||
doesn't work, so we're okay with that. In most cases, we would want to handle
|
||||
the error case explicitly. `expect()` allows us to give an error message if
|
||||
this crash happens.
|
||||
|
||||
We will cover the exact details of how all of this works later in the Guide.
|
||||
For now, this gives you enough of a basic understanding to work with.
|
||||
|
||||
Back to the code we were working on! Here's a refresher:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Type something!");
|
||||
|
||||
let input = io::stdin().read_line().ok().expect("Failed to read line");
|
||||
|
||||
println!("{}", input);
|
||||
}
|
||||
```
|
||||
|
||||
With long lines like this, Rust gives you some flexibility with the whitespace.
|
||||
We _could_ write the example like this:
|
||||
|
||||
```{rust,ignore}
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("Type something!");
|
||||
|
||||
// here, we'll show the types at each step
|
||||
|
||||
let input = io::stdin() // std::io::stdio::StdinReader
|
||||
.read_line() // IoResult<String>
|
||||
.ok() // Option<String>
|
||||
.expect("Failed to read line"); // String
|
||||
|
||||
println!("{}", input);
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes, this makes things more readable – sometimes, less. Use your judgement
|
||||
here.
|
||||
|
||||
That's all you need to get basic input from the standard input! It's not too
|
||||
complicated, but there are a number of small parts.
|
|
@ -0,0 +1,79 @@
|
|||
# Strings
|
||||
|
||||
Strings are an important concept for any programmer to master. Rust's string
|
||||
handling system is a bit different from other languages, due to its systems
|
||||
focus. Any time you have a data structure of variable size, things can get
|
||||
tricky, and strings are a re-sizable data structure. That being said, Rust's
|
||||
strings also work differently than in some other systems languages, such as C.
|
||||
|
||||
Let's dig into the details. A **string** is a sequence of Unicode scalar values
|
||||
encoded as a stream of UTF-8 bytes. All strings are guaranteed to be
|
||||
validly encoded UTF-8 sequences. Additionally, strings are not null-terminated
|
||||
and can contain null bytes.
|
||||
|
||||
Rust has two main types of strings: `&str` and `String`.
|
||||
|
||||
The first kind is a `&str`. This is pronounced a 'string slice.' String literals
|
||||
are of the type `&str`:
|
||||
|
||||
```{rust}
|
||||
let string = "Hello there."; // string: &str
|
||||
```
|
||||
|
||||
This string is statically allocated, meaning that it's saved inside our
|
||||
compiled program, and exists for the entire duration it runs. The `string`
|
||||
binding is a reference to this statically allocated string. String slices
|
||||
have a fixed size, and cannot be mutated.
|
||||
|
||||
A `String`, on the other hand, is an in-memory string. This string is
|
||||
growable, and is also guaranteed to be UTF-8.
|
||||
|
||||
```{rust}
|
||||
let mut s = "Hello".to_string(); // mut s: String
|
||||
println!("{}", s);
|
||||
|
||||
s.push_str(", world.");
|
||||
println!("{}", s);
|
||||
```
|
||||
|
||||
You can get a `&str` view into a `String` with the `as_slice()` method:
|
||||
|
||||
```{rust}
|
||||
fn takes_slice(slice: &str) {
|
||||
println!("Got: {}", slice);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = "Hello".to_string();
|
||||
takes_slice(s.as_slice());
|
||||
}
|
||||
```
|
||||
|
||||
To compare a String to a constant string, prefer `as_slice()`...
|
||||
|
||||
```{rust}
|
||||
fn compare(string: String) {
|
||||
if string.as_slice() == "Hello" {
|
||||
println!("yes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
... over `to_string()`:
|
||||
|
||||
```{rust}
|
||||
fn compare(string: String) {
|
||||
if string == "Hello".to_string() {
|
||||
println!("yes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Viewing a `String` as a `&str` is cheap, but converting the `&str` to a
|
||||
`String` involves allocating memory. No reason to do that unless you have to!
|
||||
|
||||
That's the basics of strings in Rust! They're probably a bit more complicated
|
||||
than you are used to, if you come from a scripting language, but when the
|
||||
low-level details matter, they really matter. Just remember that `String`s
|
||||
allocate memory and control their data, while `&str`s are a reference to
|
||||
another string, and you'll be all set.
|
|
@ -0,0 +1,317 @@
|
|||
% Traits
|
||||
|
||||
Do you remember the `impl` keyword, used to call a function with method
|
||||
syntax?
|
||||
|
||||
```{rust}
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
fn area(&self) -> f64 {
|
||||
std::f64::consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Traits are similar, except that we define a trait with just the method
|
||||
signature, then implement the trait for that struct. Like this:
|
||||
|
||||
```{rust}
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
std::f64::consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the `trait` block looks very similar to the `impl` block,
|
||||
but we don't define a body, just a type signature. When we `impl` a trait,
|
||||
we use `impl Trait for Item`, rather than just `impl Item`.
|
||||
|
||||
So what's the big deal? Remember the error we were getting with our generic
|
||||
`inverse` function?
|
||||
|
||||
```text
|
||||
error: binary operation `==` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
We can use traits to constrain our generics. Consider this function, which
|
||||
does not compile, and gives us a similar error:
|
||||
|
||||
```{rust,ignore}
|
||||
fn print_area<T>(shape: T) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
```
|
||||
|
||||
Rust complains:
|
||||
|
||||
```text
|
||||
error: type `T` does not implement any method in scope named `area`
|
||||
```
|
||||
|
||||
Because `T` can be any type, we can't be sure that it implements the `area`
|
||||
method. But we can add a **trait constraint** to our generic `T`, ensuring
|
||||
that it does:
|
||||
|
||||
```{rust}
|
||||
# trait HasArea {
|
||||
# fn area(&self) -> f64;
|
||||
# }
|
||||
fn print_area<T: HasArea>(shape: T) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
```
|
||||
|
||||
The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
|
||||
Because traits define function type signatures, we can be sure that any type
|
||||
which implements `HasArea` will have an `.area()` method.
|
||||
|
||||
Here's an extended example of how this works:
|
||||
|
||||
```{rust}
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
std::f64::consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
|
||||
struct Square {
|
||||
x: f64,
|
||||
y: f64,
|
||||
side: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Square {
|
||||
fn area(&self) -> f64 {
|
||||
self.side * self.side
|
||||
}
|
||||
}
|
||||
|
||||
fn print_area<T: HasArea>(shape: T) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Circle {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
radius: 1.0f64,
|
||||
};
|
||||
|
||||
let s = Square {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
side: 1.0f64,
|
||||
};
|
||||
|
||||
print_area(c);
|
||||
print_area(s);
|
||||
}
|
||||
```
|
||||
|
||||
This program outputs:
|
||||
|
||||
```text
|
||||
This shape has an area of 3.141593
|
||||
This shape has an area of 1
|
||||
```
|
||||
|
||||
As you can see, `print_area` is now generic, but also ensures that we
|
||||
have passed in the correct types. If we pass in an incorrect type:
|
||||
|
||||
```{rust,ignore}
|
||||
print_area(5i);
|
||||
```
|
||||
|
||||
We get a compile-time error:
|
||||
|
||||
```text
|
||||
error: failed to find an implementation of trait main::HasArea for int
|
||||
```
|
||||
|
||||
So far, we've only added trait implementations to structs, but you can
|
||||
implement a trait for any type. So technically, we _could_ implement
|
||||
`HasArea` for `int`:
|
||||
|
||||
```{rust}
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
impl HasArea for int {
|
||||
fn area(&self) -> f64 {
|
||||
println!("this is silly");
|
||||
|
||||
*self as f64
|
||||
}
|
||||
}
|
||||
|
||||
5i.area();
|
||||
```
|
||||
|
||||
It is considered poor style to implement methods on such primitive types, even
|
||||
though it is possible.
|
||||
|
||||
This may seem like the Wild West, but there are two other restrictions around
|
||||
implementing traits that prevent this from getting out of hand. First, traits
|
||||
must be `use`d in any scope where you wish to use the trait's method. So for
|
||||
example, this does not work:
|
||||
|
||||
```{rust,ignore}
|
||||
mod shapes {
|
||||
use std::f64::consts;
|
||||
|
||||
trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = shapes::Circle {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
radius: 1.0f64,
|
||||
};
|
||||
|
||||
println!("{}", c.area());
|
||||
}
|
||||
```
|
||||
|
||||
Now that we've moved the structs and traits into their own module, we get an
|
||||
error:
|
||||
|
||||
```text
|
||||
error: type `shapes::Circle` does not implement any method in scope named `area`
|
||||
```
|
||||
|
||||
If we add a `use` line right above `main` and make the right things public,
|
||||
everything is fine:
|
||||
|
||||
```{rust}
|
||||
use shapes::HasArea;
|
||||
|
||||
mod shapes {
|
||||
use std::f64::consts;
|
||||
|
||||
pub trait HasArea {
|
||||
fn area(&self) -> f64;
|
||||
}
|
||||
|
||||
pub struct Circle {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub radius: f64,
|
||||
}
|
||||
|
||||
impl HasArea for Circle {
|
||||
fn area(&self) -> f64 {
|
||||
consts::PI * (self.radius * self.radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let c = shapes::Circle {
|
||||
x: 0.0f64,
|
||||
y: 0.0f64,
|
||||
radius: 1.0f64,
|
||||
};
|
||||
|
||||
println!("{}", c.area());
|
||||
}
|
||||
```
|
||||
|
||||
This means that even if someone does something bad like add methods to `int`,
|
||||
it won't affect you, unless you `use` that trait.
|
||||
|
||||
There's one more restriction on implementing traits. Either the trait or the
|
||||
type you're writing the `impl` for must be inside your crate. So, we could
|
||||
implement the `HasArea` type for `int`, because `HasArea` is in our crate. But
|
||||
if we tried to implement `Float`, a trait provided by Rust, for `int`, we could
|
||||
not, because both the trait and the type aren't in our crate.
|
||||
|
||||
One last thing about traits: generic functions with a trait bound use
|
||||
**monomorphization** ("mono": one, "morph": form), so they are statically
|
||||
dispatched. What's that mean? Well, let's take a look at `print_area` again:
|
||||
|
||||
```{rust,ignore}
|
||||
fn print_area<T: HasArea>(shape: T) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Circle { ... };
|
||||
|
||||
let s = Square { ... };
|
||||
|
||||
print_area(c);
|
||||
print_area(s);
|
||||
}
|
||||
```
|
||||
|
||||
When we use this trait with `Circle` and `Square`, Rust ends up generating
|
||||
two different functions with the concrete type, and replacing the call sites with
|
||||
calls to the concrete implementations. In other words, you get something like
|
||||
this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn __print_area_circle(shape: Circle) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
|
||||
fn __print_area_square(shape: Square) {
|
||||
println!("This shape has an area of {}", shape.area());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Circle { ... };
|
||||
|
||||
let s = Square { ... };
|
||||
|
||||
__print_area_circle(c);
|
||||
__print_area_square(s);
|
||||
}
|
||||
```
|
||||
|
||||
The names don't actually change to this, it's just for illustration. But
|
||||
as you can see, there's no overhead of deciding which version to call here,
|
||||
hence 'statically dispatched.' The downside is that we have two copies of
|
||||
the same function, so our binary is a little bit larger.
|
|
@ -0,0 +1,174 @@
|
|||
# Variable bindings
|
||||
|
||||
The first thing we'll learn about are 'variable bindings.' They look like this:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
let x = 5;
|
||||
}
|
||||
```
|
||||
|
||||
Putting `fn main() {` in each example is a bit tedious, so we'll leave that out
|
||||
in the future. If you're following along, make sure to edit your `main()`
|
||||
function, rather than leaving it off. Otherwise, you'll get an error.
|
||||
|
||||
In many languages, this is called a 'variable.' But Rust's variable bindings
|
||||
have a few tricks up their sleeves. Rust has a very powerful feature called
|
||||
'pattern matching' that we'll get into detail with later, but the left
|
||||
hand side of a `let` expression is a full pattern, not just a variable name.
|
||||
This means we can do things like:
|
||||
|
||||
```{rust}
|
||||
let (x, y) = (1, 2);
|
||||
```
|
||||
|
||||
After this expression is evaluated, `x` will be one, and `y` will be two.
|
||||
Patterns are really powerful, but this is about all we can do with them so far.
|
||||
So let's just keep this in the back of our minds as we go forward.
|
||||
|
||||
Rust is a statically typed language, which means that we specify our types up
|
||||
front. So why does our first example compile? Well, Rust has this thing called
|
||||
"type inference." If it can figure out what the type of something is, Rust
|
||||
doesn't require you to actually type it out.
|
||||
|
||||
We can add the type if we want to, though. Types come after a colon (`:`):
|
||||
|
||||
```{rust}
|
||||
let x: i32 = 5;
|
||||
```
|
||||
|
||||
If I asked you to read this out loud to the rest of the class, you'd say "`x`
|
||||
is a binding with the type `i32` and the value `five`."
|
||||
|
||||
In future examples, we may annotate the type in a comment. The examples will
|
||||
look like this:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
let x = 5; // x: i32
|
||||
}
|
||||
```
|
||||
|
||||
Note the similarities between this annotation and the syntax you use with `let`.
|
||||
Including these kinds of comments is not idiomatic Rust, but we'll occasionally
|
||||
include them to help you understand what the types that Rust infers are.
|
||||
|
||||
By default, bindings are **immutable**. This code will not compile:
|
||||
|
||||
```{ignore}
|
||||
let x = 5;
|
||||
x = 10;
|
||||
```
|
||||
|
||||
It will give you this error:
|
||||
|
||||
```text
|
||||
error: re-assignment of immutable variable `x`
|
||||
x = 10;
|
||||
^~~~~~~
|
||||
```
|
||||
|
||||
If you want a binding to be mutable, you can use `mut`:
|
||||
|
||||
```{rust}
|
||||
let mut x = 5; // mut x: i32
|
||||
x = 10;
|
||||
```
|
||||
|
||||
There is no single reason that bindings are immutable by default, but we can
|
||||
think about it through one of Rust's primary focuses: safety. If you forget to
|
||||
say `mut`, the compiler will catch it, and let you know that you have mutated
|
||||
something you may not have intended to mutate. If bindings were mutable by
|
||||
default, the compiler would not be able to tell you this. If you _did_ intend
|
||||
mutation, then the solution is quite easy: add `mut`.
|
||||
|
||||
There are other good reasons to avoid mutable state when possible, but they're
|
||||
out of the scope of this guide. In general, you can often avoid explicit
|
||||
mutation, and so it is preferable in Rust. That said, sometimes, mutation is
|
||||
what you need, so it's not verboten.
|
||||
|
||||
Let's get back to bindings. Rust variable bindings have one more aspect that
|
||||
differs from other languages: bindings are required to be initialized with a
|
||||
value before you're allowed to use them. If we try...
|
||||
|
||||
```{ignore}
|
||||
let x;
|
||||
```
|
||||
|
||||
...we'll get an error:
|
||||
|
||||
```text
|
||||
src/main.rs:2:9: 2:10 error: cannot determine a type for this local variable: unconstrained type
|
||||
src/main.rs:2 let x;
|
||||
^
|
||||
```
|
||||
|
||||
Giving it a type will compile, though:
|
||||
|
||||
```{rust}
|
||||
let x: i32;
|
||||
```
|
||||
|
||||
Let's try it out. Change your `src/main.rs` file to look like this:
|
||||
|
||||
```{rust}
|
||||
fn main() {
|
||||
let x: i32;
|
||||
|
||||
println!("Hello world!");
|
||||
}
|
||||
```
|
||||
|
||||
You can use `cargo build` on the command line to build it. You'll get a warning,
|
||||
but it will still print "Hello, world!":
|
||||
|
||||
```text
|
||||
Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
|
||||
src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] on by default
|
||||
src/main.rs:2 let x: i32;
|
||||
^
|
||||
```
|
||||
|
||||
Rust warns us that we never use the variable binding, but since we never use it,
|
||||
no harm, no foul. Things change if we try to actually use this `x`, however. Let's
|
||||
do that. Change your program to look like this:
|
||||
|
||||
```{rust,ignore}
|
||||
fn main() {
|
||||
let x: i32;
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
And try to build it. You'll get an error:
|
||||
|
||||
```{bash}
|
||||
$ cargo build
|
||||
Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
|
||||
src/main.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x`
|
||||
src/main.rs:4 println!("The value of x is: {}", x);
|
||||
^
|
||||
note: in expansion of format_args!
|
||||
<std macros>:2:23: 2:77 note: expansion site
|
||||
<std macros>:1:1: 3:2 note: in expansion of println!
|
||||
src/main.rs:4:5: 4:42 note: expansion site
|
||||
error: aborting due to previous error
|
||||
Could not compile `hello_world`.
|
||||
```
|
||||
|
||||
Rust will not let us use a value that has not been initialized. Next, let's
|
||||
talk about this stuff we've added to `println!`.
|
||||
|
||||
If you include two curly braces (`{}`, some call them moustaches...) in your
|
||||
string to print, Rust will interpret this as a request to interpolate some sort
|
||||
of value. **String interpolation** is a computer science term that means "stick
|
||||
in the middle of a string." We add a comma, and then `x`, to indicate that we
|
||||
want `x` to be the value we're interpolating. The comma is used to separate
|
||||
arguments we pass to functions and macros, if you're passing more than one.
|
||||
|
||||
When you just use the curly braces, Rust will attempt to display the
|
||||
value in a meaningful way by checking out its type. If you want to specify the
|
||||
format in a more detailed manner, there are a [wide number of options
|
||||
available](std/fmt/index.html). For now, we'll just stick to the default:
|
||||
integers aren't very complicated to print.
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Basic data structures for representing a book.
|
||||
|
||||
use std::io::BufferedReader;
|
||||
use std::iter;
|
||||
use std::iter::AdditiveIterator;
|
||||
use regex::Regex;
|
||||
|
||||
pub struct BookItem {
|
||||
pub title: String,
|
||||
pub path: Path,
|
||||
pub path_to_root: Path,
|
||||
pub children: Vec<BookItem>,
|
||||
}
|
||||
|
||||
pub struct Book {
|
||||
pub chapters: Vec<BookItem>,
|
||||
}
|
||||
|
||||
/// A depth-first iterator over a book.
|
||||
pub struct BookItems<'a> {
|
||||
cur_items: &'a [BookItem],
|
||||
cur_idx: uint,
|
||||
stack: Vec<(&'a [BookItem], uint)>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BookItems<'a> {
|
||||
type Item = (String, &'a BookItem);
|
||||
|
||||
fn next(&mut self) -> Option<(String, &'a BookItem)> {
|
||||
loop {
|
||||
if self.cur_idx >= self.cur_items.len() {
|
||||
match self.stack.pop() {
|
||||
None => return None,
|
||||
Some((parent_items, parent_idx)) => {
|
||||
self.cur_items = parent_items;
|
||||
self.cur_idx = parent_idx + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let cur = self.cur_items.get(self.cur_idx).unwrap();
|
||||
|
||||
let mut section = "".to_string();
|
||||
for &(_, idx) in self.stack.iter() {
|
||||
section.push_str(&(idx + 1).to_string()[]);
|
||||
section.push('.');
|
||||
}
|
||||
section.push_str(&(self.cur_idx + 1).to_string()[]);
|
||||
section.push('.');
|
||||
|
||||
self.stack.push((self.cur_items, self.cur_idx));
|
||||
self.cur_items = &cur.children[];
|
||||
self.cur_idx = 0;
|
||||
return Some((section, cur))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Book {
|
||||
pub fn iter(&self) -> BookItems {
|
||||
BookItems {
|
||||
cur_items: &self.chapters[],
|
||||
cur_idx: 0,
|
||||
stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a book by parsing a summary (markdown table of contents).
|
||||
pub fn parse_summary<R: Reader>(input: R, src: &Path) -> Result<Book, Vec<String>> {
|
||||
fn collapse(stack: &mut Vec<BookItem>,
|
||||
top_items: &mut Vec<BookItem>,
|
||||
to_level: uint) {
|
||||
loop {
|
||||
if stack.len() < to_level { return }
|
||||
if stack.len() == 1 {
|
||||
top_items.push(stack.pop().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
let tip = stack.pop().unwrap();
|
||||
let last = stack.len() - 1;
|
||||
stack[last].children.push(tip);
|
||||
}
|
||||
}
|
||||
|
||||
let regex = r"(?P<indent>[\t ]*)\*[:space:]*\[(?P<title>.*)\]\((?P<path>.*)\)";
|
||||
let item_re = Regex::new(regex).unwrap();
|
||||
let mut top_items = vec!();
|
||||
let mut stack = vec!();
|
||||
let mut errors = vec!();
|
||||
|
||||
// always include the introduction
|
||||
top_items.push(BookItem {
|
||||
title: "Introduction".to_string(),
|
||||
path: Path::new("README.md"),
|
||||
path_to_root: Path::new("."),
|
||||
children: vec!(),
|
||||
});
|
||||
|
||||
for line_result in BufferedReader::new(input).lines() {
|
||||
let line = match line_result {
|
||||
Ok(line) => line,
|
||||
Err(err) => {
|
||||
errors.push(err.desc.to_string()); // FIXME: include detail
|
||||
return Err(errors);
|
||||
}
|
||||
};
|
||||
|
||||
item_re.captures(&line[]).map(|cap| {
|
||||
let given_path = cap.name("path");
|
||||
let title = cap.name("title").unwrap().to_string();
|
||||
|
||||
let path_from_root = match src.join(given_path.unwrap()).path_relative_from(src) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
errors.push(format!("Paths in SUMMARY.md must be relative, \
|
||||
but path '{}' for section '{}' is not.",
|
||||
given_path.unwrap(), title));
|
||||
Path::new("")
|
||||
}
|
||||
};
|
||||
let path_to_root = Path::new(iter::repeat("../")
|
||||
.take(path_from_root.components().count() - 1)
|
||||
.collect::<String>());
|
||||
let item = BookItem {
|
||||
title: title,
|
||||
path: path_from_root,
|
||||
path_to_root: path_to_root,
|
||||
children: vec!(),
|
||||
};
|
||||
let level = cap.name("indent").unwrap().chars().map(|c| {
|
||||
match c {
|
||||
' ' => 1u,
|
||||
'\t' => 4,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}).sum() / 4 + 1;
|
||||
|
||||
if level > stack.len() + 1 {
|
||||
// FIXME: better error message
|
||||
errors.push(format!("Section '{}' is indented too many levels.", item.title));
|
||||
} else if level <= stack.len() {
|
||||
collapse(&mut stack, &mut top_items, level);
|
||||
}
|
||||
stack.push(item)
|
||||
});
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
collapse(&mut stack, &mut top_items, 1);
|
||||
Ok(Book { chapters: top_items })
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation of the `build` subcommand, used to compile a book.
|
||||
|
||||
use std::os;
|
||||
use std::io;
|
||||
use std::io::{fs, File, BufferedWriter, TempDir, IoResult};
|
||||
|
||||
use subcommand::Subcommand;
|
||||
use term::Term;
|
||||
use error::{Error, CliResult, CommandResult};
|
||||
use book;
|
||||
use book::{Book, BookItem};
|
||||
use css;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use rustdoc;
|
||||
|
||||
struct Build;
|
||||
|
||||
pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
|
||||
if name == "build" {
|
||||
Some(box Build as Box<Subcommand>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn write_toc(book: &Book, path_to_root: &Path, out: &mut Writer) -> IoResult<()> {
|
||||
fn walk_items(items: &[BookItem],
|
||||
section: &str,
|
||||
path_to_root: &Path,
|
||||
out: &mut Writer) -> IoResult<()> {
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
try!(walk_item(item, &format!("{}{}.", section, i + 1)[], path_to_root, out));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn walk_item(item: &BookItem,
|
||||
section: &str,
|
||||
path_to_root: &Path,
|
||||
out: &mut Writer) -> IoResult<()> {
|
||||
try!(writeln!(out, "<li><a href='{}'><b>{}</b> {}</a>",
|
||||
path_to_root.join(item.path.with_extension("html")).display(),
|
||||
section,
|
||||
item.title));
|
||||
if !item.children.is_empty() {
|
||||
try!(writeln!(out, "<ul class='section'>"));
|
||||
let _ = walk_items(&item.children[], section, path_to_root, out);
|
||||
try!(writeln!(out, "</ul>"));
|
||||
}
|
||||
try!(writeln!(out, "</li>"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
try!(writeln!(out, "<div id='toc'>"));
|
||||
try!(writeln!(out, "<ul class='chapter'>"));
|
||||
try!(walk_items(&book.chapters[], "", path_to_root, out));
|
||||
try!(writeln!(out, "</ul>"));
|
||||
try!(writeln!(out, "</div>"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(book: &Book, tgt: &Path) -> CliResult<()> {
|
||||
let tmp = TempDir::new("rust-book")
|
||||
.ok()
|
||||
// FIXME: lift to Result instead
|
||||
.expect("could not create temporary directory");
|
||||
|
||||
for (section, item) in book.iter() {
|
||||
println!("{} {}", section, item.title);
|
||||
|
||||
let out_path = tgt.join(item.path.dirname());
|
||||
|
||||
let regex = r"\[(?P<title>[^]]*)\]\((?P<url_stem>[^)]*)\.(?P<ext>md|markdown)\)";
|
||||
let md_urls = Regex::new(regex).unwrap();
|
||||
|
||||
let src;
|
||||
if os::args().len() < 3 {
|
||||
src = os::getcwd().unwrap().clone();
|
||||
} else {
|
||||
src = Path::new(os::args()[2].clone());
|
||||
}
|
||||
// preprocess the markdown, rerouting markdown references to html references
|
||||
let markdown_data = try!(File::open(&src.join(&item.path)).read_to_string());
|
||||
let preprocessed_path = tmp.path().join(item.path.filename().unwrap());
|
||||
{
|
||||
let urls = md_urls.replace_all(&markdown_data[], "[$title]($url_stem.html)");
|
||||
try!(File::create(&preprocessed_path)
|
||||
.write_str(&urls[]));
|
||||
}
|
||||
|
||||
// write the prelude to a temporary HTML file for rustdoc inclusion
|
||||
let prelude = tmp.path().join("prelude.html");
|
||||
{
|
||||
let mut toc = BufferedWriter::new(try!(File::create(&prelude)));
|
||||
let _ = write_toc(book, &item.path_to_root, &mut toc);
|
||||
try!(writeln!(&mut toc, "<div id='page-wrapper'>"));
|
||||
try!(writeln!(&mut toc, "<div id='page'>"));
|
||||
}
|
||||
|
||||
// write the postlude to a temporary HTML file for rustdoc inclusion
|
||||
let postlude = tmp.path().join("postlude.html");
|
||||
{
|
||||
let mut toc = BufferedWriter::new(try!(File::create(&postlude)));
|
||||
try!(writeln!(&mut toc, "</div></div>"));
|
||||
}
|
||||
|
||||
try!(fs::mkdir_recursive(&out_path, io::USER_DIR));
|
||||
|
||||
let rustdoc_args: &[String] = &[
|
||||
"".to_string(),
|
||||
preprocessed_path.display().to_string(),
|
||||
format!("-o{}", out_path.display()),
|
||||
format!("--html-before-content={}", prelude.display()),
|
||||
format!("--html-after-content={}", postlude.display()),
|
||||
format!("--markdown-css={}", item.path_to_root.join("rust-book.css").display()),
|
||||
"--markdown-no-toc".to_string(),
|
||||
];
|
||||
let output_result = rustdoc::main_args(rustdoc_args);
|
||||
if output_result != 0 {
|
||||
|
||||
let message = format!("Could not execute `rustdoc`: {}", output_result);
|
||||
return Err(box message as Box<Error>);
|
||||
}
|
||||
}
|
||||
|
||||
// create index.html from the root README
|
||||
try!(fs::copy(&tgt.join("README.html"), &tgt.join("index.html")));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Subcommand for Build {
|
||||
fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn usage(&self) {}
|
||||
fn execute(&mut self, term: &mut Term) -> CommandResult<()> {
|
||||
let cwd = os::getcwd().unwrap();
|
||||
let src;
|
||||
let tgt;
|
||||
|
||||
if os::args().len() < 3 {
|
||||
src = cwd.clone();
|
||||
} else {
|
||||
src = Path::new(os::args()[2].clone());
|
||||
}
|
||||
|
||||
if os::args().len() < 4 {
|
||||
tgt = cwd.join("_book");
|
||||
} else {
|
||||
tgt = Path::new(os::args()[3].clone());
|
||||
}
|
||||
|
||||
let _ = fs::mkdir(&tgt, io::USER_DIR); // FIXME: handle errors
|
||||
|
||||
// FIXME: handle errors
|
||||
let _ = File::create(&tgt.join("rust-book.css")).write_str(css::STYLE);
|
||||
|
||||
let summary = File::open(&src.join("SUMMARY.md"));
|
||||
match book::parse_summary(summary, &src) {
|
||||
Ok(book) => {
|
||||
// execute rustdoc on the whole book
|
||||
let _ = render(&book, &tgt).map_err(|err| {
|
||||
term.err(&format!("error: {}", err.description())[]);
|
||||
err.detail().map(|detail| {
|
||||
term.err(&format!("detail: {}", detail)[]);
|
||||
})
|
||||
});
|
||||
}
|
||||
Err(errors) => {
|
||||
for err in errors.into_iter() {
|
||||
term.err(&err[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(()) // lol
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// The rust-book CSS in string form.
|
||||
|
||||
pub static STYLE: &'static str = r#"
|
||||
@import url("//static.rust-lang.org/doc/master/rust.css");
|
||||
|
||||
body {
|
||||
max-width:none;
|
||||
}
|
||||
|
||||
#toc {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
width: 250px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.07);
|
||||
padding: 10px 10px;
|
||||
font-size: 16px;
|
||||
background: none repeat scroll 0% 0% #FFF;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
position: absolute;
|
||||
overflow-y: auto;
|
||||
left: 260px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
box-sizing: border-box;
|
||||
background: none repeat scroll 0% 0% #FFF;
|
||||
}
|
||||
|
||||
#page {
|
||||
margin-left: auto;
|
||||
margin-right:auto;
|
||||
width: 750px;
|
||||
}
|
||||
|
||||
.chapter {
|
||||
list-style: none outside none;
|
||||
padding-left: 0px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.section {
|
||||
list-style: none outside none;
|
||||
padding-left: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.section li {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chapter li a {
|
||||
color: #000000;
|
||||
}
|
||||
"#;
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Error handling utilities. WIP.
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Show, Formatter};
|
||||
|
||||
use std::io::IoError;
|
||||
|
||||
pub type CliError = Box<Error + 'static>;
|
||||
pub type CliResult<T> = Result<T, CliError>;
|
||||
|
||||
pub type CommandError = Box<Error + 'static>;
|
||||
pub type CommandResult<T> = Result<T, CommandError>;
|
||||
|
||||
pub trait Error {
|
||||
fn description(&self) -> &str;
|
||||
|
||||
fn detail(&self) -> Option<&str> { None }
|
||||
fn cause(&self) -> Option<&Error> { None }
|
||||
}
|
||||
|
||||
pub trait FromError<E> {
|
||||
fn from_err(err: E) -> Self;
|
||||
}
|
||||
|
||||
impl Show for Box<Error + 'static> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Error + 'static> FromError<E> for Box<Error + 'static> {
|
||||
fn from_err(err: E) -> Box<Error + 'static> {
|
||||
box err as Box<Error>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Error for &'a str {
|
||||
fn description<'b>(&'b self) -> &'b str {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for String {
|
||||
fn description<'a>(&'a self) -> &'a str {
|
||||
&self[]
|
||||
}
|
||||
}
|
||||
|
||||
impl FromError<()> for () {
|
||||
fn from_err(_: ()) -> () { () }
|
||||
}
|
||||
|
||||
impl FromError<IoError> for IoError {
|
||||
fn from_err(error: IoError) -> IoError { error }
|
||||
}
|
||||
|
||||
impl Error for IoError {
|
||||
fn description(&self) -> &str {
|
||||
self.desc
|
||||
}
|
||||
fn detail(&self) -> Option<&str> {
|
||||
self.detail.as_ref().map(|s| &s[])
|
||||
}
|
||||
}
|
||||
|
||||
//fn iter_map_err<T, U, E, I: Iterator<Result<T,E>>>(iter: I,
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation of the `help` subcommand. Currently just prints basic usage info.
|
||||
|
||||
use subcommand::Subcommand;
|
||||
use error::CliResult;
|
||||
use error::CommandResult;
|
||||
use term::Term;
|
||||
|
||||
struct Help;
|
||||
|
||||
pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
|
||||
match name {
|
||||
"help" | "--help" | "-h" | "-?" => Some(box Help as Box<Subcommand>),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
impl Subcommand for Help {
|
||||
fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn usage(&self) {}
|
||||
fn execute(&mut self, _: &mut Term) -> CommandResult<()> {
|
||||
usage();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn usage() {
|
||||
println!("Usage: rust-book <command> [<args>]");
|
||||
println!("");
|
||||
println!("The <command> must be one of:");
|
||||
println!(" help Print this message.");
|
||||
println!(" build Build the book in subdirectory _book");
|
||||
println!(" serve --NOT YET IMPLEMENTED--");
|
||||
println!(" test --NOT YET IMPLEMENTED--");
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(slicing_syntax, box_syntax)]
|
||||
|
||||
extern crate regex;
|
||||
|
||||
extern crate rustdoc;
|
||||
|
||||
use std::os;
|
||||
use subcommand::Subcommand;
|
||||
use term::Term;
|
||||
|
||||
macro_rules! try (
|
||||
($expr:expr) => ({
|
||||
use error;
|
||||
match $expr {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Err(error::FromError::from_err(err))
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
mod term;
|
||||
mod error;
|
||||
mod book;
|
||||
|
||||
mod subcommand;
|
||||
mod help;
|
||||
mod build;
|
||||
mod serve;
|
||||
mod test;
|
||||
|
||||
mod css;
|
||||
|
||||
#[cfg(not(test))] // thanks #12327
|
||||
fn main() {
|
||||
let mut term = Term::new();
|
||||
let cmd = os::args();
|
||||
|
||||
if cmd.len() < 1 {
|
||||
help::usage()
|
||||
} else {
|
||||
match subcommand::parse_name(&cmd[1][]) {
|
||||
Some(mut subcmd) => {
|
||||
match subcmd.parse_args(cmd.tail()) {
|
||||
Ok(_) => {
|
||||
match subcmd.execute(&mut term) {
|
||||
Ok(_) => (),
|
||||
Err(_) => os::set_exit_status(-1),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err.description());
|
||||
println!("");
|
||||
subcmd.usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
println!("Unrecognized command '{}'.", cmd[1]);
|
||||
println!("");
|
||||
help::usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation of the `serve` subcommand. Just a stub for now.
|
||||
|
||||
use subcommand::Subcommand;
|
||||
use error::CliResult;
|
||||
use error::CommandResult;
|
||||
use term::Term;
|
||||
|
||||
struct Serve;
|
||||
|
||||
pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
|
||||
if name == "serve" {
|
||||
Some(box Serve as Box<Subcommand>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Subcommand for Serve {
|
||||
fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn usage(&self) {}
|
||||
fn execute(&mut self, _: &mut Term) -> CommandResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Common API for all rust-book subcommands.
|
||||
|
||||
use error::CliResult;
|
||||
use error::CommandResult;
|
||||
use term::Term;
|
||||
|
||||
use help;
|
||||
use build;
|
||||
use serve;
|
||||
use test;
|
||||
|
||||
pub trait Subcommand {
|
||||
/// Mutate the subcommand by parsing its arguments.
|
||||
///
|
||||
/// Returns `Err` on a parsing error.
|
||||
fn parse_args(&mut self, args: &[String]) -> CliResult<()>;
|
||||
/// Print the CLI usage information.
|
||||
fn usage(&self);
|
||||
/// Actually execute the subcommand.
|
||||
fn execute(&mut self, term: &mut Term) -> CommandResult<()>;
|
||||
}
|
||||
|
||||
/// Create a Subcommand object based on its name.
|
||||
pub fn parse_name(name: &str) -> Option<Box<Subcommand>> {
|
||||
for parser in [
|
||||
help::parse_cmd as fn(&str) -> Option<Box<Subcommand>>,
|
||||
build::parse_cmd as fn(&str) -> Option<Box<Subcommand>>,
|
||||
serve::parse_cmd as fn(&str) -> Option<Box<Subcommand>>,
|
||||
test::parse_cmd as fn(&str) -> Option<Box<Subcommand>>].iter() {
|
||||
let parsed = (*parser)(name);
|
||||
if parsed.is_some() { return parsed }
|
||||
}
|
||||
None
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! An abstraction of the terminal. Eventually, provide color and
|
||||
//! verbosity support. For now, just a wrapper around stdout/stderr.
|
||||
|
||||
use std::io::stdio;
|
||||
|
||||
pub struct Term {
|
||||
err: Box<Writer + 'static>
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn new() -> Term {
|
||||
Term {
|
||||
err: box stdio::stderr() as Box<Writer>,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err(&mut self, msg: &str) {
|
||||
// swallow any errors
|
||||
let _ = self.err.write_line(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation of the `test` subcommand. Just a stub for now.
|
||||
|
||||
use subcommand::Subcommand;
|
||||
use error::CliResult;
|
||||
use error::CommandResult;
|
||||
use error::Error;
|
||||
use term::Term;
|
||||
use book;
|
||||
use std::io::{Command, File};
|
||||
use std::os;
|
||||
|
||||
struct Test;
|
||||
|
||||
pub fn parse_cmd(name: &str) -> Option<Box<Subcommand>> {
|
||||
if name == "test" {
|
||||
Some(box Test as Box<Subcommand>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Subcommand for Test {
|
||||
fn parse_args(&mut self, _: &[String]) -> CliResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn usage(&self) {}
|
||||
fn execute(&mut self, term: &mut Term) -> CommandResult<()> {
|
||||
let cwd = os::getcwd().unwrap();
|
||||
let src = cwd.clone();
|
||||
|
||||
let summary = File::open(&src.join("SUMMARY.md"));
|
||||
match book::parse_summary(summary, &src) {
|
||||
Ok(book) => {
|
||||
for (_, item) in book.iter() {
|
||||
let output_result = Command::new("rustdoc")
|
||||
.arg(&item.path)
|
||||
.arg("--test")
|
||||
.output();
|
||||
match output_result {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
term.err(&format!("{}\n{}",
|
||||
String::from_utf8_lossy(&output.output[]),
|
||||
String::from_utf8_lossy(&output.error[]))[]);
|
||||
return Err(box "Some tests failed." as Box<Error>);
|
||||
}
|
||||
|
||||
}
|
||||
Err(e) => {
|
||||
let message = format!("Could not execute `rustdoc`: {}", e);
|
||||
return Err(box message as Box<Error>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(errors) => {
|
||||
for err in errors.into_iter() {
|
||||
term.err(&err[]);
|
||||
}
|
||||
return Err(box "There was an error." as Box<Error>);
|
||||
}
|
||||
}
|
||||
Ok(()) // lol
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue