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
|
## Quick Start
|
||||||
|
|
||||||
1. Download a [binary installer][installer] for your platform.
|
1. Download a [binary installer][installer] for your platform.
|
||||||
2. Read the [guide].
|
2. Read [The Rust Programming Language][trpl].
|
||||||
3. Enjoy!
|
3. Enjoy!
|
||||||
|
|
||||||
> ***Note:*** Windows users can read the detailed
|
> ***Note:*** Windows users can read the detailed
|
||||||
> [using Rust on Windows][win-wiki] notes on the wiki.
|
> [using Rust on Windows][win-wiki] notes on the wiki.
|
||||||
|
|
||||||
[installer]: http://www.rust-lang.org/install.html
|
[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
|
[win-wiki]: https://github.com/rust-lang/rust/wiki/Using-Rust-on-Windows
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
@ -53,7 +53,7 @@ documentation.
|
||||||
When complete, `make install` will place several programs into
|
When complete, `make install` will place several programs into
|
||||||
`/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
|
`/usr/local/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
|
||||||
API-documentation tool.
|
API-documentation tool.
|
||||||
3. Read the [guide].
|
3. Read [The Rust Programming Language][trpl].
|
||||||
4. Enjoy!
|
4. Enjoy!
|
||||||
|
|
||||||
### Building on Windows
|
### 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
|
[repo]: https://github.com/rust-lang/rust
|
||||||
[tarball]: https://static.rust-lang.org/dist/rust-nightly.tar.gz
|
[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
|
## Notes
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,10 @@ Version 1.0.0-alpha (January 2015)
|
||||||
distribution into the Cargo ecosystem so they can evolve
|
distribution into the Cargo ecosystem so they can evolve
|
||||||
separately and don't need to be stabilized as quickly, including
|
separately and don't need to be stabilized as quickly, including
|
||||||
'time', 'getopts', 'num', 'regex', and 'term'.
|
'time', 'getopts', 'num', 'regex', and 'term'.
|
||||||
* Documentation continues to be expanded with more guides, more
|
* Documentation continues to be expanded with more API coverage, more
|
||||||
API coverage and more examples.
|
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
|
* All official Rust binary installers now come with [Cargo], the
|
||||||
Rust package manager.
|
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
|
[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
|
[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
|
[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)
|
Version 0.12.0 (October 2014)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
@ -1055,18 +1055,6 @@ do
|
||||||
make_dir $h/test/debuginfo-gdb
|
make_dir $h/test/debuginfo-gdb
|
||||||
make_dir $h/test/debuginfo-lldb
|
make_dir $h/test/debuginfo-lldb
|
||||||
make_dir $h/test/codegen
|
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
|
done
|
||||||
|
|
||||||
# Configure submodules
|
# Configure submodules
|
||||||
|
|
|
@ -57,7 +57,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \
|
||||||
rustc_trans rustc_back rustc_llvm
|
rustc_trans rustc_back rustc_llvm
|
||||||
HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
|
HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
|
||||||
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
|
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
|
||||||
TOOLS := compiletest rustdoc rustc
|
TOOLS := compiletest rustdoc rustc rustbook
|
||||||
|
|
||||||
DEPS_core :=
|
DEPS_core :=
|
||||||
DEPS_libc := core
|
DEPS_libc := core
|
||||||
|
@ -99,9 +99,11 @@ DEPS_fmt_macros = std
|
||||||
TOOL_DEPS_compiletest := test getopts
|
TOOL_DEPS_compiletest := test getopts
|
||||||
TOOL_DEPS_rustdoc := rustdoc
|
TOOL_DEPS_rustdoc := rustdoc
|
||||||
TOOL_DEPS_rustc := rustc_driver
|
TOOL_DEPS_rustc := rustc_driver
|
||||||
|
TOOL_DEPS_rustbook := std regex rustdoc
|
||||||
TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs
|
TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs
|
||||||
TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs
|
TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs
|
||||||
TOOL_SOURCE_rustc := $(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_core := 1
|
||||||
ONLY_RLIB_libc := 1
|
ONLY_RLIB_libc := 1
|
||||||
|
|
27
mk/docs.mk
27
mk/docs.mk
|
@ -9,7 +9,7 @@
|
||||||
# except according to those terms.
|
# 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).
|
# 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
|
# L10N_LANGS are the languages for which the docs have been
|
||||||
# translated.
|
# translated.
|
||||||
######################################################################
|
######################################################################
|
||||||
DOCS := index intro tutorial guide guide-ffi guide-macros guide-ownership \
|
DOCS := index intro tutorial complement-bugreport \
|
||||||
guide-tasks guide-container guide-pointers guide-testing \
|
complement-lang-faq complement-design-faq complement-project-faq \
|
||||||
guide-plugin guide-crates complement-bugreport guide-error-handling \
|
rustdoc reference
|
||||||
complement-lang-faq complement-design-faq complement-project-faq \
|
|
||||||
rustdoc guide-unsafe guide-strings reference
|
|
||||||
|
|
||||||
PDF_DOCS := guide reference
|
PDF_DOCS := reference
|
||||||
|
|
||||||
RUSTDOC_DEPS_reference := doc/full-toc.inc
|
RUSTDOC_DEPS_reference := doc/full-toc.inc
|
||||||
RUSTDOC_FLAGS_reference := --html-in-header=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
|
# ./configure
|
||||||
RUSTDOC = $(RPATH_VAR2_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(RUSTDOC_EXE)
|
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
|
D := $(S)src/doc
|
||||||
|
|
||||||
DOC_TARGETS :=
|
DOC_TARGETS := trpl
|
||||||
COMPILER_DOC_TARGETS :=
|
COMPILER_DOC_TARGETS :=
|
||||||
DOC_L10N_TARGETS :=
|
DOC_L10N_TARGETS :=
|
||||||
|
|
||||||
|
@ -270,3 +274,10 @@ endif
|
||||||
|
|
||||||
docs: $(DOC_TARGETS)
|
docs: $(DOC_TARGETS)
|
||||||
compiler-docs: $(COMPILER_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)
|
$(Q)$(PREPARE_MAN_CMD) $(PREPARE_SOURCE_MAN_DIR)/$(1) $(PREPARE_DEST_MAN_DIR)/$(1)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
PREPARE_TOOLS = $(filter-out compiletest, $(TOOLS))
|
PREPARE_TOOLS = $(filter-out compiletest rustbook, $(TOOLS))
|
||||||
|
|
||||||
|
|
||||||
# $(1) is tool
|
# $(1) is tool
|
||||||
|
|
29
mk/tests.mk
29
mk/tests.mk
|
@ -147,6 +147,17 @@ else
|
||||||
CFG_ADB_TEST_DIR=
|
CFG_ADB_TEST_DIR=
|
||||||
endif
|
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
|
# Main test targets
|
||||||
|
@ -292,6 +303,7 @@ tidy:
|
||||||
| grep '^$(S)src/rust-installer' -v \
|
| grep '^$(S)src/rust-installer' -v \
|
||||||
| xargs $(CFG_PYTHON) $(S)src/etc/check-binaries.py
|
| xargs $(CFG_PYTHON) $(S)src/etc/check-binaries.py
|
||||||
|
|
||||||
|
|
||||||
endif
|
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-crate-$$(crate)-exec)
|
||||||
|
|
||||||
check-stage$(1)-T-$(2)-H-$(3)-doc-exec: \
|
check-stage$(1)-T-$(2)-H-$(3)-doc-exec: \
|
||||||
$$(foreach docname,$$(DOCS), \
|
$$(foreach docname,$$(DOC_NAMES), \
|
||||||
check-stage$(1)-T-$(2)-H-$(3)-doc-$$(docname)-exec)
|
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-exec: \
|
||||||
check-stage$(1)-T-$(2)-H-$(3)-pretty-rpass-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.
|
# rustdoc etc.
|
||||||
ifeq ($(NO_REBUILD),)
|
ifeq ($(NO_REBUILD),)
|
||||||
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \
|
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \
|
||||||
$$(D)/$(4).md \
|
$$(DOCFILE_$(4)) \
|
||||||
$$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \
|
$$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \
|
||||||
$$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3))
|
$$(RUSTDOC_EXE_$(1)_T_$(2)_H_$(3))
|
||||||
else
|
else
|
||||||
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(D)/$(4).md
|
DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = $$(DOCFILE_$(4))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(2),$$(CFG_BUILD))
|
ifeq ($(2),$$(CFG_BUILD))
|
||||||
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): $$(DOCTESTDEP_$(1)_$(2)_$(3)_$(4))
|
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)): $$(DOCTESTDEP_$(1)_$(2)_$(3)_$(4))
|
||||||
@$$(call E, run doc-$(4) [$(2)])
|
@$$(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
|
else
|
||||||
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)):
|
$$(call TEST_OK_FILE,$(1),$(2),$(3),doc-$(4)):
|
||||||
touch $$@
|
touch $$@
|
||||||
|
@ -815,7 +828,7 @@ endef
|
||||||
$(foreach host,$(CFG_HOST), \
|
$(foreach host,$(CFG_HOST), \
|
||||||
$(foreach target,$(CFG_TARGET), \
|
$(foreach target,$(CFG_TARGET), \
|
||||||
$(foreach stage,$(STAGES), \
|
$(foreach stage,$(STAGES), \
|
||||||
$(foreach docname,$(DOCS), \
|
$(foreach docname,$(DOC_NAMES), \
|
||||||
$(eval $(call DEF_DOC_TEST,$(stage),$(target),$(host),$(docname)))))))
|
$(eval $(call DEF_DOC_TEST,$(stage),$(target),$(host),$(docname)))))))
|
||||||
|
|
||||||
# Crates
|
# Crates
|
||||||
|
@ -877,7 +890,7 @@ TEST_GROUPS = \
|
||||||
debuginfo-lldb \
|
debuginfo-lldb \
|
||||||
codegen \
|
codegen \
|
||||||
doc \
|
doc \
|
||||||
$(foreach docname,$(DOCS),doc-$(docname)) \
|
$(foreach docname,$(DOC_NAMES),doc-$(docname)) \
|
||||||
pretty \
|
pretty \
|
||||||
pretty-rpass \
|
pretty-rpass \
|
||||||
pretty-rpass-valgrind \
|
pretty-rpass-valgrind \
|
||||||
|
@ -946,7 +959,7 @@ $(foreach stage,$(STAGES), \
|
||||||
$(eval $(call DEF_CHECK_FOR_STAGE_AND_HOSTS_AND_GROUP,$(stage),$(host),$(group))))))
|
$(eval $(call DEF_CHECK_FOR_STAGE_AND_HOSTS_AND_GROUP,$(stage),$(host),$(group))))))
|
||||||
|
|
||||||
define DEF_CHECK_DOC_FOR_STAGE
|
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)) \
|
check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-$$(docname)) \
|
||||||
$$(foreach crate,$$(TEST_DOC_CRATES), \
|
$$(foreach crate,$$(TEST_DOC_CRATES), \
|
||||||
check-stage$(1)-T-$$(CFG_BUILD)-H-$$(CFG_BUILD)-doc-crate-$$(crate))
|
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
|
minute intro](intro.html). It will give you an overview of the basic ideas of Rust
|
||||||
at a high level.
|
at a high level.
|
||||||
|
|
||||||
Once you know you really want to learn Rust, the next step is reading [the
|
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
|
Rust Programming Language](book/index.html). It is a lengthy explanation of
|
||||||
concepts. Upon completing the guide, you'll be an intermediate Rust developer,
|
Rust, its syntax, and its concepts. Upon completing the book, you'll be an
|
||||||
and will have a good grasp of the fundamental ideas behind Rust.
|
intermediate Rust developer, and will have a good grasp of the fundamental
|
||||||
|
ideas behind Rust.
|
||||||
|
|
||||||
# Community & Getting Help
|
# 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
|
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).
|
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
|
# Tools
|
||||||
|
|
||||||
Rust's still a young language, so there isn't a ton of tooling yet, but the
|
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
|
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
|
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,
|
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
|
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.
|
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
|
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
|
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
|
concurrent code, and how you don't have to pay a speed cost for much of this
|
||||||
safety.
|
safety.
|
||||||
|
|
||||||
To continue your Rustic education, read [the guide](guide.html) for a more
|
To continue your Rustic education, read [The Rust Programming
|
||||||
in-depth exploration of Rust's syntax and concepts.
|
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.
|
influenced the design.
|
||||||
|
|
||||||
This document does not serve as an introduction to the language. Background
|
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.
|
help acquire such background familiarity.
|
||||||
|
|
||||||
This document also does not serve as a reference to the [standard] library
|
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
|
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.
|
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
|
[standard]: std/index.html
|
||||||
|
|
||||||
# Notation
|
# 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:
|
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.
|
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.
|
declarative way.
|
||||||
|
|
||||||
## Macros
|
## Macros
|
||||||
|
@ -2076,7 +2076,7 @@ On `struct`s:
|
||||||
list of names `#[macro_use(foo, bar)]` restricts the import to just those
|
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
|
macros named. The `extern crate` must appear at the crate root, not inside
|
||||||
`mod`, which ensures proper function of the [`$crate` macro
|
`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.
|
- `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
|
- `no_link` on an `extern crate` — even if we load this crate for macros or
|
||||||
compiler plugins, don't link it into the output.
|
compiler plugins, don't link it into the output.
|
||||||
|
|
||||||
See the [macros guide](guide-macros.html#scoping-and-macro-import/export) for
|
See the [macros section of the
|
||||||
more information on macro scope.
|
book](book/macros.html#scoping-and-macro-import/export) for more information on
|
||||||
|
macro scope.
|
||||||
|
|
||||||
|
|
||||||
### Miscellaneous attributes
|
### 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`,
|
The lint checks supported by the compiler can be found via `rustc -W help`,
|
||||||
along with their default settings. [Compiler
|
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}
|
```{.ignore}
|
||||||
mod m1 {
|
mod m1 {
|
||||||
|
@ -4227,7 +4228,7 @@ communication facilities.
|
||||||
The Rust compiler supports various methods to link crates together both
|
The Rust compiler supports various methods to link crates together both
|
||||||
statically and dynamically. This section will explore the various methods to
|
statically and dynamically. This section will explore the various methods to
|
||||||
link Rust crates together, and more information about native libraries can be
|
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
|
In one session of compilation, the compiler can generate multiple artifacts
|
||||||
through the usage of either command line flags or the `crate_type` attribute.
|
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
|
* [Unicode Annex #31](http://www.unicode.org/reports/tr31/): identifier and
|
||||||
pattern syntax
|
pattern syntax
|
||||||
|
|
||||||
[ffi]: guide-ffi.html
|
[ffi]: book/ffi.html
|
||||||
[plugin]: guide-plugin.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