mirror of
https://git.proxmox.com/git/rustc
synced 2026-01-01 04:39:00 +00:00
New upstream version 1.27.1+dfsg1
This commit is contained in:
parent
2c912e08a8
commit
83c7162d06
@ -47,6 +47,12 @@ as it's possible that someone else has already reported your error. This doesn't
|
||||
always work, and sometimes it's hard to know what to search for, so consider this
|
||||
extra credit. We won't mind if you accidentally file a duplicate report.
|
||||
|
||||
Similarly, to help others who encountered the bug find your issue,
|
||||
consider filing an issue with with a descriptive title, which contains information that might be unique to it.
|
||||
This can be the language or compiler feature used, the conditions that trigger the bug,
|
||||
or part of the error message if there is any.
|
||||
An example could be: **"impossible case reached" on lifetime inference for impl Trait in return position**.
|
||||
|
||||
Opening an issue is as easy as following [this
|
||||
link](https://github.com/rust-lang/rust/issues/new) and filling out the fields.
|
||||
Here's a template that you can use to file a bug, though it's not necessary to
|
||||
@ -121,6 +127,7 @@ configuration used in the build process. Some options to note:
|
||||
#### `[rust]`:
|
||||
- `debuginfo = true` - Build a compiler with debuginfo. Makes building rustc slower, but then you can use a debugger to debug `rustc`.
|
||||
- `debuginfo-lines = true` - An alternative to `debuginfo = true` that doesn't let you use a debugger, but doesn't make building rustc slower and still gives you line numbers in backtraces.
|
||||
- `debuginfo-tools = true` - Build the extended tools with debuginfo.
|
||||
- `debug-assertions = true` - Makes the log output of `debug!` work.
|
||||
- `optimize = false` - Disable optimizations to speed up compilation of stage1 rust, but makes the stage1 compiler x100 slower.
|
||||
|
||||
@ -135,6 +142,8 @@ file. If you still have a `config.mk` file in your directory - from
|
||||
### Building
|
||||
[building]: #building
|
||||
|
||||
A default configuration shall use around 3.5 GB of disk space, whereas building a debug configuration may require more than 30 GB.
|
||||
|
||||
Dependencies
|
||||
- [build dependencies](README.md#building-from-source)
|
||||
- `gdb` 6.2.0 minimum, 7.1 or later recommended for test builds
|
||||
|
||||
171
RELEASES.md
171
RELEASES.md
@ -1,3 +1,173 @@
|
||||
Version 1.27.1 (2018-07-10)
|
||||
===========================
|
||||
|
||||
Security Notes
|
||||
--------------
|
||||
|
||||
- rustdoc would execute plugins in the /tmp/rustdoc/plugins directory
|
||||
when running, which enabled executing code as some other user on a
|
||||
given machine. This release fixes that vulnerability; you can read
|
||||
more about this on the [blog][rustdoc-sec].
|
||||
|
||||
Thank you to Red Hat for responsibily disclosing this vulnerability to us.
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
|
||||
- The borrow checker was fixed to avoid an additional potential unsoundness when using
|
||||
match ergonomics: [#51415][51415], [#49534][49534].
|
||||
|
||||
[51415]: https://github.com/rust-lang/rust/issues/51415
|
||||
[49534]: https://github.com/rust-lang/rust/issues/49534
|
||||
[rustdoc-sec]: https://blog.rust-lang.org/2018/07/06/security-advisory-for-rustdoc.html
|
||||
|
||||
Version 1.27.0 (2018-06-21)
|
||||
==========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Removed 'proc' from the reserved keywords list.][49699] This allows `proc` to
|
||||
be used as an identifer.
|
||||
- [The dyn syntax is now available.][49968] This syntax is equivalent to the
|
||||
bare `Trait` syntax, and should make it clearer when being used in tandem with
|
||||
`impl Trait`. Since it is equivalent to the following syntax:
|
||||
`&Trait == &dyn Trait`, `&mut Trait == &mut dyn Trait`, and
|
||||
`Box<Trait> == Box<dyn Trait>`.
|
||||
- [Attributes on generic parameters such as types and lifetimes are
|
||||
now stable.][48851] e.g.
|
||||
`fn foo<#[lifetime_attr] 'a, #[type_attr] T: 'a>() {}`
|
||||
- [The `#[must_use]` attribute can now also be used on functions as well as
|
||||
types.][48925] It provides a lint that by default warns users when the
|
||||
value returned by a function has not been used.
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Added the `armv5te-unknown-linux-musl` target.][50423]
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [SIMD (Single Instruction Multiple Data) on x86/x86_64 is now stable.][49664]
|
||||
This includes [`arch::x86`] & [`arch::x86_64`] modules which contain
|
||||
SIMD intrinsics, a new macro called `is_x86_feature_detected!`, the
|
||||
`#[target_feature(enable="")]` attribute, and adding `target_feature = ""` to
|
||||
the `cfg` attribute.
|
||||
- [A lot of methods for `[u8]`, `f32`, and `f64` previously only available in
|
||||
std are now available in core.][49896]
|
||||
- [The generic `Rhs` type parameter on `ops::{Shl, ShlAssign, Shr}` now defaults
|
||||
to `Self`.][49630]
|
||||
- [`std::str::replace` now has the `#[must_use]` attribute][50177] to clarify
|
||||
that the operation isn't done in place.
|
||||
- [`Clone::clone`, `Iterator::collect`, and `ToOwned::to_owned` now have
|
||||
the `#[must_use]` attribute][49533] to warn about unused potentially
|
||||
expensive allocations.
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
- [`DoubleEndedIterator::rfind`]
|
||||
- [`DoubleEndedIterator::rfold`]
|
||||
- [`DoubleEndedIterator::try_rfold`]
|
||||
- [`Duration::from_micros`]
|
||||
- [`Duration::from_nanos`]
|
||||
- [`Duration::subsec_micros`]
|
||||
- [`Duration::subsec_millis`]
|
||||
- [`HashMap::remove_entry`]
|
||||
- [`Iterator::try_fold`]
|
||||
- [`Iterator::try_for_each`]
|
||||
- [`NonNull::cast`]
|
||||
- [`Option::filter`]
|
||||
- [`String::replace_range`]
|
||||
- [`Take::set_limit`]
|
||||
- [`hint::unreachable_unchecked`]
|
||||
- [`os::unix::process::parent_id`]
|
||||
- [`process::id`]
|
||||
- [`ptr::swap_nonoverlapping`]
|
||||
- [`slice::rsplit_mut`]
|
||||
- [`slice::rsplit`]
|
||||
- [`slice::swap_with_slice`]
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [`cargo-metadata` now includes `authors`, `categories`, `keywords`,
|
||||
`readme`, and `repository` fields.][cargo/5386]
|
||||
- [Added the `--target-dir` optional argument.][cargo/5393] This allows you to specify
|
||||
a different directory than `target` for placing compilation artifacts.
|
||||
- [Cargo will be adding automatic target inference for binaries, benchmarks,
|
||||
examples, and tests in the Rust 2018 edition.][cargo/5335] If your project specifies
|
||||
specific targets e.g. using `[[bin]]` and have other binaries in locations
|
||||
where cargo would infer a binary, Cargo will produce a warning. You can
|
||||
disable this feature ahead of time by setting any of the following `autobins`,
|
||||
`autobenches`, `autoexamples`, `autotests` to false.
|
||||
- [Cargo will now cache compiler information.][cargo/5359] This can be disabled by
|
||||
setting `CARGO_CACHE_RUSTC_INFO=0` in your environment.
|
||||
|
||||
Misc
|
||||
----
|
||||
- [Added “The Rustc book” into the official documentation.][49707]
|
||||
[“The Rustc book”] documents and teaches how to use the rustc compiler.
|
||||
- [All books available on `doc.rust-lang.org` are now searchable.][49623]
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Calling a `CharExt` or `StrExt` method directly on core will no longer
|
||||
work.][49896] e.g. `::core::prelude::v1::StrExt::is_empty("")` will not
|
||||
compile, `"".is_empty()` will still compile.
|
||||
- [`Debug` output on `atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicUsize}`
|
||||
will only print the inner type.][48553] e.g.
|
||||
`print!("{:?}", AtomicBool::new(true))` will print `true`
|
||||
not `AtomicBool(true)`.
|
||||
- [The maximum number for `repr(align(N))` is now 2²⁹.][50378] Previously you
|
||||
could enter higher numbers but they were not supported by LLVM. Up to 512MB
|
||||
alignment should cover all use cases.
|
||||
|
||||
[48553]: https://github.com/rust-lang/rust/pull/48553/
|
||||
[48851]: https://github.com/rust-lang/rust/pull/48851/
|
||||
[48925]: https://github.com/rust-lang/rust/pull/48925/
|
||||
[49533]: https://github.com/rust-lang/rust/pull/49533/
|
||||
[49623]: https://github.com/rust-lang/rust/pull/49623/
|
||||
[49630]: https://github.com/rust-lang/rust/pull/49630/
|
||||
[49664]: https://github.com/rust-lang/rust/pull/49664/
|
||||
[49699]: https://github.com/rust-lang/rust/pull/49699/
|
||||
[49707]: https://github.com/rust-lang/rust/pull/49707/
|
||||
[49719]: https://github.com/rust-lang/rust/pull/49719/
|
||||
[49896]: https://github.com/rust-lang/rust/pull/49896/
|
||||
[49968]: https://github.com/rust-lang/rust/pull/49968/
|
||||
[50177]: https://github.com/rust-lang/rust/pull/50177/
|
||||
[50378]: https://github.com/rust-lang/rust/pull/50378/
|
||||
[50398]: https://github.com/rust-lang/rust/pull/50398/
|
||||
[50423]: https://github.com/rust-lang/rust/pull/50423/
|
||||
[cargo/5203]: https://github.com/rust-lang/cargo/pull/5203/
|
||||
[cargo/5335]: https://github.com/rust-lang/cargo/pull/5335/
|
||||
[cargo/5359]: https://github.com/rust-lang/cargo/pull/5359/
|
||||
[cargo/5386]: https://github.com/rust-lang/cargo/pull/5386/
|
||||
[cargo/5393]: https://github.com/rust-lang/cargo/pull/5393/
|
||||
[`DoubleEndedIterator::rfind`]: https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.rfind
|
||||
[`DoubleEndedIterator::rfold`]: https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.rfold
|
||||
[`DoubleEndedIterator::try_rfold`]: https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.try_rfold
|
||||
[`Duration::from_micros`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.from_micros
|
||||
[`Duration::from_nanos`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.from_nanos
|
||||
[`Duration::subsec_micros`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.subsec_micros
|
||||
[`Duration::subsec_millis`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.subsec_millis
|
||||
[`HashMap::remove_entry`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.remove_entry
|
||||
[`Iterator::try_fold`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_fold
|
||||
[`Iterator::try_for_each`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_for_each
|
||||
[`NonNull::cast`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.cast
|
||||
[`Option::filter`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.filter
|
||||
[`String::replace_range`]: https://doc.rust-lang.org/std/string/struct.String.html#method.replace_range
|
||||
[`Take::set_limit`]: https://doc.rust-lang.org/std/io/struct.Take.html#method.set_limit
|
||||
[`slice::rsplit_mut`]: https://doc.rust-lang.org/std/primitive.slice.html#method.rsplit_mut
|
||||
[`slice::rsplit`]: https://doc.rust-lang.org/std/primitive.slice.html#method.rsplit
|
||||
[`slice::swap_with_slice`]: https://doc.rust-lang.org/std/primitive.slice.html#method.swap_with_slice
|
||||
[`arch::x86_64`]: https://doc.rust-lang.org/std/arch/x86_64/index.html
|
||||
[`arch::x86`]: https://doc.rust-lang.org/std/arch/x86/index.html
|
||||
[`fs::read`]:
|
||||
[`fs::write`]:
|
||||
[`hint::unreachable_unchecked`]: https://doc.rust-lang.org/std/hint/fn.unreachable_unchecked.html
|
||||
[`os::unix::process::parent_id`]: https://doc.rust-lang.org/std/os/unix/process/fn.parent_id.html
|
||||
[`ptr::swap_nonoverlapping`]: https://doc.rust-lang.org/std/ptr/fn.swap_nonoverlapping.html
|
||||
[`process::id`]: https://doc.rust-lang.org/std/process/fn.id.html
|
||||
[“The Rustc book”]: https://doc.rust-lang.org/rustc
|
||||
|
||||
|
||||
Version 1.26.2 (2018-06-05)
|
||||
==========================
|
||||
|
||||
@ -8,6 +178,7 @@ Compatibility Notes
|
||||
|
||||
[51117]: https://github.com/rust-lang/rust/issues/51117
|
||||
|
||||
|
||||
Version 1.26.1 (2018-05-29)
|
||||
==========================
|
||||
|
||||
|
||||
@ -262,6 +262,10 @@
|
||||
# standard library.
|
||||
#debuginfo-only-std = false
|
||||
|
||||
# Enable debuginfo for the extended tools: cargo, rls, rustfmt
|
||||
# Adding debuginfo makes them several times larger.
|
||||
#debuginfo-tools = false
|
||||
|
||||
# Whether or not jemalloc is built and enabled
|
||||
#use-jemalloc = true
|
||||
|
||||
@ -339,6 +343,12 @@
|
||||
# rustc to execute.
|
||||
#lld = false
|
||||
|
||||
# Whether to deny warnings in crates
|
||||
#deny-warnings = true
|
||||
|
||||
# Print backtrace on internal compiler errors during bootstrap
|
||||
#backtrace-on-ice = false
|
||||
|
||||
# =============================================================================
|
||||
# Options for specific targets
|
||||
#
|
||||
|
||||
@ -1 +1 @@
|
||||
594fb253c2b02b320c728391a425d028e6dc7a09
|
||||
5f2b325f64ed6caa7179f3e04913db437656ec7e
|
||||
1189
src/Cargo.lock
generated
1189
src/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -40,14 +40,6 @@ members = [
|
||||
"tools/rls/test_data/workspace_symbol",
|
||||
]
|
||||
|
||||
# Curiously, compiletest will segfault if compiled with opt-level=3 on 64-bit
|
||||
# MSVC when running the compile-fail test suite when a should-fail test panics.
|
||||
# But hey if this is removed and it gets past the bots, sounds good to me.
|
||||
[profile.release]
|
||||
opt-level = 2
|
||||
[profile.bench]
|
||||
opt-level = 2
|
||||
|
||||
# These options are controlled from our rustc wrapper script, so turn them off
|
||||
# here and have them controlled elsewhere.
|
||||
[profile.dev]
|
||||
|
||||
@ -42,3 +42,7 @@ serde_json = "1.0.2"
|
||||
toml = "0.4"
|
||||
lazy_static = "0.2"
|
||||
time = "0.1"
|
||||
petgraph = "0.4.12"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5"
|
||||
|
||||
@ -64,6 +64,10 @@ The script accepts commands, flags, and arguments to determine what to do:
|
||||
# execute tests in the standard library in stage0
|
||||
./x.py test --stage 0 src/libstd
|
||||
|
||||
# execute tests in the core and standard library in stage0,
|
||||
# without running doc tests (thus avoid depending on building the compiler)
|
||||
./x.py test --stage 0 --no-doc src/libcore src/libstd
|
||||
|
||||
# execute all doc tests
|
||||
./x.py test src/doc
|
||||
```
|
||||
|
||||
@ -107,6 +107,13 @@ fn main() {
|
||||
env::join_paths(&dylib_path).unwrap());
|
||||
let mut maybe_crate = None;
|
||||
|
||||
// Print backtrace in case of ICE
|
||||
if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() {
|
||||
cmd.env("RUST_BACKTRACE", "1");
|
||||
}
|
||||
|
||||
cmd.env("RUSTC_BREAK_ON_ICE", "1");
|
||||
|
||||
if let Some(target) = target {
|
||||
// The stage0 compiler has a special sysroot distinct from what we
|
||||
// actually downloaded, so we just always pass the `--sysroot` option.
|
||||
@ -247,9 +254,6 @@ fn main() {
|
||||
// When running miri tests, we need to generate MIR for all libraries
|
||||
if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
|
||||
cmd.arg("-Zalways-encode-mir");
|
||||
if stage != "0" {
|
||||
cmd.arg("-Zmiri");
|
||||
}
|
||||
cmd.arg("-Zmir-emit-validate=1");
|
||||
}
|
||||
|
||||
@ -279,6 +283,10 @@ fn main() {
|
||||
cmd.arg("--color=always");
|
||||
}
|
||||
|
||||
if env::var_os("RUSTC_DENY_WARNINGS").is_some() {
|
||||
cmd.arg("-Dwarnings");
|
||||
}
|
||||
|
||||
if verbose > 1 {
|
||||
eprintln!("rustc command: {:?}", cmd);
|
||||
eprintln!("sysroot: {:?}", sysroot);
|
||||
|
||||
@ -314,7 +314,7 @@ class RustBuild(object):
|
||||
self.build_dir = os.path.join(os.getcwd(), "build")
|
||||
self.clean = False
|
||||
self.config_toml = ''
|
||||
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
|
||||
self.rust_root = ''
|
||||
self.use_locked_deps = ''
|
||||
self.use_vendored_sources = ''
|
||||
self.verbose = False
|
||||
@ -710,6 +710,7 @@ def bootstrap(help_triggered):
|
||||
parser = argparse.ArgumentParser(description='Build rust')
|
||||
parser.add_argument('--config')
|
||||
parser.add_argument('--build')
|
||||
parser.add_argument('--src')
|
||||
parser.add_argument('--clean', action='store_true')
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||
|
||||
@ -718,6 +719,7 @@ def bootstrap(help_triggered):
|
||||
|
||||
# Configure initial bootstrap
|
||||
build = RustBuild()
|
||||
build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..'))
|
||||
build.verbose = args.verbose
|
||||
build.clean = args.clean
|
||||
|
||||
@ -768,10 +770,7 @@ def bootstrap(help_triggered):
|
||||
if 'dev' in data:
|
||||
build.set_dev_environment()
|
||||
|
||||
# No help text depends on submodules. This check saves ~1 minute of git commands, even if
|
||||
# all the submodules are present and downloaded!
|
||||
if not help_triggered:
|
||||
build.update_submodules()
|
||||
build.update_submodules()
|
||||
|
||||
# Fetch/build the bootstrap
|
||||
build.build = args.build or build.build_triple()
|
||||
@ -788,6 +787,7 @@ def bootstrap(help_triggered):
|
||||
env["SRC"] = build.rust_root
|
||||
env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
|
||||
env["BOOTSTRAP_PYTHON"] = sys.executable
|
||||
env["BUILD_DIR"] = build.build_dir
|
||||
run(args, env=env, verbose=build.verbose)
|
||||
|
||||
|
||||
|
||||
@ -19,12 +19,13 @@ use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use compile;
|
||||
use install;
|
||||
use dist;
|
||||
use util::{exe, libdir, add_lib_path};
|
||||
use {Build, Mode};
|
||||
use {Build, Mode, DocTests};
|
||||
use cache::{INTERNER, Interned, Cache};
|
||||
use check;
|
||||
use test;
|
||||
@ -35,6 +36,9 @@ use native;
|
||||
|
||||
pub use Compiler;
|
||||
|
||||
use petgraph::Graph;
|
||||
use petgraph::graph::NodeIndex;
|
||||
|
||||
pub struct Builder<'a> {
|
||||
pub build: &'a Build,
|
||||
pub top_stage: u32,
|
||||
@ -42,6 +46,10 @@ pub struct Builder<'a> {
|
||||
cache: Cache,
|
||||
stack: RefCell<Vec<Box<Any>>>,
|
||||
time_spent_on_dependencies: Cell<Duration>,
|
||||
pub paths: Vec<PathBuf>,
|
||||
graph_nodes: RefCell<HashMap<String, NodeIndex>>,
|
||||
graph: RefCell<Graph<String, bool>>,
|
||||
parent: Cell<Option<NodeIndex>>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Builder<'a> {
|
||||
@ -146,18 +154,17 @@ impl StepDescription {
|
||||
eprintln!("{:?} not skipped for {:?} -- not in {:?}", pathset,
|
||||
self.name, builder.config.exclude);
|
||||
}
|
||||
let build = builder.build;
|
||||
let hosts = &build.hosts;
|
||||
let hosts = &builder.hosts;
|
||||
|
||||
// Determine the targets participating in this rule.
|
||||
let targets = if self.only_hosts {
|
||||
if !build.config.run_host_only {
|
||||
if !builder.config.run_host_only {
|
||||
return; // don't run anything
|
||||
} else {
|
||||
&build.hosts
|
||||
&builder.hosts
|
||||
}
|
||||
} else {
|
||||
&build.targets
|
||||
&builder.targets
|
||||
};
|
||||
|
||||
for host in hosts {
|
||||
@ -303,7 +310,8 @@ impl<'a> Builder<'a> {
|
||||
tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
|
||||
tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy,
|
||||
native::Llvm, tool::Rustfmt, tool::Miri, native::Lld),
|
||||
Kind::Check => describe!(check::Std, check::Test, check::Rustc),
|
||||
Kind::Check => describe!(check::Std, check::Test, check::Rustc, check::CodegenBackend,
|
||||
check::Rustdoc),
|
||||
Kind::Test => describe!(test::Tidy, test::Bootstrap, test::Ui, test::RunPass,
|
||||
test::CompileFail, test::ParseFail, test::RunFail, test::RunPassValgrind,
|
||||
test::MirOpt, test::Codegen, test::CodegenUnits, test::Incremental, test::Debuginfo,
|
||||
@ -315,15 +323,15 @@ impl<'a> Builder<'a> {
|
||||
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
|
||||
test::RunMakeFullDeps,
|
||||
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
|
||||
test::TheBook, test::UnstableBook,
|
||||
test::TheBook, test::UnstableBook, test::RustcBook,
|
||||
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,
|
||||
// Run run-make last, since these won't pass without make on Windows
|
||||
test::RunMake),
|
||||
test::RunMake, test::RustdocUi),
|
||||
Kind::Bench => describe!(test::Crate, test::CrateLibrustc),
|
||||
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
|
||||
doc::Standalone, doc::Std, doc::Test, doc::WhitelistedRustc, doc::Rustc,
|
||||
doc::ErrorIndex, doc::Nomicon, doc::Reference, doc::Rustdoc, doc::RustByExample,
|
||||
doc::CargoBook),
|
||||
doc::RustcBook, doc::CargoBook),
|
||||
Kind::Dist => describe!(dist::Docs, dist::RustcDocs, dist::Mingw, dist::Rustc,
|
||||
dist::DebuggerScripts, dist::Std, dist::Analysis, dist::Src,
|
||||
dist::PlainSourceTarball, dist::Cargo, dist::Rls, dist::Rustfmt, dist::Extended,
|
||||
@ -351,6 +359,10 @@ impl<'a> Builder<'a> {
|
||||
cache: Cache::new(),
|
||||
stack: RefCell::new(Vec::new()),
|
||||
time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
|
||||
paths: vec![],
|
||||
graph_nodes: RefCell::new(HashMap::new()),
|
||||
graph: RefCell::new(Graph::new()),
|
||||
parent: Cell::new(None),
|
||||
};
|
||||
|
||||
let builder = &builder;
|
||||
@ -367,7 +379,7 @@ impl<'a> Builder<'a> {
|
||||
Some(help)
|
||||
}
|
||||
|
||||
pub fn run(build: &Build) {
|
||||
pub fn new(build: &Build) -> Builder {
|
||||
let (kind, paths) = match build.config.cmd {
|
||||
Subcommand::Build { ref paths } => (Kind::Build, &paths[..]),
|
||||
Subcommand::Check { ref paths } => (Kind::Check, &paths[..]),
|
||||
@ -379,12 +391,6 @@ impl<'a> Builder<'a> {
|
||||
Subcommand::Clean { .. } => panic!(),
|
||||
};
|
||||
|
||||
if let Some(path) = paths.get(0) {
|
||||
if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let builder = Builder {
|
||||
build,
|
||||
top_stage: build.config.stage.unwrap_or(2),
|
||||
@ -392,20 +398,33 @@ impl<'a> Builder<'a> {
|
||||
cache: Cache::new(),
|
||||
stack: RefCell::new(Vec::new()),
|
||||
time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
|
||||
paths: paths.to_owned(),
|
||||
graph_nodes: RefCell::new(HashMap::new()),
|
||||
graph: RefCell::new(Graph::new()),
|
||||
parent: Cell::new(None),
|
||||
};
|
||||
|
||||
if kind == Kind::Dist {
|
||||
assert!(!build.config.test_miri, "Do not distribute with miri enabled.\n\
|
||||
assert!(!builder.config.test_miri, "Do not distribute with miri enabled.\n\
|
||||
The distributed libraries would include all MIR (increasing binary size).
|
||||
The distributed MIR would include validation statements.");
|
||||
}
|
||||
|
||||
StepDescription::run(&Builder::get_step_descriptions(builder.kind), &builder, paths);
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn execute_cli(&self) -> Graph<String, bool> {
|
||||
self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths);
|
||||
self.graph.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn default_doc(&self, paths: Option<&[PathBuf]>) {
|
||||
let paths = paths.unwrap_or(&[]);
|
||||
StepDescription::run(&Builder::get_step_descriptions(Kind::Doc), self, paths);
|
||||
self.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), paths);
|
||||
}
|
||||
|
||||
fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
|
||||
StepDescription::run(v, self, paths);
|
||||
}
|
||||
|
||||
/// Obtain a compiler at a given stage and for a given host. Explicitly does
|
||||
@ -457,7 +476,7 @@ impl<'a> Builder<'a> {
|
||||
|
||||
pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
|
||||
self.sysroot_libdir(compiler, compiler.host)
|
||||
.with_file_name(self.build.config.rust_codegen_backends_dir.clone())
|
||||
.with_file_name(self.config.rust_codegen_backends_dir.clone())
|
||||
}
|
||||
|
||||
/// Returns the compiler's libdir where it stores the dynamic libraries that
|
||||
@ -467,7 +486,7 @@ impl<'a> Builder<'a> {
|
||||
/// Windows.
|
||||
pub fn rustc_libdir(&self, compiler: Compiler) -> PathBuf {
|
||||
if compiler.is_snapshot(self) {
|
||||
self.build.rustc_snapshot_libdir()
|
||||
self.rustc_snapshot_libdir()
|
||||
} else {
|
||||
self.sysroot(compiler).join(libdir(&compiler.host))
|
||||
}
|
||||
@ -504,12 +523,12 @@ impl<'a> Builder<'a> {
|
||||
let compiler = self.compiler(self.top_stage, host);
|
||||
cmd.env("RUSTC_STAGE", compiler.stage.to_string())
|
||||
.env("RUSTC_SYSROOT", self.sysroot(compiler))
|
||||
.env("RUSTDOC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
|
||||
.env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
|
||||
.env("RUSTDOC_LIBDIR", self.sysroot_libdir(compiler, self.config.build))
|
||||
.env("CFG_RELEASE_CHANNEL", &self.config.channel)
|
||||
.env("RUSTDOC_REAL", self.rustdoc(host))
|
||||
.env("RUSTDOC_CRATE_VERSION", self.build.rust_version())
|
||||
.env("RUSTDOC_CRATE_VERSION", self.rust_version())
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
if let Some(linker) = self.build.linker(host) {
|
||||
if let Some(linker) = self.linker(host) {
|
||||
cmd.env("RUSTC_TARGET_LINKER", linker);
|
||||
}
|
||||
cmd
|
||||
@ -534,6 +553,12 @@ impl<'a> Builder<'a> {
|
||||
.arg("--target")
|
||||
.arg(target);
|
||||
|
||||
// Set a flag for `check` so that certain build scripts can do less work
|
||||
// (e.g. not building/requiring LLVM).
|
||||
if cmd == "check" {
|
||||
cargo.env("RUST_CHECK", "1");
|
||||
}
|
||||
|
||||
// If we were invoked from `make` then that's already got a jobserver
|
||||
// set up for us so no need to tell Cargo about jobs all over again.
|
||||
if env::var_os("MAKEFLAGS").is_none() && env::var_os("MFLAGS").is_none() {
|
||||
@ -566,6 +591,8 @@ impl<'a> Builder<'a> {
|
||||
format!("{} {}", env::var("RUSTFLAGS").unwrap_or_default(), extra_args));
|
||||
}
|
||||
|
||||
let want_rustdoc = self.doc_tests != DocTests::No;
|
||||
|
||||
// Customize the compiler we're running. Specify the compiler to cargo
|
||||
// as our shim and then pass it some various options used to configure
|
||||
// how the actual compiler itself is called.
|
||||
@ -582,7 +609,7 @@ impl<'a> Builder<'a> {
|
||||
.env("RUSTC_LIBDIR", self.rustc_libdir(compiler))
|
||||
.env("RUSTC_RPATH", self.config.rust_rpath.to_string())
|
||||
.env("RUSTDOC", self.out.join("bootstrap/debug/rustdoc"))
|
||||
.env("RUSTDOC_REAL", if cmd == "doc" || cmd == "test" {
|
||||
.env("RUSTDOC_REAL", if cmd == "doc" || (cmd == "test" && want_rustdoc) {
|
||||
self.rustdoc(compiler.host)
|
||||
} else {
|
||||
PathBuf::from("/path/to/nowhere/rustdoc/not/required")
|
||||
@ -590,23 +617,27 @@ impl<'a> Builder<'a> {
|
||||
.env("TEST_MIRI", self.config.test_miri.to_string())
|
||||
.env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir());
|
||||
|
||||
if let Some(host_linker) = self.build.linker(compiler.host) {
|
||||
if let Some(host_linker) = self.linker(compiler.host) {
|
||||
cargo.env("RUSTC_HOST_LINKER", host_linker);
|
||||
}
|
||||
if let Some(target_linker) = self.build.linker(target) {
|
||||
if let Some(target_linker) = self.linker(target) {
|
||||
cargo.env("RUSTC_TARGET_LINKER", target_linker);
|
||||
}
|
||||
if let Some(ref error_format) = self.config.rustc_error_format {
|
||||
cargo.env("RUSTC_ERROR_FORMAT", error_format);
|
||||
}
|
||||
if cmd != "build" && cmd != "check" {
|
||||
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(self.compiler(2, self.build.build)));
|
||||
if cmd != "build" && cmd != "check" && want_rustdoc {
|
||||
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(self.compiler(2, self.config.build)));
|
||||
}
|
||||
|
||||
if mode != Mode::Tool {
|
||||
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
||||
// get compiled with debuginfo.
|
||||
// Adding debuginfo increases their sizes by a factor of 3-4.
|
||||
if mode == Mode::Tool {
|
||||
// Tools like cargo and rls don't get debuginfo by default right now, but this can be
|
||||
// enabled in the config. Adding debuginfo makes them several times larger.
|
||||
if self.config.rust_debuginfo_tools {
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
|
||||
cargo.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string());
|
||||
}
|
||||
} else {
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
|
||||
cargo.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string());
|
||||
cargo.env("RUSTC_FORCE_UNSTABLE", "1");
|
||||
@ -654,7 +685,7 @@ impl<'a> Builder<'a> {
|
||||
//
|
||||
// If LLVM support is disabled we need to use the snapshot compiler to compile
|
||||
// build scripts, as the new compiler doesn't support executables.
|
||||
if mode == Mode::Libstd || !self.build.config.llvm_enabled {
|
||||
if mode == Mode::Libstd || !self.config.llvm_enabled {
|
||||
cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
|
||||
} else {
|
||||
@ -677,8 +708,17 @@ impl<'a> Builder<'a> {
|
||||
cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1");
|
||||
}
|
||||
|
||||
if self.config.backtrace_on_ice {
|
||||
cargo.env("RUSTC_BACKTRACE_ON_ICE", "1");
|
||||
}
|
||||
|
||||
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
|
||||
|
||||
// in std, we want to avoid denying warnings for stage 0 as that makes cfg's painful.
|
||||
if self.config.deny_warnings && !(mode == Mode::Libstd && stage == 0) {
|
||||
cargo.env("RUSTC_DENY_WARNINGS", "1");
|
||||
}
|
||||
|
||||
// Throughout the build Cargo can execute a number of build scripts
|
||||
// compiling C/C++ code and we need to pass compilers, archivers, flags, etc
|
||||
// obtained previously to those build scripts.
|
||||
@ -728,12 +768,14 @@ impl<'a> Builder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if mode == Mode::Libstd && self.config.extended && compiler.is_final_stage(self) {
|
||||
if cmd == "build" && mode == Mode::Libstd
|
||||
&& self.config.extended && compiler.is_final_stage(self)
|
||||
{
|
||||
cargo.env("RUSTC_SAVE_ANALYSIS", "api".to_string());
|
||||
}
|
||||
|
||||
// For `cargo doc` invocations, make rustdoc print the Rust version into the docs
|
||||
cargo.env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
|
||||
cargo.env("RUSTDOC_CRATE_VERSION", self.rust_version());
|
||||
|
||||
// Environment variables *required* throughout the build
|
||||
//
|
||||
@ -741,7 +783,7 @@ impl<'a> Builder<'a> {
|
||||
cargo.env("CFG_COMPILER_HOST_TRIPLE", target);
|
||||
|
||||
// Set this for all builds to make sure doc builds also get it.
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &self.build.config.channel);
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel);
|
||||
|
||||
// This one's a bit tricky. As of the time of this writing the compiler
|
||||
// links to the `winapi` crate on crates.io. This crate provides raw
|
||||
@ -807,7 +849,7 @@ impl<'a> Builder<'a> {
|
||||
cargo
|
||||
}
|
||||
|
||||
/// Ensure that a given step is built, returning it's output. This will
|
||||
/// Ensure that a given step is built, returning its output. This will
|
||||
/// cache the step, so it is safe (and good!) to call this as often as
|
||||
/// needed to ensure that all dependencies are built.
|
||||
pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
|
||||
@ -826,14 +868,39 @@ impl<'a> Builder<'a> {
|
||||
panic!(out);
|
||||
}
|
||||
if let Some(out) = self.cache.get(&step) {
|
||||
self.build.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step));
|
||||
self.verbose(&format!("{}c {:?}", " ".repeat(stack.len()), step));
|
||||
|
||||
{
|
||||
let mut graph = self.graph.borrow_mut();
|
||||
let parent = self.parent.get();
|
||||
let us = *self.graph_nodes.borrow_mut()
|
||||
.entry(format!("{:?}", step))
|
||||
.or_insert_with(|| graph.add_node(format!("{:?}", step)));
|
||||
if let Some(parent) = parent {
|
||||
graph.add_edge(parent, us, false);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
self.build.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step));
|
||||
self.verbose(&format!("{}> {:?}", " ".repeat(stack.len()), step));
|
||||
stack.push(Box::new(step.clone()));
|
||||
}
|
||||
|
||||
let prev_parent = self.parent.get();
|
||||
|
||||
{
|
||||
let mut graph = self.graph.borrow_mut();
|
||||
let parent = self.parent.get();
|
||||
let us = *self.graph_nodes.borrow_mut()
|
||||
.entry(format!("{:?}", step))
|
||||
.or_insert_with(|| graph.add_node(format!("{:?}", step)));
|
||||
self.parent.set(Some(us));
|
||||
if let Some(parent) = parent {
|
||||
graph.add_edge(parent, us, true);
|
||||
}
|
||||
}
|
||||
|
||||
let (out, dur) = {
|
||||
let start = Instant::now();
|
||||
let zero = Duration::new(0, 0);
|
||||
@ -844,7 +911,9 @@ impl<'a> Builder<'a> {
|
||||
(out, dur - deps)
|
||||
};
|
||||
|
||||
if self.build.config.print_step_timings && dur > Duration::from_millis(100) {
|
||||
self.parent.set(prev_parent);
|
||||
|
||||
if self.config.print_step_timings && dur > Duration::from_millis(100) {
|
||||
println!("[TIMING] {:?} -- {}.{:03}",
|
||||
step,
|
||||
dur.as_secs(),
|
||||
@ -856,8 +925,523 @@ impl<'a> Builder<'a> {
|
||||
let cur_step = stack.pop().expect("step stack empty");
|
||||
assert_eq!(cur_step.downcast_ref(), Some(&step));
|
||||
}
|
||||
self.build.verbose(&format!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step));
|
||||
self.verbose(&format!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step));
|
||||
self.cache.put(step, out.clone());
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod __test {
|
||||
use config::Config;
|
||||
use std::thread;
|
||||
use super::*;
|
||||
|
||||
fn configure(host: &[&str], target: &[&str]) -> Config {
|
||||
let mut config = Config::default_opts();
|
||||
// don't save toolstates
|
||||
config.save_toolstates = None;
|
||||
config.run_host_only = true;
|
||||
config.dry_run = true;
|
||||
// try to avoid spurious failures in dist where we create/delete each others file
|
||||
let dir = config.out.join("tmp-rustbuild-tests")
|
||||
.join(&thread::current().name().unwrap_or("unknown").replace(":", "-"));
|
||||
t!(fs::create_dir_all(&dir));
|
||||
config.out = dir;
|
||||
config.build = INTERNER.intern_str("A");
|
||||
config.hosts = vec![config.build].clone().into_iter()
|
||||
.chain(host.iter().map(|s| INTERNER.intern_str(s))).collect::<Vec<_>>();
|
||||
config.targets = config.hosts.clone().into_iter()
|
||||
.chain(target.iter().map(|s| INTERNER.intern_str(s))).collect::<Vec<_>>();
|
||||
config
|
||||
}
|
||||
|
||||
fn first<A, B>(v: Vec<(A, B)>) -> Vec<A> {
|
||||
v.into_iter().map(|(a, _)| a).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_baseline() {
|
||||
let build = Build::new(configure(&[], &[]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
|
||||
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_with_targets() {
|
||||
let build = Build::new(configure(&[], &["B"]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
dist::Docs { stage: 2, host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
dist::Mingw { host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
|
||||
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_with_hosts() {
|
||||
let build = Build::new(configure(&["B"], &[]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
dist::Docs { stage: 2, host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
dist::Mingw { host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
|
||||
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
|
||||
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_with_targets_and_hosts() {
|
||||
let build = Build::new(configure(&["B"], &["C"]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
let c = INTERNER.intern_str("C");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
dist::Docs { stage: 2, host: b },
|
||||
dist::Docs { stage: 2, host: c },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
dist::Mingw { host: b },
|
||||
dist::Mingw { host: c },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
|
||||
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
|
||||
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_with_target_flag() {
|
||||
let mut config = configure(&["B"], &["C"]);
|
||||
config.run_host_only = false; // as-if --target=C was passed
|
||||
let build = Build::new(config);
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
let c = INTERNER.intern_str("C");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
dist::Docs { stage: 2, host: b },
|
||||
dist::Docs { stage: 2, host: c },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
dist::Mingw { host: b },
|
||||
dist::Mingw { host: c },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dist_with_same_targets_and_hosts() {
|
||||
let build = Build::new(configure(&["B"], &["B"]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
|
||||
assert_eq!(first(builder.cache.all::<dist::Docs>()), &[
|
||||
dist::Docs { stage: 2, host: a },
|
||||
dist::Docs { stage: 2, host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[
|
||||
dist::Mingw { host: a },
|
||||
dist::Mingw { host: b },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Rustc>()), &[
|
||||
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
|
||||
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Std>()), &[
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
dist::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
|
||||
assert_eq!(first(builder.cache.all::<compile::Std>()), &[
|
||||
compile::Std {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Std {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Std {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
compile::Std {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<compile::Assemble>()), &[
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 0 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 1 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 2 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: b, stage: 2 },
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_default() {
|
||||
let build = Build::new(configure(&["B"], &["C"]));
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
let c = INTERNER.intern_str("C");
|
||||
|
||||
assert!(!builder.cache.all::<compile::Std>().is_empty());
|
||||
assert!(!builder.cache.all::<compile::Assemble>().is_empty());
|
||||
assert_eq!(first(builder.cache.all::<compile::Rustc>()), &[
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: b,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_with_target_flag() {
|
||||
let mut config = configure(&["B"], &["C"]);
|
||||
config.run_host_only = false;
|
||||
let build = Build::new(config);
|
||||
let mut builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
|
||||
|
||||
let a = INTERNER.intern_str("A");
|
||||
let b = INTERNER.intern_str("B");
|
||||
let c = INTERNER.intern_str("C");
|
||||
|
||||
assert!(!builder.cache.all::<compile::Std>().is_empty());
|
||||
assert_eq!(first(builder.cache.all::<compile::Assemble>()), &[
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 0 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 1 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: b, stage: 1 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: a, stage: 2 },
|
||||
},
|
||||
compile::Assemble {
|
||||
target_compiler: Compiler { host: b, stage: 2 },
|
||||
},
|
||||
]);
|
||||
assert_eq!(first(builder.cache.all::<compile::Rustc>()), &[
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: b,
|
||||
},
|
||||
compile::Rustc {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(first(builder.cache.all::<compile::Test>()), &[
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: a,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 0 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 1 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: b,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: a, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
compile::Test {
|
||||
compiler: Compiler { host: b, stage: 2 },
|
||||
target: c,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_no_doc_stage0() {
|
||||
let mut config = configure(&[], &[]);
|
||||
config.stage = Some(0);
|
||||
config.cmd = Subcommand::Test {
|
||||
paths: vec!["src/libstd".into()],
|
||||
test_args: vec![],
|
||||
rustc_args: vec![],
|
||||
fail_fast: true,
|
||||
doc_tests: DocTests::No,
|
||||
};
|
||||
|
||||
let build = Build::new(config);
|
||||
let mut builder = Builder::new(&build);
|
||||
|
||||
let host = INTERNER.intern_str("A");
|
||||
|
||||
builder.run_step_descriptions(
|
||||
&[StepDescription::from::<test::Crate>()],
|
||||
&["src/libstd".into()],
|
||||
);
|
||||
|
||||
// Ensure we don't build any compiler artifacts.
|
||||
assert!(builder.cache.all::<compile::Rustc>().is_empty());
|
||||
assert_eq!(first(builder.cache.all::<test::Crate>()), &[
|
||||
test::Crate {
|
||||
compiler: Compiler { host, stage: 0 },
|
||||
target: host,
|
||||
mode: Mode::Libstd,
|
||||
test_kind: test::TestKind::Test,
|
||||
krate: INTERNER.intern_str("std"),
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
use std::cmp::{PartialOrd, Ord, Ordering};
|
||||
|
||||
use builder::Step;
|
||||
|
||||
@ -154,6 +155,19 @@ impl AsRef<OsStr> for Interned<String> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Interned<String>> for Interned<String> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
l.get(*self).partial_cmp(l.get(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Interned<String> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let l = INTERNER.strs.lock().unwrap();
|
||||
l.get(*self).cmp(l.get(*other))
|
||||
}
|
||||
}
|
||||
|
||||
struct TyIntern<T> {
|
||||
items: Vec<T>,
|
||||
@ -264,4 +278,16 @@ impl Cache {
|
||||
.expect("invalid type mapped");
|
||||
stepcache.get(step).cloned()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all<S: Ord + Copy + Step>(&mut self) -> Vec<(S, S::Output)> {
|
||||
let cache = self.0.get_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let mut v = cache.remove(&type_id)
|
||||
.map(|b| b.downcast::<HashMap<S, S::Output>>().expect("correct type"))
|
||||
.map(|m| m.into_iter().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
v.sort_by_key(|&(a, _)| a);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,8 +79,16 @@ pub fn find(build: &mut Build) {
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false)
|
||||
.target(&target).host(&build.build);
|
||||
if target.contains("msvc") {
|
||||
cfg.static_crt(true);
|
||||
match build.crt_static(target) {
|
||||
Some(a) => { cfg.static_crt(a); }
|
||||
None => {
|
||||
if target.contains("msvc") {
|
||||
cfg.static_crt(true);
|
||||
}
|
||||
if target.contains("musl") {
|
||||
cfg.static_flag(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = build.config.target_config.get(&target);
|
||||
@ -97,8 +105,9 @@ pub fn find(build: &mut Build) {
|
||||
cc2ar(compiler.path(), &target)
|
||||
};
|
||||
|
||||
build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
|
||||
build.cc.insert(target, compiler);
|
||||
build.verbose(&format!("CC_{} = {:?}", &target, build.cc(target)));
|
||||
build.verbose(&format!("CFLAGS_{} = {:?}", &target, build.cflags(target)));
|
||||
if let Some(ar) = ar {
|
||||
build.verbose(&format!("AR_{} = {:?}", &target, ar));
|
||||
build.ar.insert(target, ar);
|
||||
|
||||
@ -24,7 +24,7 @@ use Build;
|
||||
use config::Config;
|
||||
|
||||
// The version number
|
||||
pub const CFG_RELEASE_NUM: &str = "1.26.2";
|
||||
pub const CFG_RELEASE_NUM: &str = "1.27.1";
|
||||
|
||||
pub struct GitInfo {
|
||||
inner: Option<Info>,
|
||||
|
||||
@ -10,10 +10,11 @@
|
||||
|
||||
//! Implementation of compiling the compiler and standard library, in "check" mode.
|
||||
|
||||
use compile::{run_cargo, std_cargo, test_cargo, rustc_cargo, add_to_sysroot};
|
||||
use compile::{run_cargo, std_cargo, test_cargo, rustc_cargo, rustc_cargo_env, add_to_sysroot};
|
||||
use builder::{RunConfig, Builder, ShouldRun, Step};
|
||||
use {Build, Compiler, Mode};
|
||||
use cache::Interned;
|
||||
use tool::{self, prepare_tool_cargo};
|
||||
use {Compiler, Mode};
|
||||
use cache::{INTERNER, Interned};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@ -36,24 +37,24 @@ impl Step for Std {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let compiler = builder.compiler(0, build.build);
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libstd);
|
||||
builder.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
|
||||
let out_dir = build.stage_out(compiler, Mode::Libstd);
|
||||
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "check");
|
||||
std_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
let _folder = builder.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
println!("Checking std artifacts ({} -> {})", &compiler.host, target);
|
||||
run_cargo(build,
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&libstd_stamp(build, compiler, target),
|
||||
&libstd_stamp(builder, compiler, target),
|
||||
true);
|
||||
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
|
||||
add_to_sysroot(&builder, &libdir, &libstd_stamp(builder, compiler, target));
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,26 +84,71 @@ impl Step for Rustc {
|
||||
/// the `compiler` targeting the `target` architecture. The artifacts
|
||||
/// created will also be linked into the sysroot directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = builder.compiler(0, build.build);
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
|
||||
let stage_out = builder.stage_out(compiler, Mode::Librustc);
|
||||
build.clear_if_dirty(&stage_out, &libstd_stamp(build, compiler, target));
|
||||
build.clear_if_dirty(&stage_out, &libtest_stamp(build, compiler, target));
|
||||
builder.clear_if_dirty(&stage_out, &libstd_stamp(builder, compiler, target));
|
||||
builder.clear_if_dirty(&stage_out, &libtest_stamp(builder, compiler, target));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check");
|
||||
rustc_cargo(build, &mut cargo);
|
||||
rustc_cargo(builder, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
println!("Checking compiler artifacts ({} -> {})", &compiler.host, target);
|
||||
run_cargo(build,
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&librustc_stamp(build, compiler, target),
|
||||
&librustc_stamp(builder, compiler, target),
|
||||
true);
|
||||
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
add_to_sysroot(&libdir, &librustc_stamp(build, compiler, target));
|
||||
add_to_sysroot(&builder, &libdir, &librustc_stamp(builder, compiler, target));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CodegenBackend {
|
||||
pub target: Interned<String>,
|
||||
pub backend: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for CodegenBackend {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.all_krates("rustc_trans")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
let backend = run.builder.config.rust_codegen_backends.get(0);
|
||||
let backend = backend.cloned().unwrap_or_else(|| {
|
||||
INTERNER.intern_str("llvm")
|
||||
});
|
||||
run.builder.ensure(CodegenBackend {
|
||||
target: run.target,
|
||||
backend,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
let backend = self.backend;
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check");
|
||||
let features = builder.rustc_features().to_string();
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_trans/Cargo.toml"));
|
||||
rustc_cargo_env(builder, &mut cargo);
|
||||
|
||||
// We won't build LLVM if it's not available, as it shouldn't affect `check`.
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
|
||||
run_cargo(builder,
|
||||
cargo.arg("--features").arg(features),
|
||||
&codegen_backend_stamp(builder, compiler, target, backend),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,41 +172,105 @@ impl Step for Test {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
let compiler = builder.compiler(0, build.build);
|
||||
|
||||
let out_dir = build.stage_out(compiler, Mode::Libtest);
|
||||
build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libtest);
|
||||
builder.clear_if_dirty(&out_dir, &libstd_stamp(builder, compiler, target));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "check");
|
||||
test_cargo(build, &compiler, target, &mut cargo);
|
||||
test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
let _folder = builder.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
println!("Checking test artifacts ({} -> {})", &compiler.host, target);
|
||||
run_cargo(build,
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&libtest_stamp(build, compiler, target),
|
||||
&libtest_stamp(builder, compiler, target),
|
||||
true);
|
||||
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
add_to_sysroot(&libdir, &libtest_stamp(build, compiler, target));
|
||||
add_to_sysroot(builder, &libdir, &libtest_stamp(builder, compiler, target));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Rustdoc {
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustdoc {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/tools/rustdoc")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rustdoc {
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
target,
|
||||
"check",
|
||||
"src/tools/rustdoc");
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustdoc", compiler.stage));
|
||||
println!("Checking rustdoc artifacts ({} -> {})", &compiler.host, target);
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&rustdoc_stamp(builder, compiler, target),
|
||||
true);
|
||||
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
add_to_sysroot(&builder, &libdir, &rustdoc_stamp(builder, compiler, target));
|
||||
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler,
|
||||
target,
|
||||
mode: Mode::Tool,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Cargo's output path for the standard library in a given stage, compiled
|
||||
/// by a particular compiler for the specified target.
|
||||
pub fn libstd_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Libstd, target).join(".libstd-check.stamp")
|
||||
pub fn libstd_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libstd, target).join(".libstd-check.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for libtest in a given stage, compiled by a particular
|
||||
/// compiler for the specified target.
|
||||
pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Libtest, target).join(".libtest-check.stamp")
|
||||
pub fn libtest_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libtest, target).join(".libtest-check.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for librustc in a given stage, compiled by a particular
|
||||
/// compiler for the specified target.
|
||||
pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Librustc, target).join(".librustc-check.stamp")
|
||||
pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Librustc, target).join(".librustc-check.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for librustc_trans in a given stage, compiled by a particular
|
||||
/// compiler for the specified target and backend.
|
||||
fn codegen_backend_stamp(builder: &Builder,
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
backend: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Librustc, target)
|
||||
.join(format!(".librustc_trans-{}-check.stamp", backend))
|
||||
}
|
||||
|
||||
/// Cargo's output path for rustdoc in a given stage, compiled by a particular
|
||||
/// compiler for the specified target.
|
||||
pub fn rustdoc_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Tool, target).join(".rustdoc-check.stamp")
|
||||
}
|
||||
|
||||
@ -30,15 +30,15 @@ use build_helper::{output, mtime, up_to_date};
|
||||
use filetime::FileTime;
|
||||
use serde_json;
|
||||
|
||||
use util::{exe, libdir, is_dylib, copy, read_stamp_file, CiEnv};
|
||||
use {Build, Compiler, Mode};
|
||||
use util::{exe, libdir, is_dylib, CiEnv};
|
||||
use {Compiler, Mode};
|
||||
use native;
|
||||
use tool;
|
||||
|
||||
use cache::{INTERNER, Interned};
|
||||
use builder::{Step, RunConfig, ShouldRun, Builder};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Std {
|
||||
pub target: Interned<String>,
|
||||
pub compiler: Compiler,
|
||||
@ -65,25 +65,24 @@ impl Step for Std {
|
||||
/// using the `compiler` targeting the `target` architecture. The artifacts
|
||||
/// created will also be linked into the sysroot directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let compiler = self.compiler;
|
||||
|
||||
builder.ensure(StartupObjects { compiler, target });
|
||||
|
||||
if build.force_use_stage1(compiler, target) {
|
||||
let from = builder.compiler(1, build.build);
|
||||
if builder.force_use_stage1(compiler, target) {
|
||||
let from = builder.compiler(1, builder.config.build);
|
||||
builder.ensure(Std {
|
||||
compiler: from,
|
||||
target,
|
||||
});
|
||||
println!("Uplifting stage1 std ({} -> {})", from.host, target);
|
||||
builder.info(&format!("Uplifting stage1 std ({} -> {})", from.host, target));
|
||||
|
||||
// Even if we're not building std this stage, the new sysroot must
|
||||
// still contain the musl startup objects.
|
||||
if target.contains("musl") {
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
copy_musl_third_party_objects(build, target, &libdir);
|
||||
copy_musl_third_party_objects(builder, target, &libdir);
|
||||
}
|
||||
|
||||
builder.ensure(StdLink {
|
||||
@ -96,24 +95,24 @@ impl Step for Std {
|
||||
|
||||
if target.contains("musl") {
|
||||
let libdir = builder.sysroot_libdir(compiler, target);
|
||||
copy_musl_third_party_objects(build, target, &libdir);
|
||||
copy_musl_third_party_objects(builder, target, &libdir);
|
||||
}
|
||||
|
||||
let out_dir = build.cargo_out(compiler, Mode::Libstd, target);
|
||||
build.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Libstd, target);
|
||||
builder.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
|
||||
std_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
println!("Building stage{} std artifacts ({} -> {})", compiler.stage,
|
||||
&compiler.host, target);
|
||||
run_cargo(build,
|
||||
let _folder = builder.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
builder.info(&format!("Building stage{} std artifacts ({} -> {})", compiler.stage,
|
||||
&compiler.host, target));
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&libstd_stamp(build, compiler, target),
|
||||
&libstd_stamp(builder, compiler, target),
|
||||
false);
|
||||
|
||||
builder.ensure(StdLink {
|
||||
compiler: builder.compiler(compiler.stage, build.build),
|
||||
compiler: builder.compiler(compiler.stage, builder.config.build),
|
||||
target_compiler: compiler,
|
||||
target,
|
||||
});
|
||||
@ -126,62 +125,72 @@ impl Step for Std {
|
||||
/// with a glibc-targeting toolchain, given we have the appropriate startup
|
||||
/// files. As those shipped with glibc won't work, copy the ones provided by
|
||||
/// musl so we have them on linux-gnu hosts.
|
||||
fn copy_musl_third_party_objects(build: &Build,
|
||||
fn copy_musl_third_party_objects(builder: &Builder,
|
||||
target: Interned<String>,
|
||||
into: &Path) {
|
||||
for &obj in &["crt1.o", "crti.o", "crtn.o"] {
|
||||
copy(&build.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
|
||||
builder.copy(&builder.musl_root(target).unwrap().join("lib").join(obj), &into.join(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure cargo to compile the standard library, adding appropriate env vars
|
||||
/// and such.
|
||||
pub fn std_cargo(build: &Builder,
|
||||
pub fn std_cargo(builder: &Builder,
|
||||
compiler: &Compiler,
|
||||
target: Interned<String>,
|
||||
cargo: &mut Command) {
|
||||
let mut features = build.std_features();
|
||||
|
||||
if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") {
|
||||
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
|
||||
}
|
||||
|
||||
// When doing a local rebuild we tell cargo that we're stage1 rather than
|
||||
// stage0. This works fine if the local rust and being-built rust have the
|
||||
// same view of what the default allocator is, but fails otherwise. Since
|
||||
// we don't have a way to express an allocator preference yet, work
|
||||
// around the issue in the case of a local rebuild with jemalloc disabled.
|
||||
if compiler.stage == 0 && build.local_rebuild && !build.config.use_jemalloc {
|
||||
features.push_str(" force_alloc_system");
|
||||
}
|
||||
if builder.no_std(target) == Some(true) {
|
||||
// for no-std targets we only compile a few no_std crates
|
||||
cargo.arg("--features").arg("c mem")
|
||||
.args(&["-p", "alloc"])
|
||||
.args(&["-p", "compiler_builtins"])
|
||||
.args(&["-p", "std_unicode"])
|
||||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/rustc/compiler_builtins_shim/Cargo.toml"));
|
||||
} else {
|
||||
let mut features = builder.std_features();
|
||||
|
||||
if compiler.stage != 0 && build.config.sanitizers {
|
||||
// This variable is used by the sanitizer runtime crates, e.g.
|
||||
// rustc_lsan, to build the sanitizer runtime from C code
|
||||
// When this variable is missing, those crates won't compile the C code,
|
||||
// so we don't set this variable during stage0 where llvm-config is
|
||||
// missing
|
||||
// We also only build the runtimes when --enable-sanitizers (or its
|
||||
// config.toml equivalent) is used
|
||||
let llvm_config = build.ensure(native::Llvm {
|
||||
target: build.config.build,
|
||||
emscripten: false,
|
||||
});
|
||||
cargo.env("LLVM_CONFIG", llvm_config);
|
||||
}
|
||||
|
||||
cargo.arg("--features").arg(features)
|
||||
.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libstd/Cargo.toml"));
|
||||
|
||||
if let Some(target) = build.config.target_config.get(&target) {
|
||||
if let Some(ref jemalloc) = target.jemalloc {
|
||||
cargo.env("JEMALLOC_OVERRIDE", jemalloc);
|
||||
// When doing a local rebuild we tell cargo that we're stage1 rather than
|
||||
// stage0. This works fine if the local rust and being-built rust have the
|
||||
// same view of what the default allocator is, but fails otherwise. Since
|
||||
// we don't have a way to express an allocator preference yet, work
|
||||
// around the issue in the case of a local rebuild with jemalloc disabled.
|
||||
if compiler.stage == 0 && builder.local_rebuild && !builder.config.use_jemalloc {
|
||||
features.push_str(" force_alloc_system");
|
||||
}
|
||||
}
|
||||
if target.contains("musl") {
|
||||
if let Some(p) = build.musl_root(target) {
|
||||
cargo.env("MUSL_ROOT", p);
|
||||
|
||||
if compiler.stage != 0 && builder.config.sanitizers {
|
||||
// This variable is used by the sanitizer runtime crates, e.g.
|
||||
// rustc_lsan, to build the sanitizer runtime from C code
|
||||
// When this variable is missing, those crates won't compile the C code,
|
||||
// so we don't set this variable during stage0 where llvm-config is
|
||||
// missing
|
||||
// We also only build the runtimes when --enable-sanitizers (or its
|
||||
// config.toml equivalent) is used
|
||||
let llvm_config = builder.ensure(native::Llvm {
|
||||
target: builder.config.build,
|
||||
emscripten: false,
|
||||
});
|
||||
cargo.env("LLVM_CONFIG", llvm_config);
|
||||
}
|
||||
|
||||
cargo.arg("--features").arg(features)
|
||||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/libstd/Cargo.toml"));
|
||||
|
||||
if let Some(target) = builder.config.target_config.get(&target) {
|
||||
if let Some(ref jemalloc) = target.jemalloc {
|
||||
cargo.env("JEMALLOC_OVERRIDE", jemalloc);
|
||||
}
|
||||
}
|
||||
if target.contains("musl") {
|
||||
if let Some(p) = builder.musl_root(target) {
|
||||
cargo.env("MUSL_ROOT", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,24 +218,23 @@ impl Step for StdLink {
|
||||
/// libraries for `target`, and this method will find them in the relevant
|
||||
/// output directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target_compiler = self.target_compiler;
|
||||
let target = self.target;
|
||||
println!("Copying stage{} std from stage{} ({} -> {} / {})",
|
||||
builder.info(&format!("Copying stage{} std from stage{} ({} -> {} / {})",
|
||||
target_compiler.stage,
|
||||
compiler.stage,
|
||||
&compiler.host,
|
||||
target_compiler.host,
|
||||
target);
|
||||
target));
|
||||
let libdir = builder.sysroot_libdir(target_compiler, target);
|
||||
add_to_sysroot(&libdir, &libstd_stamp(build, compiler, target));
|
||||
add_to_sysroot(builder, &libdir, &libstd_stamp(builder, compiler, target));
|
||||
|
||||
if build.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
|
||||
if builder.config.sanitizers && compiler.stage != 0 && target == "x86_64-apple-darwin" {
|
||||
// The sanitizers are only built in stage1 or above, so the dylibs will
|
||||
// be missing in stage0 and causes panic. See the `std()` function above
|
||||
// for reason why the sanitizers are not built in stage0.
|
||||
copy_apple_sanitizer_dylibs(&build.native_dir(target), "osx", &libdir);
|
||||
copy_apple_sanitizer_dylibs(builder, &builder.native_dir(target), "osx", &libdir);
|
||||
}
|
||||
|
||||
builder.ensure(tool::CleanTools {
|
||||
@ -237,7 +245,7 @@ impl Step for StdLink {
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
|
||||
fn copy_apple_sanitizer_dylibs(builder: &Builder, native_dir: &Path, platform: &str, into: &Path) {
|
||||
for &sanitizer in &["asan", "tsan"] {
|
||||
let filename = format!("libclang_rt.{}_{}_dynamic.dylib", sanitizer, platform);
|
||||
let mut src_path = native_dir.join(sanitizer);
|
||||
@ -245,7 +253,7 @@ fn copy_apple_sanitizer_dylibs(native_dir: &Path, platform: &str, into: &Path) {
|
||||
src_path.push("lib");
|
||||
src_path.push("darwin");
|
||||
src_path.push(&filename);
|
||||
copy(&src_path, &into.join(filename));
|
||||
builder.copy(&src_path, &into.join(filename));
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,15 +284,14 @@ impl Step for StartupObjects {
|
||||
/// files, so we just use the nightly snapshot compiler to always build them (as
|
||||
/// no other compilers are guaranteed to be available).
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let for_compiler = self.compiler;
|
||||
let target = self.target;
|
||||
if !target.contains("pc-windows-gnu") {
|
||||
return
|
||||
}
|
||||
|
||||
let src_dir = &build.src.join("src/rtstartup");
|
||||
let dst_dir = &build.native_dir(target).join("rtstartup");
|
||||
let src_dir = &builder.src.join("src/rtstartup");
|
||||
let dst_dir = &builder.native_dir(target).join("rtstartup");
|
||||
let sysroot_dir = &builder.sysroot_libdir(for_compiler, target);
|
||||
t!(fs::create_dir_all(dst_dir));
|
||||
|
||||
@ -292,8 +299,8 @@ impl Step for StartupObjects {
|
||||
let src_file = &src_dir.join(file.to_string() + ".rs");
|
||||
let dst_file = &dst_dir.join(file.to_string() + ".o");
|
||||
if !up_to_date(src_file, dst_file) {
|
||||
let mut cmd = Command::new(&build.initial_rustc);
|
||||
build.run(cmd.env("RUSTC_BOOTSTRAP", "1")
|
||||
let mut cmd = Command::new(&builder.initial_rustc);
|
||||
builder.run(cmd.env("RUSTC_BOOTSTRAP", "1")
|
||||
.arg("--cfg").arg("stage0")
|
||||
.arg("--target").arg(target)
|
||||
.arg("--emit=obj")
|
||||
@ -301,23 +308,23 @@ impl Step for StartupObjects {
|
||||
.arg(src_file));
|
||||
}
|
||||
|
||||
copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
|
||||
builder.copy(dst_file, &sysroot_dir.join(file.to_string() + ".o"));
|
||||
}
|
||||
|
||||
for obj in ["crt2.o", "dllcrt2.o"].iter() {
|
||||
let src = compiler_file(build,
|
||||
build.cc(target),
|
||||
let src = compiler_file(builder,
|
||||
builder.cc(target),
|
||||
target,
|
||||
obj);
|
||||
copy(&src, &sysroot_dir.join(obj));
|
||||
builder.copy(&src, &sysroot_dir.join(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Test {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
pub compiler: Compiler,
|
||||
}
|
||||
|
||||
impl Step for Test {
|
||||
@ -341,41 +348,41 @@ impl Step for Test {
|
||||
/// the build using the `compiler` targeting the `target` architecture. The
|
||||
/// artifacts created will also be linked into the sysroot directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let compiler = self.compiler;
|
||||
|
||||
builder.ensure(Std { compiler, target });
|
||||
|
||||
if build.force_use_stage1(compiler, target) {
|
||||
if builder.force_use_stage1(compiler, target) {
|
||||
builder.ensure(Test {
|
||||
compiler: builder.compiler(1, build.build),
|
||||
compiler: builder.compiler(1, builder.config.build),
|
||||
target,
|
||||
});
|
||||
println!("Uplifting stage1 test ({} -> {})", &build.build, target);
|
||||
builder.info(
|
||||
&format!("Uplifting stage1 test ({} -> {})", builder.config.build, target));
|
||||
builder.ensure(TestLink {
|
||||
compiler: builder.compiler(1, build.build),
|
||||
compiler: builder.compiler(1, builder.config.build),
|
||||
target_compiler: compiler,
|
||||
target,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let out_dir = build.cargo_out(compiler, Mode::Libtest, target);
|
||||
build.clear_if_dirty(&out_dir, &libstd_stamp(build, compiler, target));
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Libtest, target);
|
||||
builder.clear_if_dirty(&out_dir, &libstd_stamp(builder, compiler, target));
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
|
||||
test_cargo(build, &compiler, target, &mut cargo);
|
||||
test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
println!("Building stage{} test artifacts ({} -> {})", compiler.stage,
|
||||
&compiler.host, target);
|
||||
run_cargo(build,
|
||||
let _folder = builder.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
builder.info(&format!("Building stage{} test artifacts ({} -> {})", compiler.stage,
|
||||
&compiler.host, target));
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&libtest_stamp(build, compiler, target),
|
||||
&libtest_stamp(builder, compiler, target),
|
||||
false);
|
||||
|
||||
builder.ensure(TestLink {
|
||||
compiler: builder.compiler(compiler.stage, build.build),
|
||||
compiler: builder.compiler(compiler.stage, builder.config.build),
|
||||
target_compiler: compiler,
|
||||
target,
|
||||
});
|
||||
@ -383,7 +390,7 @@ impl Step for Test {
|
||||
}
|
||||
|
||||
/// Same as `std_cargo`, but for libtest
|
||||
pub fn test_cargo(build: &Build,
|
||||
pub fn test_cargo(builder: &Builder,
|
||||
_compiler: &Compiler,
|
||||
_target: Interned<String>,
|
||||
cargo: &mut Command) {
|
||||
@ -391,7 +398,7 @@ pub fn test_cargo(build: &Build,
|
||||
cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
|
||||
}
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/libtest/Cargo.toml"));
|
||||
.arg(builder.src.join("src/libtest/Cargo.toml"));
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
@ -410,18 +417,17 @@ impl Step for TestLink {
|
||||
|
||||
/// Same as `std_link`, only for libtest
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target_compiler = self.target_compiler;
|
||||
let target = self.target;
|
||||
println!("Copying stage{} test from stage{} ({} -> {} / {})",
|
||||
builder.info(&format!("Copying stage{} test from stage{} ({} -> {} / {})",
|
||||
target_compiler.stage,
|
||||
compiler.stage,
|
||||
&compiler.host,
|
||||
target_compiler.host,
|
||||
target);
|
||||
add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
|
||||
&libtest_stamp(build, compiler, target));
|
||||
target));
|
||||
add_to_sysroot(builder, &builder.sysroot_libdir(target_compiler, target),
|
||||
&libtest_stamp(builder, compiler, target));
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler: target_compiler,
|
||||
target,
|
||||
@ -430,10 +436,10 @@ impl Step for TestLink {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Rustc {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
pub compiler: Compiler,
|
||||
}
|
||||
|
||||
impl Step for Rustc {
|
||||
@ -458,20 +464,20 @@ impl Step for Rustc {
|
||||
/// the `compiler` targeting the `target` architecture. The artifacts
|
||||
/// created will also be linked into the sysroot directory.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
|
||||
builder.ensure(Test { compiler, target });
|
||||
|
||||
if build.force_use_stage1(compiler, target) {
|
||||
if builder.force_use_stage1(compiler, target) {
|
||||
builder.ensure(Rustc {
|
||||
compiler: builder.compiler(1, build.build),
|
||||
compiler: builder.compiler(1, builder.config.build),
|
||||
target,
|
||||
});
|
||||
println!("Uplifting stage1 rustc ({} -> {})", &build.build, target);
|
||||
builder.info(&format!("Uplifting stage1 rustc ({} -> {})",
|
||||
builder.config.build, target));
|
||||
builder.ensure(RustcLink {
|
||||
compiler: builder.compiler(1, build.build),
|
||||
compiler: builder.compiler(1, builder.config.build),
|
||||
target_compiler: compiler,
|
||||
target,
|
||||
});
|
||||
@ -480,71 +486,71 @@ impl Step for Rustc {
|
||||
|
||||
// Ensure that build scripts have a std to link against.
|
||||
builder.ensure(Std {
|
||||
compiler: builder.compiler(self.compiler.stage, build.build),
|
||||
target: build.build,
|
||||
compiler: builder.compiler(self.compiler.stage, builder.config.build),
|
||||
target: builder.config.build,
|
||||
});
|
||||
let cargo_out = builder.cargo_out(compiler, Mode::Librustc, target);
|
||||
build.clear_if_dirty(&cargo_out, &libstd_stamp(build, compiler, target));
|
||||
build.clear_if_dirty(&cargo_out, &libtest_stamp(build, compiler, target));
|
||||
builder.clear_if_dirty(&cargo_out, &libstd_stamp(builder, compiler, target));
|
||||
builder.clear_if_dirty(&cargo_out, &libtest_stamp(builder, compiler, target));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
|
||||
rustc_cargo(build, &mut cargo);
|
||||
rustc_cargo(builder, &mut cargo);
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
println!("Building stage{} compiler artifacts ({} -> {})",
|
||||
compiler.stage, &compiler.host, target);
|
||||
run_cargo(build,
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
builder.info(&format!("Building stage{} compiler artifacts ({} -> {})",
|
||||
compiler.stage, &compiler.host, target));
|
||||
run_cargo(builder,
|
||||
&mut cargo,
|
||||
&librustc_stamp(build, compiler, target),
|
||||
&librustc_stamp(builder, compiler, target),
|
||||
false);
|
||||
|
||||
builder.ensure(RustcLink {
|
||||
compiler: builder.compiler(compiler.stage, build.build),
|
||||
compiler: builder.compiler(compiler.stage, builder.config.build),
|
||||
target_compiler: compiler,
|
||||
target,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rustc_cargo(build: &Build, cargo: &mut Command) {
|
||||
cargo.arg("--features").arg(build.rustc_features())
|
||||
pub fn rustc_cargo(builder: &Builder, cargo: &mut Command) {
|
||||
cargo.arg("--features").arg(builder.rustc_features())
|
||||
.arg("--manifest-path")
|
||||
.arg(build.src.join("src/rustc/Cargo.toml"));
|
||||
rustc_cargo_env(build, cargo);
|
||||
.arg(builder.src.join("src/rustc/Cargo.toml"));
|
||||
rustc_cargo_env(builder, cargo);
|
||||
}
|
||||
|
||||
fn rustc_cargo_env(build: &Build, cargo: &mut Command) {
|
||||
pub fn rustc_cargo_env(builder: &Builder, cargo: &mut Command) {
|
||||
// Set some configuration variables picked up by build scripts and
|
||||
// the compiler alike
|
||||
cargo.env("CFG_RELEASE", build.rust_release())
|
||||
.env("CFG_RELEASE_CHANNEL", &build.config.channel)
|
||||
.env("CFG_VERSION", build.rust_version())
|
||||
.env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default())
|
||||
.env("CFG_CODEGEN_BACKENDS_DIR", &build.config.rust_codegen_backends_dir);
|
||||
cargo.env("CFG_RELEASE", builder.rust_release())
|
||||
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
|
||||
.env("CFG_VERSION", builder.rust_version())
|
||||
.env("CFG_PREFIX", builder.config.prefix.clone().unwrap_or_default())
|
||||
.env("CFG_CODEGEN_BACKENDS_DIR", &builder.config.rust_codegen_backends_dir);
|
||||
|
||||
let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib"));
|
||||
let libdir_relative = builder.config.libdir_relative().unwrap_or(Path::new("lib"));
|
||||
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
|
||||
|
||||
// If we're not building a compiler with debugging information then remove
|
||||
// these two env vars which would be set otherwise.
|
||||
if build.config.rust_debuginfo_only_std {
|
||||
if builder.config.rust_debuginfo_only_std {
|
||||
cargo.env_remove("RUSTC_DEBUGINFO");
|
||||
cargo.env_remove("RUSTC_DEBUGINFO_LINES");
|
||||
}
|
||||
|
||||
if let Some(ref ver_date) = build.rust_info.commit_date() {
|
||||
if let Some(ref ver_date) = builder.rust_info.commit_date() {
|
||||
cargo.env("CFG_VER_DATE", ver_date);
|
||||
}
|
||||
if let Some(ref ver_hash) = build.rust_info.sha() {
|
||||
if let Some(ref ver_hash) = builder.rust_info.sha() {
|
||||
cargo.env("CFG_VER_HASH", ver_hash);
|
||||
}
|
||||
if !build.unstable_features() {
|
||||
if !builder.unstable_features() {
|
||||
cargo.env("CFG_DISABLE_UNSTABLE_FEATURES", "1");
|
||||
}
|
||||
if let Some(ref s) = build.config.rustc_default_linker {
|
||||
if let Some(ref s) = builder.config.rustc_default_linker {
|
||||
cargo.env("CFG_DEFAULT_LINKER", s);
|
||||
}
|
||||
if build.config.rustc_parallel_queries {
|
||||
if builder.config.rustc_parallel_queries {
|
||||
cargo.env("RUSTC_PARALLEL_QUERIES", "1");
|
||||
}
|
||||
}
|
||||
@ -565,18 +571,17 @@ impl Step for RustcLink {
|
||||
|
||||
/// Same as `std_link`, only for librustc
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target_compiler = self.target_compiler;
|
||||
let target = self.target;
|
||||
println!("Copying stage{} rustc from stage{} ({} -> {} / {})",
|
||||
builder.info(&format!("Copying stage{} rustc from stage{} ({} -> {} / {})",
|
||||
target_compiler.stage,
|
||||
compiler.stage,
|
||||
&compiler.host,
|
||||
target_compiler.host,
|
||||
target);
|
||||
add_to_sysroot(&builder.sysroot_libdir(target_compiler, target),
|
||||
&librustc_stamp(build, compiler, target));
|
||||
target));
|
||||
add_to_sysroot(builder, &builder.sysroot_libdir(target_compiler, target),
|
||||
&librustc_stamp(builder, compiler, target));
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler: target_compiler,
|
||||
target,
|
||||
@ -609,87 +614,45 @@ impl Step for CodegenBackend {
|
||||
run.builder.ensure(CodegenBackend {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.host),
|
||||
target: run.target,
|
||||
backend
|
||||
backend,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let backend = self.backend;
|
||||
|
||||
builder.ensure(Rustc { compiler, target });
|
||||
|
||||
if build.force_use_stage1(compiler, target) {
|
||||
if builder.force_use_stage1(compiler, target) {
|
||||
builder.ensure(CodegenBackend {
|
||||
compiler: builder.compiler(1, build.build),
|
||||
compiler: builder.compiler(1, builder.config.build),
|
||||
target,
|
||||
backend: self.backend,
|
||||
backend,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
|
||||
let mut features = build.rustc_features().to_string();
|
||||
let mut features = builder.rustc_features().to_string();
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(build.src.join("src/librustc_trans/Cargo.toml"));
|
||||
rustc_cargo_env(build, &mut cargo);
|
||||
.arg(builder.src.join("src/librustc_trans/Cargo.toml"));
|
||||
rustc_cargo_env(builder, &mut cargo);
|
||||
|
||||
match &*self.backend {
|
||||
"llvm" | "emscripten" => {
|
||||
// Build LLVM for our target. This will implicitly build the
|
||||
// host LLVM if necessary.
|
||||
let llvm_config = builder.ensure(native::Llvm {
|
||||
target,
|
||||
emscripten: self.backend == "emscripten",
|
||||
});
|
||||
features += &build_codegen_backend(&builder, &mut cargo, &compiler, target, backend);
|
||||
|
||||
if self.backend == "emscripten" {
|
||||
features.push_str(" emscripten");
|
||||
}
|
||||
|
||||
println!("Building stage{} codegen artifacts ({} -> {}, {})",
|
||||
compiler.stage, &compiler.host, target, self.backend);
|
||||
|
||||
// Pass down configuration from the LLVM build into the build of
|
||||
// librustc_llvm and librustc_trans.
|
||||
if build.is_rust_llvm(target) {
|
||||
cargo.env("LLVM_RUSTLLVM", "1");
|
||||
}
|
||||
cargo.env("LLVM_CONFIG", &llvm_config);
|
||||
if self.backend != "emscripten" {
|
||||
let target_config = build.config.target_config.get(&target);
|
||||
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
|
||||
cargo.env("CFG_LLVM_ROOT", s);
|
||||
}
|
||||
}
|
||||
// Building with a static libstdc++ is only supported on linux right now,
|
||||
// not for MSVC or macOS
|
||||
if build.config.llvm_static_stdcpp &&
|
||||
!target.contains("freebsd") &&
|
||||
!target.contains("windows") &&
|
||||
!target.contains("apple") {
|
||||
let file = compiler_file(build,
|
||||
build.cxx(target).unwrap(),
|
||||
target,
|
||||
"libstdc++.a");
|
||||
cargo.env("LLVM_STATIC_STDCPP", file);
|
||||
}
|
||||
if build.config.llvm_link_shared {
|
||||
cargo.env("LLVM_LINK_SHARED", "1");
|
||||
}
|
||||
}
|
||||
_ => panic!("unknown backend: {}", self.backend),
|
||||
}
|
||||
|
||||
let tmp_stamp = build.cargo_out(compiler, Mode::Librustc, target)
|
||||
let tmp_stamp = builder.cargo_out(compiler, Mode::Librustc, target)
|
||||
.join(".tmp.stamp");
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
|
||||
let files = run_cargo(build,
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
|
||||
let files = run_cargo(builder,
|
||||
cargo.arg("--features").arg(features),
|
||||
&tmp_stamp,
|
||||
false);
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
}
|
||||
let mut files = files.into_iter()
|
||||
.filter(|f| {
|
||||
let filename = f.file_name().unwrap().to_str().unwrap();
|
||||
@ -704,12 +667,69 @@ impl Step for CodegenBackend {
|
||||
codegen_backend.display(),
|
||||
f.display());
|
||||
}
|
||||
let stamp = codegen_backend_stamp(build, compiler, target, self.backend);
|
||||
let stamp = codegen_backend_stamp(builder, compiler, target, backend);
|
||||
let codegen_backend = codegen_backend.to_str().unwrap();
|
||||
t!(t!(File::create(&stamp)).write_all(codegen_backend.as_bytes()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_codegen_backend(builder: &Builder,
|
||||
cargo: &mut Command,
|
||||
compiler: &Compiler,
|
||||
target: Interned<String>,
|
||||
backend: Interned<String>) -> String {
|
||||
let mut features = String::new();
|
||||
|
||||
match &*backend {
|
||||
"llvm" | "emscripten" => {
|
||||
// Build LLVM for our target. This will implicitly build the
|
||||
// host LLVM if necessary.
|
||||
let llvm_config = builder.ensure(native::Llvm {
|
||||
target,
|
||||
emscripten: backend == "emscripten",
|
||||
});
|
||||
|
||||
if backend == "emscripten" {
|
||||
features.push_str(" emscripten");
|
||||
}
|
||||
|
||||
builder.info(&format!("Building stage{} codegen artifacts ({} -> {}, {})",
|
||||
compiler.stage, &compiler.host, target, backend));
|
||||
|
||||
// Pass down configuration from the LLVM build into the build of
|
||||
// librustc_llvm and librustc_trans.
|
||||
if builder.is_rust_llvm(target) {
|
||||
cargo.env("LLVM_RUSTLLVM", "1");
|
||||
}
|
||||
cargo.env("LLVM_CONFIG", &llvm_config);
|
||||
if backend != "emscripten" {
|
||||
let target_config = builder.config.target_config.get(&target);
|
||||
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
|
||||
cargo.env("CFG_LLVM_ROOT", s);
|
||||
}
|
||||
}
|
||||
// Building with a static libstdc++ is only supported on linux right now,
|
||||
// not for MSVC or macOS
|
||||
if builder.config.llvm_static_stdcpp &&
|
||||
!target.contains("freebsd") &&
|
||||
!target.contains("windows") &&
|
||||
!target.contains("apple") {
|
||||
let file = compiler_file(builder,
|
||||
builder.cxx(target).unwrap(),
|
||||
target,
|
||||
"libstdc++.a");
|
||||
cargo.env("LLVM_STATIC_STDCPP", file);
|
||||
}
|
||||
if builder.config.llvm_link_shared {
|
||||
cargo.env("LLVM_LINK_SHARED", "1");
|
||||
}
|
||||
}
|
||||
_ => panic!("unknown backend: {}", backend),
|
||||
}
|
||||
|
||||
features
|
||||
}
|
||||
|
||||
/// Creates the `codegen-backends` folder for a compiler that's about to be
|
||||
/// assembled as a complete compiler.
|
||||
///
|
||||
@ -719,7 +739,6 @@ impl Step for CodegenBackend {
|
||||
fn copy_codegen_backends_to_sysroot(builder: &Builder,
|
||||
compiler: Compiler,
|
||||
target_compiler: Compiler) {
|
||||
let build = builder.build;
|
||||
let target = target_compiler.host;
|
||||
|
||||
// Note that this step is different than all the other `*Link` steps in
|
||||
@ -733,8 +752,12 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
|
||||
let dst = builder.sysroot_codegen_backends(target_compiler);
|
||||
t!(fs::create_dir_all(&dst));
|
||||
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
}
|
||||
|
||||
for backend in builder.config.rust_codegen_backends.iter() {
|
||||
let stamp = codegen_backend_stamp(build, compiler, target, *backend);
|
||||
let stamp = codegen_backend_stamp(builder, compiler, target, *backend);
|
||||
let mut dylib = String::new();
|
||||
t!(t!(File::open(&stamp)).read_to_string(&mut dylib));
|
||||
let file = Path::new(&dylib);
|
||||
@ -748,7 +771,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
|
||||
backend,
|
||||
&filename[dot..])
|
||||
};
|
||||
copy(&file, &dst.join(target_filename));
|
||||
builder.copy(&file, &dst.join(target_filename));
|
||||
}
|
||||
}
|
||||
|
||||
@ -764,41 +787,43 @@ fn copy_lld_to_sysroot(builder: &Builder,
|
||||
t!(fs::create_dir_all(&dst));
|
||||
|
||||
let exe = exe("lld", &target);
|
||||
copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe));
|
||||
builder.copy(&lld_install_root.join("bin").join(&exe), &dst.join(&exe));
|
||||
}
|
||||
|
||||
/// Cargo's output path for the standard library in a given stage, compiled
|
||||
/// by a particular compiler for the specified target.
|
||||
pub fn libstd_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Libstd, target).join(".libstd.stamp")
|
||||
pub fn libstd_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libstd, target).join(".libstd.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for libtest in a given stage, compiled by a particular
|
||||
/// compiler for the specified target.
|
||||
pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
|
||||
pub fn libtest_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for librustc in a given stage, compiled by a particular
|
||||
/// compiler for the specified target.
|
||||
pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
|
||||
pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
|
||||
}
|
||||
|
||||
fn codegen_backend_stamp(build: &Build,
|
||||
/// Cargo's output path for librustc_trans in a given stage, compiled by a particular
|
||||
/// compiler for the specified target and backend.
|
||||
fn codegen_backend_stamp(builder: &Builder,
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
backend: Interned<String>) -> PathBuf {
|
||||
build.cargo_out(compiler, Mode::Librustc, target)
|
||||
builder.cargo_out(compiler, Mode::Librustc, target)
|
||||
.join(format!(".librustc_trans-{}.stamp", backend))
|
||||
}
|
||||
|
||||
fn compiler_file(build: &Build,
|
||||
pub fn compiler_file(builder: &Builder,
|
||||
compiler: &Path,
|
||||
target: Interned<String>,
|
||||
file: &str) -> PathBuf {
|
||||
let mut cmd = Command::new(compiler);
|
||||
cmd.args(build.cflags(target));
|
||||
cmd.args(builder.cflags(target));
|
||||
cmd.arg(format!("-print-file-name={}", file));
|
||||
let out = output(&mut cmd);
|
||||
PathBuf::from(out.trim())
|
||||
@ -823,12 +848,11 @@ impl Step for Sysroot {
|
||||
/// thinks it is by default, but it's the same as the default for stages
|
||||
/// 1-3.
|
||||
fn run(self, builder: &Builder) -> Interned<PathBuf> {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let sysroot = if compiler.stage == 0 {
|
||||
build.out.join(&compiler.host).join("stage0-sysroot")
|
||||
builder.out.join(&compiler.host).join("stage0-sysroot")
|
||||
} else {
|
||||
build.out.join(&compiler.host).join(format!("stage{}", compiler.stage))
|
||||
builder.out.join(&compiler.host).join(format!("stage{}", compiler.stage))
|
||||
};
|
||||
let _ = fs::remove_dir_all(&sysroot);
|
||||
t!(fs::create_dir_all(&sysroot));
|
||||
@ -836,7 +860,7 @@ impl Step for Sysroot {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Copy, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Assemble {
|
||||
/// The compiler which we will produce in this step. Assemble itself will
|
||||
/// take care of ensuring that the necessary prerequisites to do so exist,
|
||||
@ -855,14 +879,13 @@ impl Step for Assemble {
|
||||
/// Prepare a new compiler from the artifacts in `stage`
|
||||
///
|
||||
/// This will assemble a compiler in `build/$host/stage$stage`. The compiler
|
||||
/// must have been previously produced by the `stage - 1` build.build
|
||||
/// must have been previously produced by the `stage - 1` builder.build
|
||||
/// compiler.
|
||||
fn run(self, builder: &Builder) -> Compiler {
|
||||
let build = builder.build;
|
||||
let target_compiler = self.target_compiler;
|
||||
|
||||
if target_compiler.stage == 0 {
|
||||
assert_eq!(build.build, target_compiler.host,
|
||||
assert_eq!(builder.config.build, target_compiler.host,
|
||||
"Cannot obtain compiler for non-native build triple at stage 0");
|
||||
// The stage 0 compiler for the build triple is always pre-built.
|
||||
return target_compiler;
|
||||
@ -885,14 +908,14 @@ impl Step for Assemble {
|
||||
// FIXME: It may be faster if we build just a stage 1 compiler and then
|
||||
// use that to bootstrap this compiler forward.
|
||||
let build_compiler =
|
||||
builder.compiler(target_compiler.stage - 1, build.build);
|
||||
builder.compiler(target_compiler.stage - 1, builder.config.build);
|
||||
|
||||
// Build the libraries for this compiler to link to (i.e., the libraries
|
||||
// it uses at runtime). NOTE: Crates the target compiler compiles don't
|
||||
// link to these. (FIXME: Is that correct? It seems to be correct most
|
||||
// of the time but I think we do link to these for stage2/bin compilers
|
||||
// when not performing a full bootstrap).
|
||||
if builder.build.config.keep_stage.map_or(false, |s| target_compiler.stage <= s) {
|
||||
if builder.config.keep_stage.map_or(false, |s| target_compiler.stage <= s) {
|
||||
builder.verbose("skipping compilation of compiler due to --keep-stage");
|
||||
let compiler = build_compiler;
|
||||
for stage in 0..min(target_compiler.stage, builder.config.keep_stage.unwrap()) {
|
||||
@ -907,7 +930,7 @@ impl Step for Assemble {
|
||||
compiler: build_compiler,
|
||||
target: target_compiler.host,
|
||||
});
|
||||
for &backend in build.config.rust_codegen_backends.iter() {
|
||||
for &backend in builder.config.rust_codegen_backends.iter() {
|
||||
builder.ensure(CodegenBackend {
|
||||
compiler: build_compiler,
|
||||
target: target_compiler.host,
|
||||
@ -916,7 +939,7 @@ impl Step for Assemble {
|
||||
}
|
||||
}
|
||||
|
||||
let lld_install = if build.config.lld_enabled {
|
||||
let lld_install = if builder.config.lld_enabled {
|
||||
Some(builder.ensure(native::Lld {
|
||||
target: target_compiler.host,
|
||||
}))
|
||||
@ -926,17 +949,17 @@ impl Step for Assemble {
|
||||
|
||||
let stage = target_compiler.stage;
|
||||
let host = target_compiler.host;
|
||||
println!("Assembling stage{} compiler ({})", stage, host);
|
||||
builder.info(&format!("Assembling stage{} compiler ({})", stage, host));
|
||||
|
||||
// Link in all dylibs to the libdir
|
||||
let sysroot = builder.sysroot(target_compiler);
|
||||
let sysroot_libdir = sysroot.join(libdir(&*host));
|
||||
t!(fs::create_dir_all(&sysroot_libdir));
|
||||
let src_libdir = builder.sysroot_libdir(build_compiler, host);
|
||||
for f in t!(fs::read_dir(&src_libdir)).map(|f| t!(f)) {
|
||||
for f in builder.read_dir(&src_libdir) {
|
||||
let filename = f.file_name().into_string().unwrap();
|
||||
if is_dylib(&filename) {
|
||||
copy(&f.path(), &sysroot_libdir.join(&filename));
|
||||
builder.copy(&f.path(), &sysroot_libdir.join(&filename));
|
||||
}
|
||||
}
|
||||
|
||||
@ -948,13 +971,13 @@ impl Step for Assemble {
|
||||
}
|
||||
|
||||
// Link the compiler binary itself into place
|
||||
let out_dir = build.cargo_out(build_compiler, Mode::Librustc, host);
|
||||
let out_dir = builder.cargo_out(build_compiler, Mode::Librustc, host);
|
||||
let rustc = out_dir.join(exe("rustc", &*host));
|
||||
let bindir = sysroot.join("bin");
|
||||
t!(fs::create_dir_all(&bindir));
|
||||
let compiler = builder.rustc(target_compiler);
|
||||
let _ = fs::remove_file(&compiler);
|
||||
copy(&rustc, &compiler);
|
||||
builder.copy(&rustc, &compiler);
|
||||
|
||||
target_compiler
|
||||
}
|
||||
@ -964,10 +987,10 @@ impl Step for Assemble {
|
||||
///
|
||||
/// For a particular stage this will link the file listed in `stamp` into the
|
||||
/// `sysroot_dst` provided.
|
||||
pub fn add_to_sysroot(sysroot_dst: &Path, stamp: &Path) {
|
||||
pub fn add_to_sysroot(builder: &Builder, sysroot_dst: &Path, stamp: &Path) {
|
||||
t!(fs::create_dir_all(&sysroot_dst));
|
||||
for path in read_stamp_file(stamp) {
|
||||
copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
|
||||
for path in builder.read_stamp_file(stamp) {
|
||||
builder.copy(&path, &sysroot_dst.join(path.file_name().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -994,9 +1017,13 @@ fn stderr_isatty() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: bool)
|
||||
pub fn run_cargo(builder: &Builder, cargo: &mut Command, stamp: &Path, is_check: bool)
|
||||
-> Vec<PathBuf>
|
||||
{
|
||||
if builder.config.dry_run {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// `target_root_dir` looks like $dir/$target/release
|
||||
let target_root_dir = stamp.parent().unwrap();
|
||||
// `target_deps_dir` looks like $dir/$target/release/deps
|
||||
@ -1011,7 +1038,7 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
|
||||
// files we need to probe for later.
|
||||
let mut deps = Vec::new();
|
||||
let mut toplevel = Vec::new();
|
||||
let ok = stream_cargo(build, cargo, &mut |msg| {
|
||||
let ok = stream_cargo(builder, cargo, &mut |msg| {
|
||||
let filenames = match msg {
|
||||
CargoMessage::CompilerArtifact { filenames, .. } => filenames,
|
||||
_ => return,
|
||||
@ -1120,37 +1147,42 @@ pub fn run_cargo(build: &Build, cargo: &mut Command, stamp: &Path, is_check: boo
|
||||
let max = max.unwrap();
|
||||
let max_path = max_path.unwrap();
|
||||
if stamp_contents == new_contents && max <= stamp_mtime {
|
||||
build.verbose(&format!("not updating {:?}; contents equal and {:?} <= {:?}",
|
||||
builder.verbose(&format!("not updating {:?}; contents equal and {:?} <= {:?}",
|
||||
stamp, max, stamp_mtime));
|
||||
return deps
|
||||
}
|
||||
if max > stamp_mtime {
|
||||
build.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
|
||||
builder.verbose(&format!("updating {:?} as {:?} changed", stamp, max_path));
|
||||
} else {
|
||||
build.verbose(&format!("updating {:?} as deps changed", stamp));
|
||||
builder.verbose(&format!("updating {:?} as deps changed", stamp));
|
||||
}
|
||||
t!(t!(File::create(stamp)).write_all(&new_contents));
|
||||
deps
|
||||
}
|
||||
|
||||
pub fn stream_cargo(
|
||||
build: &Build,
|
||||
builder: &Builder,
|
||||
cargo: &mut Command,
|
||||
cb: &mut FnMut(CargoMessage),
|
||||
) -> bool {
|
||||
if builder.config.dry_run {
|
||||
return true;
|
||||
}
|
||||
// Instruct Cargo to give us json messages on stdout, critically leaving
|
||||
// stderr as piped so we can get those pretty colors.
|
||||
cargo.arg("--message-format").arg("json")
|
||||
.stdout(Stdio::piped());
|
||||
|
||||
if stderr_isatty() && build.ci_env == CiEnv::None {
|
||||
if stderr_isatty() && builder.ci_env == CiEnv::None &&
|
||||
// if the terminal is reported as dumb, then we don't want to enable color for rustc
|
||||
env::var_os("TERM").map(|t| t != *"dumb").unwrap_or(true) {
|
||||
// since we pass message-format=json to cargo, we need to tell the rustc
|
||||
// wrapper to give us colored output if necessary. This is because we
|
||||
// only want Cargo's JSON output, not rustcs.
|
||||
cargo.env("RUSTC_COLOR", "1");
|
||||
}
|
||||
|
||||
build.verbose(&format!("running: {:?}", cargo));
|
||||
builder.verbose(&format!("running: {:?}", cargo));
|
||||
let mut child = match cargo.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(e) => panic!("failed to execute command: {:?}\nerror: {}", cargo, e),
|
||||
@ -1172,7 +1204,7 @@ pub fn stream_cargo(
|
||||
// Make sure Cargo actually succeeded after we read all of its stdout.
|
||||
let status = t!(child.wait());
|
||||
if !status.success() {
|
||||
println!("command did not execute successfully: {:?}\n\
|
||||
eprintln!("command did not execute successfully: {:?}\n\
|
||||
expected success, got: {}",
|
||||
cargo,
|
||||
status);
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
@ -69,6 +69,10 @@ pub struct Config {
|
||||
pub jobs: Option<u32>,
|
||||
pub cmd: Subcommand,
|
||||
pub incremental: bool,
|
||||
pub dry_run: bool,
|
||||
|
||||
pub deny_warnings: bool,
|
||||
pub backtrace_on_ice: bool,
|
||||
|
||||
// llvm codegen options
|
||||
pub llvm_enabled: bool,
|
||||
@ -91,6 +95,7 @@ pub struct Config {
|
||||
pub rust_debuginfo: bool,
|
||||
pub rust_debuginfo_lines: bool,
|
||||
pub rust_debuginfo_only_std: bool,
|
||||
pub rust_debuginfo_tools: bool,
|
||||
pub rust_rpath: bool,
|
||||
pub rustc_parallel_queries: bool,
|
||||
pub rustc_default_linker: Option<String>,
|
||||
@ -143,6 +148,7 @@ pub struct Config {
|
||||
// These are either the stage0 downloaded binaries or the locally installed ones.
|
||||
pub initial_cargo: PathBuf,
|
||||
pub initial_rustc: PathBuf,
|
||||
pub out: PathBuf,
|
||||
}
|
||||
|
||||
/// Per-target configuration stored in the global configuration structure.
|
||||
@ -159,6 +165,7 @@ pub struct Target {
|
||||
pub crt_static: Option<bool>,
|
||||
pub musl_root: Option<PathBuf>,
|
||||
pub qemu_rootfs: Option<PathBuf>,
|
||||
pub no_std: bool,
|
||||
}
|
||||
|
||||
/// Structure of the `config.toml` file that configuration is read from.
|
||||
@ -277,6 +284,7 @@ struct Rust {
|
||||
debuginfo: Option<bool>,
|
||||
debuginfo_lines: Option<bool>,
|
||||
debuginfo_only_std: Option<bool>,
|
||||
debuginfo_tools: Option<bool>,
|
||||
experimental_parallel_queries: Option<bool>,
|
||||
debug_jemalloc: Option<bool>,
|
||||
use_jemalloc: Option<bool>,
|
||||
@ -298,6 +306,8 @@ struct Rust {
|
||||
codegen_backends_dir: Option<String>,
|
||||
wasm_syscall: Option<bool>,
|
||||
lld: Option<bool>,
|
||||
deny_warnings: Option<bool>,
|
||||
backtrace_on_ice: Option<bool>,
|
||||
}
|
||||
|
||||
/// TOML representation of how each build target is configured.
|
||||
@ -317,11 +327,16 @@ struct TomlTarget {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn parse(args: &[String]) -> Config {
|
||||
let flags = Flags::parse(&args);
|
||||
let file = flags.config.clone();
|
||||
fn path_from_python(var_key: &str) -> PathBuf {
|
||||
match env::var_os(var_key) {
|
||||
// Do not trust paths from Python and normalize them slightly (#49785).
|
||||
Some(var_val) => Path::new(&var_val).components().collect(),
|
||||
_ => panic!("expected '{}' to be set", var_key),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_opts() -> Config {
|
||||
let mut config = Config::default();
|
||||
config.exclude = flags.exclude;
|
||||
config.llvm_enabled = true;
|
||||
config.llvm_optimize = true;
|
||||
config.llvm_version_check = true;
|
||||
@ -340,15 +355,42 @@ impl Config {
|
||||
config.test_miri = false;
|
||||
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
|
||||
config.rust_codegen_backends_dir = "codegen-backends".to_owned();
|
||||
config.deny_warnings = true;
|
||||
|
||||
// set by bootstrap.py
|
||||
config.build = INTERNER.intern_str(&env::var("BUILD").expect("'BUILD' to be set"));
|
||||
config.src = Config::path_from_python("SRC");
|
||||
config.out = Config::path_from_python("BUILD_DIR");
|
||||
|
||||
let stage0_root = config.out.join(&config.build).join("stage0/bin");
|
||||
config.initial_rustc = stage0_root.join(exe("rustc", &config.build));
|
||||
config.initial_cargo = stage0_root.join(exe("cargo", &config.build));
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn parse(args: &[String]) -> Config {
|
||||
let flags = Flags::parse(&args);
|
||||
let file = flags.config.clone();
|
||||
let mut config = Config::default_opts();
|
||||
config.exclude = flags.exclude;
|
||||
config.rustc_error_format = flags.rustc_error_format;
|
||||
config.on_fail = flags.on_fail;
|
||||
config.stage = flags.stage;
|
||||
config.src = flags.src;
|
||||
config.jobs = flags.jobs;
|
||||
config.cmd = flags.cmd;
|
||||
config.incremental = flags.incremental;
|
||||
config.dry_run = flags.dry_run;
|
||||
config.keep_stage = flags.keep_stage;
|
||||
if let Some(value) = flags.warnings {
|
||||
config.deny_warnings = value;
|
||||
}
|
||||
|
||||
if config.dry_run {
|
||||
let dir = config.out.join("tmp-dry-run");
|
||||
t!(fs::create_dir_all(&dir));
|
||||
config.out = dir;
|
||||
}
|
||||
|
||||
// If --target was specified but --host wasn't specified, don't run any host-only tests.
|
||||
config.run_host_only = !(flags.host.is_empty() && !flags.target.is_empty());
|
||||
@ -368,12 +410,7 @@ impl Config {
|
||||
}).unwrap_or_else(|| TomlConfig::default());
|
||||
|
||||
let build = toml.build.clone().unwrap_or(Build::default());
|
||||
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
|
||||
set(&mut config.build, flags.build);
|
||||
if config.build.is_empty() {
|
||||
// set by bootstrap.py
|
||||
config.build = INTERNER.intern_str(&env::var("BUILD").unwrap());
|
||||
}
|
||||
// set by bootstrap.py
|
||||
config.hosts.push(config.build.clone());
|
||||
for host in build.host.iter() {
|
||||
let host = INTERNER.intern_str(host);
|
||||
@ -437,6 +474,7 @@ impl Config {
|
||||
let mut llvm_assertions = None;
|
||||
let mut debuginfo_lines = None;
|
||||
let mut debuginfo_only_std = None;
|
||||
let mut debuginfo_tools = None;
|
||||
let mut debug = None;
|
||||
let mut debug_jemalloc = None;
|
||||
let mut debuginfo = None;
|
||||
@ -474,6 +512,7 @@ impl Config {
|
||||
debuginfo = rust.debuginfo;
|
||||
debuginfo_lines = rust.debuginfo_lines;
|
||||
debuginfo_only_std = rust.debuginfo_only_std;
|
||||
debuginfo_tools = rust.debuginfo_tools;
|
||||
optimize = rust.optimize;
|
||||
ignore_git = rust.ignore_git;
|
||||
debug_jemalloc = rust.debug_jemalloc;
|
||||
@ -493,6 +532,8 @@ impl Config {
|
||||
config.rustc_default_linker = rust.default_linker.clone();
|
||||
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
|
||||
config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from);
|
||||
set(&mut config.deny_warnings, rust.deny_warnings.or(flags.warnings));
|
||||
set(&mut config.backtrace_on_ice, rust.backtrace_on_ice);
|
||||
|
||||
if let Some(ref backends) = rust.codegen_backends {
|
||||
config.rust_codegen_backends = backends.iter()
|
||||
@ -514,13 +555,13 @@ impl Config {
|
||||
let mut target = Target::default();
|
||||
|
||||
if let Some(ref s) = cfg.llvm_config {
|
||||
target.llvm_config = Some(env::current_dir().unwrap().join(s));
|
||||
target.llvm_config = Some(config.src.join(s));
|
||||
}
|
||||
if let Some(ref s) = cfg.jemalloc {
|
||||
target.jemalloc = Some(env::current_dir().unwrap().join(s));
|
||||
target.jemalloc = Some(config.src.join(s));
|
||||
}
|
||||
if let Some(ref s) = cfg.android_ndk {
|
||||
target.ndk = Some(env::current_dir().unwrap().join(s));
|
||||
target.ndk = Some(config.src.join(s));
|
||||
}
|
||||
target.cc = cfg.cc.clone().map(PathBuf::from);
|
||||
target.cxx = cfg.cxx.clone().map(PathBuf::from);
|
||||
@ -541,22 +582,12 @@ impl Config {
|
||||
set(&mut config.rust_dist_src, t.src_tarball);
|
||||
}
|
||||
|
||||
let cwd = t!(env::current_dir());
|
||||
let out = cwd.join("build");
|
||||
|
||||
let stage0_root = out.join(&config.build).join("stage0/bin");
|
||||
config.initial_rustc = match build.rustc {
|
||||
Some(s) => PathBuf::from(s),
|
||||
None => stage0_root.join(exe("rustc", &config.build)),
|
||||
};
|
||||
config.initial_cargo = match build.cargo {
|
||||
Some(s) => PathBuf::from(s),
|
||||
None => stage0_root.join(exe("cargo", &config.build)),
|
||||
};
|
||||
|
||||
// Now that we've reached the end of our configuration, infer the
|
||||
// default values for all options that we haven't otherwise stored yet.
|
||||
|
||||
set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
|
||||
set(&mut config.initial_cargo, build.cargo.map(PathBuf::from));
|
||||
|
||||
let default = false;
|
||||
config.llvm_assertions = llvm_assertions.unwrap_or(default);
|
||||
|
||||
@ -566,6 +597,7 @@ impl Config {
|
||||
};
|
||||
config.rust_debuginfo_lines = debuginfo_lines.unwrap_or(default);
|
||||
config.rust_debuginfo_only_std = debuginfo_only_std.unwrap_or(default);
|
||||
config.rust_debuginfo_tools = debuginfo_tools.unwrap_or(false);
|
||||
|
||||
let default = debug == Some(true);
|
||||
config.debug_jemalloc = debug_jemalloc.unwrap_or(default);
|
||||
|
||||
@ -79,6 +79,7 @@ o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger
|
||||
o("debuginfo", "rust.debuginfo", "build with debugger metadata")
|
||||
o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
|
||||
o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
|
||||
o("debuginfo-tools", "rust.debuginfo-tools", "build extended tools with debugging information")
|
||||
o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill")
|
||||
v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
|
||||
|
||||
@ -119,6 +120,8 @@ v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root",
|
||||
"arm-unknown-linux-musleabi install directory")
|
||||
v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root",
|
||||
"arm-unknown-linux-musleabihf install directory")
|
||||
v("musl-root-armv5te", "target.armv5te-unknown-linux-musleabi.musl-root",
|
||||
"armv5te-unknown-linux-musleabi install directory")
|
||||
v("musl-root-armv7", "target.armv7-unknown-linux-musleabihf.musl-root",
|
||||
"armv7-unknown-linux-musleabihf install directory")
|
||||
v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root",
|
||||
@ -145,7 +148,7 @@ o("jemalloc", "rust.use-jemalloc", "build liballoc with jemalloc")
|
||||
o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
|
||||
o("extended", "build.extended", "build an extended rust tool set")
|
||||
|
||||
v("tools", "build.tools", "List of extended tools will be installed")
|
||||
v("tools", None, "List of extended tools will be installed")
|
||||
v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
|
||||
v("host", None, "GNUs ./configure syntax LLVM host triples")
|
||||
v("target", None, "GNUs ./configure syntax LLVM target triples")
|
||||
@ -321,6 +324,8 @@ for key in known_args:
|
||||
set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
|
||||
elif option.name == 'jemalloc-root':
|
||||
set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
|
||||
elif option.name == 'tools':
|
||||
set('build.tools', value.split(','))
|
||||
elif option.name == 'host':
|
||||
set('build.host', value.split(','))
|
||||
elif option.name == 'target':
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Documentation generation for rustbuild.
|
||||
//! Documentation generation for rustbuilder.
|
||||
//!
|
||||
//! This module implements generation for all bits and pieces of documentation
|
||||
//! for the Rust project. This notably includes suites like the rust book, the
|
||||
@ -23,14 +23,15 @@ use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::path::{PathBuf, Path};
|
||||
|
||||
use {Build, Mode};
|
||||
use Mode;
|
||||
use build_helper::up_to_date;
|
||||
|
||||
use util::{cp_r, symlink_dir};
|
||||
use util::symlink_dir;
|
||||
use builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
|
||||
use tool::Tool;
|
||||
use compile;
|
||||
use cache::{INTERNER, Interned};
|
||||
use config::Config;
|
||||
|
||||
macro_rules! book {
|
||||
($($name:ident, $path:expr, $book_name:expr;)+) => {
|
||||
@ -46,7 +47,7 @@ macro_rules! book {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path($path).default_condition(builder.build.config.docs)
|
||||
run.path($path).default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -70,6 +71,7 @@ book!(
|
||||
Nomicon, "src/doc/nomicon", "nomicon";
|
||||
Reference, "src/doc/reference", "reference";
|
||||
Rustdoc, "src/doc/rustdoc", "rustdoc";
|
||||
RustcBook, "src/doc/rustc", "rustc";
|
||||
RustByExample, "src/doc/rust-by-example", "rust-by-example";
|
||||
);
|
||||
|
||||
@ -93,7 +95,7 @@ impl Step for Rustbook {
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
fn run(self, builder: &Builder) {
|
||||
let src = builder.build.src.join("src/doc");
|
||||
let src = builder.src.join("src/doc");
|
||||
builder.ensure(RustbookSrc {
|
||||
target: self.target,
|
||||
name: self.name,
|
||||
@ -113,7 +115,7 @@ impl Step for UnstableBook {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc/unstable-book").default_condition(builder.build.config.docs)
|
||||
run.path("src/doc/unstable-book").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -129,7 +131,7 @@ impl Step for UnstableBook {
|
||||
builder.ensure(RustbookSrc {
|
||||
target: self.target,
|
||||
name: INTERNER.intern_str("unstable-book"),
|
||||
src: builder.build.md_doc_out(self.target),
|
||||
src: builder.md_doc_out(self.target),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -146,7 +148,7 @@ impl Step for CargoBook {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/cargo/src/doc/book").default_condition(builder.build.config.docs)
|
||||
run.path("src/tools/cargo/src/doc/book").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -157,22 +159,20 @@ impl Step for CargoBook {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
|
||||
let target = self.target;
|
||||
let name = self.name;
|
||||
let src = build.src.join("src/tools/cargo/src/doc");
|
||||
let src = builder.src.join("src/tools/cargo/src/doc");
|
||||
|
||||
let out = build.doc_out(target);
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let out = out.join(name);
|
||||
|
||||
println!("Cargo Book ({}) - {}", target, name);
|
||||
builder.info(&format!("Cargo Book ({}) - {}", target, name));
|
||||
|
||||
let _ = fs::remove_dir_all(&out);
|
||||
|
||||
build.run(builder.tool_cmd(Tool::Rustbook)
|
||||
builder.run(builder.tool_cmd(Tool::Rustbook)
|
||||
.arg("build")
|
||||
.arg(&src)
|
||||
.arg("-d")
|
||||
@ -199,23 +199,23 @@ impl Step for RustbookSrc {
|
||||
/// This will not actually generate any documentation if the documentation has
|
||||
/// already been generated.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let name = self.name;
|
||||
let src = self.src;
|
||||
let out = build.doc_out(target);
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let out = out.join(name);
|
||||
let src = src.join(name);
|
||||
let index = out.join("index.html");
|
||||
let rustbook = builder.tool_exe(Tool::Rustbook);
|
||||
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
|
||||
if up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
|
||||
return
|
||||
}
|
||||
println!("Rustbook ({}) - {}", target, name);
|
||||
builder.info(&format!("Rustbook ({}) - {}", target, name));
|
||||
let _ = fs::remove_dir_all(&out);
|
||||
build.run(builder.tool_cmd(Tool::Rustbook)
|
||||
builder.run(rustbook_cmd
|
||||
.arg("build")
|
||||
.arg(&src)
|
||||
.arg("-d")
|
||||
@ -236,12 +236,12 @@ impl Step for TheBook {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc/book").default_condition(builder.build.config.docs)
|
||||
run.path("src/doc/book").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(TheBook {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
name: "book",
|
||||
});
|
||||
@ -257,7 +257,6 @@ impl Step for TheBook {
|
||||
/// * Index page
|
||||
/// * Redirect pages
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let name = self.name;
|
||||
@ -281,12 +280,12 @@ impl Step for TheBook {
|
||||
|
||||
// build the index page
|
||||
let index = format!("{}/index.md", name);
|
||||
println!("Documenting book index ({})", target);
|
||||
builder.info(&format!("Documenting book index ({})", target));
|
||||
invoke_rustdoc(builder, compiler, target, &index);
|
||||
|
||||
// build the redirect pages
|
||||
println!("Documenting book redirect pages ({})", target);
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc/book/redirects"))) {
|
||||
builder.info(&format!("Documenting book redirect pages ({})", target));
|
||||
for file in t!(fs::read_dir(builder.src.join("src/doc/book/redirects"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let path = path.to_str().unwrap();
|
||||
@ -297,13 +296,12 @@ impl Step for TheBook {
|
||||
}
|
||||
|
||||
fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned<String>, markdown: &str) {
|
||||
let build = builder.build;
|
||||
let out = build.doc_out(target);
|
||||
let out = builder.doc_out(target);
|
||||
|
||||
let path = build.src.join("src/doc").join(markdown);
|
||||
let path = builder.src.join("src/doc").join(markdown);
|
||||
|
||||
let favicon = build.src.join("src/doc/favicon.inc");
|
||||
let footer = build.src.join("src/doc/footer.inc");
|
||||
let favicon = builder.src.join("src/doc/favicon.inc");
|
||||
let footer = builder.src.join("src/doc/footer.inc");
|
||||
let version_info = out.join("version_info.html");
|
||||
|
||||
let mut cmd = builder.rustdoc_cmd(compiler.host);
|
||||
@ -321,7 +319,7 @@ fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned<String
|
||||
.arg("--markdown-css")
|
||||
.arg("../rust.css");
|
||||
|
||||
build.run(&mut cmd);
|
||||
builder.run(&mut cmd);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
@ -336,12 +334,12 @@ impl Step for Standalone {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/doc").default_condition(builder.build.config.docs)
|
||||
run.path("src/doc").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Standalone {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
@ -355,31 +353,30 @@ impl Step for Standalone {
|
||||
///
|
||||
/// In the end, this is just a glorified wrapper around rustdoc!
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let compiler = self.compiler;
|
||||
println!("Documenting standalone ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
builder.info(&format!("Documenting standalone ({})", target));
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
let favicon = build.src.join("src/doc/favicon.inc");
|
||||
let footer = build.src.join("src/doc/footer.inc");
|
||||
let full_toc = build.src.join("src/doc/full-toc.inc");
|
||||
t!(fs::copy(build.src.join("src/doc/rust.css"), out.join("rust.css")));
|
||||
let favicon = builder.src.join("src/doc/favicon.inc");
|
||||
let footer = builder.src.join("src/doc/footer.inc");
|
||||
let full_toc = builder.src.join("src/doc/full-toc.inc");
|
||||
t!(fs::copy(builder.src.join("src/doc/rust.css"), out.join("rust.css")));
|
||||
|
||||
let version_input = build.src.join("src/doc/version_info.html.template");
|
||||
let version_input = builder.src.join("src/doc/version_info.html.template");
|
||||
let version_info = out.join("version_info.html");
|
||||
|
||||
if !up_to_date(&version_input, &version_info) {
|
||||
if !builder.config.dry_run && !up_to_date(&version_input, &version_info) {
|
||||
let mut info = String::new();
|
||||
t!(t!(File::open(&version_input)).read_to_string(&mut info));
|
||||
let info = info.replace("VERSION", &build.rust_release())
|
||||
.replace("SHORT_HASH", build.rust_info.sha_short().unwrap_or(""))
|
||||
.replace("STAMP", build.rust_info.sha().unwrap_or(""));
|
||||
let info = info.replace("VERSION", &builder.rust_release())
|
||||
.replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or(""))
|
||||
.replace("STAMP", builder.rust_info.sha().unwrap_or(""));
|
||||
t!(t!(File::create(&version_info)).write_all(info.as_bytes()));
|
||||
}
|
||||
|
||||
for file in t!(fs::read_dir(build.src.join("src/doc"))) {
|
||||
for file in t!(fs::read_dir(builder.src.join("src/doc"))) {
|
||||
let file = t!(file);
|
||||
let path = file.path();
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
@ -394,7 +391,7 @@ impl Step for Standalone {
|
||||
up_to_date(&favicon, &html) &&
|
||||
up_to_date(&full_toc, &html) &&
|
||||
up_to_date(&version_info, &html) &&
|
||||
up_to_date(&rustdoc, &html) {
|
||||
(builder.config.dry_run || up_to_date(&rustdoc, &html)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -414,7 +411,7 @@ impl Step for Standalone {
|
||||
} else {
|
||||
cmd.arg("--markdown-css").arg("rust.css");
|
||||
}
|
||||
build.run(&mut cmd);
|
||||
builder.run(&mut cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +428,7 @@ impl Step for Std {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.all_krates("std").default_condition(builder.build.config.docs)
|
||||
run.all_krates("std").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -446,22 +443,21 @@ impl Step for Std {
|
||||
/// This will generate all documentation for the standard library and its
|
||||
/// dependencies. This is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} std ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
builder.info(&format!("Documenting stage{} std ({})", stage, target));
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
builder.ensure(compile::Std { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Libstd)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libstd)
|
||||
.join(target).join("doc");
|
||||
|
||||
// Here what we're doing is creating a *symlink* (directory junction on
|
||||
@ -477,9 +473,9 @@ impl Step for Std {
|
||||
//
|
||||
// This way rustdoc generates output directly into the output, and rustdoc
|
||||
// will also directly handle merging.
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
let my_out = builder.crate_doc_out(target);
|
||||
builder.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "doc");
|
||||
compile::std_cargo(builder, &compiler, target, &mut cargo);
|
||||
@ -495,8 +491,8 @@ impl Step for Std {
|
||||
t!(fs::create_dir_all(out_dir.join(krate)));
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
builder.run(&mut cargo);
|
||||
builder.cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,7 +508,7 @@ impl Step for Test {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("test").default_condition(builder.build.config.docs)
|
||||
run.krate("test").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -527,15 +523,14 @@ impl Step for Test {
|
||||
/// This will generate all documentation for libtest and its dependencies. This
|
||||
/// is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} test ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
builder.info(&format!("Documenting stage{} test ({})", stage, target));
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
@ -545,21 +540,21 @@ impl Step for Test {
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Test { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Libtest)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libtest)
|
||||
.join(target).join("doc");
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
let my_out = builder.crate_doc_out(target);
|
||||
builder.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "doc");
|
||||
compile::test_cargo(build, &compiler, target, &mut cargo);
|
||||
compile::test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
cargo.arg("--no-deps").arg("-p").arg("test");
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
builder.run(&mut cargo);
|
||||
builder.cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -576,7 +571,7 @@ impl Step for WhitelistedRustc {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("rustc-main").default_condition(builder.build.config.docs)
|
||||
run.krate("rustc-main").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -596,15 +591,14 @@ impl Step for WhitelistedRustc {
|
||||
/// here as we want to be able to keep it separate from the standard
|
||||
/// documentation. This is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} whitelisted compiler ({})", stage, target);
|
||||
let out = build.doc_out(target);
|
||||
builder.info(&format!("Documenting stage{} whitelisted compiler ({})", stage, target));
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
@ -614,16 +608,16 @@ impl Step for WhitelistedRustc {
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Librustc)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Librustc)
|
||||
.join(target).join("doc");
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
let my_out = build.crate_doc_out(target);
|
||||
build.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&my_out, &out_dir));
|
||||
let my_out = builder.crate_doc_out(target);
|
||||
builder.clear_if_dirty(&my_out, &rustdoc);
|
||||
t!(symlink_dir_force(&builder.config, &my_out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
|
||||
compile::rustc_cargo(build, &mut cargo);
|
||||
compile::rustc_cargo(builder, &mut cargo);
|
||||
|
||||
// We don't want to build docs for internal compiler dependencies in this
|
||||
// step (there is another step for that). Therefore, we whitelist the crates
|
||||
@ -633,8 +627,8 @@ impl Step for WhitelistedRustc {
|
||||
cargo.arg("-p").arg(krate);
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
cp_r(&my_out, &out);
|
||||
builder.run(&mut cargo);
|
||||
builder.cp_r(&my_out, &out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,7 +645,7 @@ impl Step for Rustc {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.krate("rustc-main").default_condition(builder.build.config.docs)
|
||||
run.krate("rustc-main").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -668,22 +662,21 @@ impl Step for Rustc {
|
||||
/// we do not merge it with the other documentation from std, test and
|
||||
/// proc_macros. This is largely just a wrapper around `cargo doc`.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
println!("Documenting stage{} compiler ({})", stage, target);
|
||||
let out = build.compiler_doc_out(target);
|
||||
builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
|
||||
let out = builder.compiler_doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let compiler = builder.compiler(stage, build.build);
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if build.force_use_stage1(compiler, target) {
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
if !build.config.compiler_docs {
|
||||
println!("\tskipping - compiler docs disabled");
|
||||
if !builder.config.compiler_docs {
|
||||
builder.info(&format!("\tskipping - compiler docs disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -691,15 +684,16 @@ impl Step for Rustc {
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
let out_dir = build.stage_out(compiler, Mode::Librustc)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Librustc)
|
||||
.join(target).join("doc");
|
||||
// We do not symlink to the same shared folder that already contains std library
|
||||
// documentation from previous steps as we do not want to include that.
|
||||
build.clear_if_dirty(&out, &rustdoc);
|
||||
t!(symlink_dir_force(&out, &out_dir));
|
||||
builder.clear_if_dirty(&out, &rustdoc);
|
||||
t!(symlink_dir_force(&builder.config, &out, &out_dir));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "doc");
|
||||
compile::rustc_cargo(build, &mut cargo);
|
||||
cargo.env("RUSTDOCFLAGS", "--document-private-items");
|
||||
compile::rustc_cargo(builder, &mut cargo);
|
||||
|
||||
// Only include compiler crates, no dependencies of those, such as `libc`.
|
||||
cargo.arg("--no-deps");
|
||||
@ -708,19 +702,19 @@ impl Step for Rustc {
|
||||
let mut compiler_crates = HashSet::new();
|
||||
for root_crate in &["rustc", "rustc_driver"] {
|
||||
let interned_root_crate = INTERNER.intern_str(root_crate);
|
||||
find_compiler_crates(&build, &interned_root_crate, &mut compiler_crates);
|
||||
find_compiler_crates(builder, &interned_root_crate, &mut compiler_crates);
|
||||
}
|
||||
|
||||
for krate in &compiler_crates {
|
||||
cargo.arg("-p").arg(krate);
|
||||
}
|
||||
|
||||
build.run(&mut cargo);
|
||||
builder.run(&mut cargo);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_compiler_crates(
|
||||
build: &Build,
|
||||
builder: &Builder,
|
||||
name: &Interned<String>,
|
||||
crates: &mut HashSet<Interned<String>>
|
||||
) {
|
||||
@ -728,9 +722,9 @@ fn find_compiler_crates(
|
||||
crates.insert(*name);
|
||||
|
||||
// Look for dependencies.
|
||||
for dep in build.crates.get(name).unwrap().deps.iter() {
|
||||
if build.crates.get(dep).unwrap().is_local(build) {
|
||||
find_compiler_crates(build, dep, crates);
|
||||
for dep in builder.crates.get(name).unwrap().deps.iter() {
|
||||
if builder.crates.get(dep).unwrap().is_local(builder) {
|
||||
find_compiler_crates(builder, dep, crates);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -747,7 +741,7 @@ impl Step for ErrorIndex {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/error_index_generator").default_condition(builder.build.config.docs)
|
||||
run.path("src/tools/error_index_generator").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -759,21 +753,20 @@ impl Step for ErrorIndex {
|
||||
/// Generates the HTML rendered error-index by running the
|
||||
/// `error_index_generator` tool.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
|
||||
println!("Documenting error index ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
builder.info(&format!("Documenting error index ({})", target));
|
||||
let out = builder.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
let mut index = builder.tool_cmd(Tool::ErrorIndex);
|
||||
index.arg("html");
|
||||
index.arg(out.join("error-index.html"));
|
||||
|
||||
// FIXME: shouldn't have to pass this env var
|
||||
index.env("CFG_BUILD", &build.build)
|
||||
.env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir());
|
||||
index.env("CFG_BUILD", &builder.config.build)
|
||||
.env("RUSTC_ERROR_METADATA_DST", builder.extended_error_dir());
|
||||
|
||||
build.run(&mut index);
|
||||
builder.run(&mut index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,7 +782,7 @@ impl Step for UnstableBookGen {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/unstable-book-gen").default_condition(builder.build.config.docs)
|
||||
run.path("src/tools/unstable-book-gen").default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -799,27 +792,29 @@ impl Step for UnstableBookGen {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
|
||||
builder.ensure(compile::Std {
|
||||
compiler: builder.compiler(builder.top_stage, build.build),
|
||||
compiler: builder.compiler(builder.top_stage, builder.config.build),
|
||||
target,
|
||||
});
|
||||
|
||||
println!("Generating unstable book md files ({})", target);
|
||||
let out = build.md_doc_out(target).join("unstable-book");
|
||||
t!(fs::create_dir_all(&out));
|
||||
t!(fs::remove_dir_all(&out));
|
||||
builder.info(&format!("Generating unstable book md files ({})", target));
|
||||
let out = builder.md_doc_out(target).join("unstable-book");
|
||||
builder.create_dir(&out);
|
||||
builder.remove_dir(&out);
|
||||
let mut cmd = builder.tool_cmd(Tool::UnstableBookGen);
|
||||
cmd.arg(build.src.join("src"));
|
||||
cmd.arg(builder.src.join("src"));
|
||||
cmd.arg(out);
|
||||
|
||||
build.run(&mut cmd);
|
||||
builder.run(&mut cmd);
|
||||
}
|
||||
}
|
||||
|
||||
fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> {
|
||||
if config.dry_run {
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(m) = fs::symlink_metadata(dst) {
|
||||
if m.file_type().is_dir() {
|
||||
try!(fs::remove_dir_all(dst));
|
||||
@ -832,5 +827,5 @@ fn symlink_dir_force(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
symlink_dir(src, dst)
|
||||
symlink_dir(config, src, dst)
|
||||
}
|
||||
|
||||
@ -13,14 +13,13 @@
|
||||
//! This module implements the command-line parsing of the build system which
|
||||
//! has various flags to configure how it's run.
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use getopts::Options;
|
||||
|
||||
use Build;
|
||||
use {Build, DocTests};
|
||||
use config::Config;
|
||||
use metadata;
|
||||
use builder::Builder;
|
||||
@ -33,17 +32,19 @@ pub struct Flags {
|
||||
pub on_fail: Option<String>,
|
||||
pub stage: Option<u32>,
|
||||
pub keep_stage: Option<u32>,
|
||||
pub build: Option<Interned<String>>,
|
||||
|
||||
pub host: Vec<Interned<String>>,
|
||||
pub target: Vec<Interned<String>>,
|
||||
pub config: Option<PathBuf>,
|
||||
pub src: PathBuf,
|
||||
pub jobs: Option<u32>,
|
||||
pub cmd: Subcommand,
|
||||
pub incremental: bool,
|
||||
pub exclude: Vec<PathBuf>,
|
||||
pub rustc_error_format: Option<String>,
|
||||
pub dry_run: bool,
|
||||
|
||||
// true => deny
|
||||
pub warnings: Option<bool>,
|
||||
}
|
||||
|
||||
pub enum Subcommand {
|
||||
@ -61,7 +62,7 @@ pub enum Subcommand {
|
||||
test_args: Vec<String>,
|
||||
rustc_args: Vec<String>,
|
||||
fail_fast: bool,
|
||||
doc_tests: bool,
|
||||
doc_tests: DocTests,
|
||||
},
|
||||
Bench {
|
||||
paths: Vec<PathBuf>,
|
||||
@ -114,11 +115,14 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
opts.optmulti("", "target", "target targets to build", "TARGET");
|
||||
opts.optmulti("", "exclude", "build paths to exclude", "PATH");
|
||||
opts.optopt("", "on-fail", "command to run on failure", "CMD");
|
||||
opts.optflag("", "dry-run", "dry run; don't build anything");
|
||||
opts.optopt("", "stage", "stage to build", "N");
|
||||
opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
|
||||
opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
|
||||
opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
|
||||
opts.optflag("h", "help", "print this help message");
|
||||
opts.optopt("", "warnings", "if value is deny, will deny warnings, otherwise use default",
|
||||
"VALUE");
|
||||
opts.optopt("", "error-format", "rustc error format", "FORMAT");
|
||||
|
||||
// fn usage()
|
||||
@ -167,7 +171,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
"extra options to pass the compiler when running tests",
|
||||
"ARGS",
|
||||
);
|
||||
opts.optflag("", "doc", "run doc tests");
|
||||
opts.optflag("", "no-doc", "do not run doc tests");
|
||||
opts.optflag("", "doc", "only run doc tests");
|
||||
},
|
||||
"bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
|
||||
"clean" => { opts.optflag("", "all", "clean all build artifacts"); },
|
||||
@ -278,10 +283,6 @@ Arguments:
|
||||
_ => { }
|
||||
};
|
||||
// Get any optional paths which occur after the subcommand
|
||||
let cwd = t!(env::current_dir());
|
||||
let src = matches.opt_str("src").map(PathBuf::from)
|
||||
.or_else(|| env::var_os("SRC").map(PathBuf::from))
|
||||
.unwrap_or(cwd.clone());
|
||||
let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
|
||||
|
||||
let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
|
||||
@ -324,7 +325,13 @@ Arguments:
|
||||
test_args: matches.opt_strs("test-args"),
|
||||
rustc_args: matches.opt_strs("rustc-args"),
|
||||
fail_fast: !matches.opt_present("no-fail-fast"),
|
||||
doc_tests: matches.opt_present("doc"),
|
||||
doc_tests: if matches.opt_present("doc") {
|
||||
DocTests::Only
|
||||
} else if matches.opt_present("no-doc") {
|
||||
DocTests::No
|
||||
} else {
|
||||
DocTests::Yes
|
||||
}
|
||||
}
|
||||
}
|
||||
"bench" => {
|
||||
@ -362,19 +369,13 @@ Arguments:
|
||||
};
|
||||
|
||||
|
||||
let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());
|
||||
|
||||
if matches.opt_present("incremental") && stage.is_none() {
|
||||
stage = Some(1);
|
||||
}
|
||||
|
||||
Flags {
|
||||
verbose: matches.opt_count("verbose"),
|
||||
stage,
|
||||
stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
|
||||
dry_run: matches.opt_present("dry-run"),
|
||||
on_fail: matches.opt_str("on-fail"),
|
||||
rustc_error_format: matches.opt_str("error-format"),
|
||||
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
|
||||
build: matches.opt_str("build").map(|s| INTERNER.intern_string(s)),
|
||||
host: split(matches.opt_strs("host"))
|
||||
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
|
||||
target: split(matches.opt_strs("target"))
|
||||
@ -385,7 +386,7 @@ Arguments:
|
||||
incremental: matches.opt_present("incremental"),
|
||||
exclude: split(matches.opt_strs("exclude"))
|
||||
.into_iter().map(|p| p.into()).collect::<Vec<_>>(),
|
||||
src,
|
||||
warnings: matches.opt_str("warnings").map(|v| v == "deny"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -417,10 +418,10 @@ impl Subcommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doc_tests(&self) -> bool {
|
||||
pub fn doc_tests(&self) -> DocTests {
|
||||
match *self {
|
||||
Subcommand::Test { doc_tests, .. } => doc_tests,
|
||||
_ => false,
|
||||
_ => DocTests::Yes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,8 +62,7 @@ fn install_sh(
|
||||
stage: u32,
|
||||
host: Option<Interned<String>>
|
||||
) {
|
||||
let build = builder.build;
|
||||
println!("Install {} stage{} ({:?})", package, stage, host);
|
||||
builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));
|
||||
|
||||
let prefix_default = PathBuf::from("/usr/local");
|
||||
let sysconfdir_default = PathBuf::from("/etc");
|
||||
@ -72,13 +71,15 @@ fn install_sh(
|
||||
let bindir_default = PathBuf::from("bin");
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = datadir_default.join("man");
|
||||
let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
|
||||
let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let datadir = build.config.datadir.as_ref().unwrap_or(&datadir_default);
|
||||
let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
|
||||
let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
|
||||
let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);
|
||||
let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
|
||||
fs::canonicalize(p).expect(&format!("could not canonicalize {}", p.display()))
|
||||
});
|
||||
let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
|
||||
let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
let bindir = builder.config.bindir.as_ref().unwrap_or(&bindir_default);
|
||||
let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
|
||||
let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);
|
||||
|
||||
let sysconfdir = prefix.join(sysconfdir);
|
||||
let datadir = prefix.join(datadir);
|
||||
@ -97,18 +98,18 @@ fn install_sh(
|
||||
let libdir = add_destdir(&libdir, &destdir);
|
||||
let mandir = add_destdir(&mandir, &destdir);
|
||||
|
||||
let empty_dir = build.out.join("tmp/empty_dir");
|
||||
let empty_dir = builder.out.join("tmp/empty_dir");
|
||||
|
||||
t!(fs::create_dir_all(&empty_dir));
|
||||
let package_name = if let Some(host) = host {
|
||||
format!("{}-{}", pkgname(build, name), host)
|
||||
format!("{}-{}", pkgname(builder, name), host)
|
||||
} else {
|
||||
pkgname(build, name)
|
||||
pkgname(builder, name)
|
||||
};
|
||||
|
||||
let mut cmd = Command::new("sh");
|
||||
cmd.current_dir(&empty_dir)
|
||||
.arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
|
||||
.arg(sanitize_sh(&tmpdir(builder).join(&package_name).join("install.sh")))
|
||||
.arg(format!("--prefix={}", sanitize_sh(&prefix)))
|
||||
.arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
|
||||
.arg(format!("--datadir={}", sanitize_sh(&datadir)))
|
||||
@ -117,7 +118,7 @@ fn install_sh(
|
||||
.arg(format!("--libdir={}", sanitize_sh(&libdir)))
|
||||
.arg(format!("--mandir={}", sanitize_sh(&mandir)))
|
||||
.arg("--disable-ldconfig");
|
||||
build.run(&mut cmd);
|
||||
builder.run(&mut cmd);
|
||||
t!(fs::remove_dir_all(&empty_dir));
|
||||
}
|
||||
|
||||
@ -178,7 +179,7 @@ macro_rules! install {
|
||||
run.builder.ensure($name {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
host: run.builder.build.build,
|
||||
host: run.builder.config.build,
|
||||
});
|
||||
}
|
||||
|
||||
@ -195,7 +196,7 @@ install!((self, builder, _config),
|
||||
install_docs(builder, self.stage, self.target);
|
||||
};
|
||||
Std, "src/libstd", true, only_hosts: true, {
|
||||
for target in &builder.build.targets {
|
||||
for target in &builder.targets {
|
||||
builder.ensure(dist::Std {
|
||||
compiler: builder.compiler(self.stage, self.host),
|
||||
target: *target
|
||||
@ -212,7 +213,7 @@ install!((self, builder, _config),
|
||||
Self::should_install(builder) {
|
||||
install_rls(builder, self.stage, self.target);
|
||||
} else {
|
||||
println!("skipping Install RLS stage{} ({})", self.stage, self.target);
|
||||
builder.info(&format!("skipping Install RLS stage{} ({})", self.stage, self.target));
|
||||
}
|
||||
};
|
||||
Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
|
||||
@ -220,7 +221,8 @@ install!((self, builder, _config),
|
||||
Self::should_install(builder) {
|
||||
install_rustfmt(builder, self.stage, self.target);
|
||||
} else {
|
||||
println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target);
|
||||
builder.info(
|
||||
&format!("skipping Install Rustfmt stage{} ({})", self.stage, self.target));
|
||||
}
|
||||
};
|
||||
Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
|
||||
|
||||
@ -122,12 +122,10 @@ struct JOBOBJECT_BASIC_LIMIT_INFORMATION {
|
||||
}
|
||||
|
||||
pub unsafe fn setup(build: &mut Build) {
|
||||
// Tell Windows to not show any UI on errors (such as not finding a required dll
|
||||
// during startup or terminating abnormally). This is important for running tests,
|
||||
// since some of them use abnormal termination by design.
|
||||
// This mode is inherited by all child processes.
|
||||
let mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); // read inherited flags
|
||||
SetErrorMode(mode | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
// Enable the Windows Error Reporting dialog which msys disables,
|
||||
// so we can JIT debug rustc
|
||||
let mode = SetErrorMode(0);
|
||||
SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
|
||||
|
||||
// Create a new job object for us to use
|
||||
let job = CreateJobObjectW(0 as *mut _, 0 as *const _);
|
||||
|
||||
@ -115,7 +115,6 @@
|
||||
|
||||
#![deny(warnings)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(slice_concat_ext)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate build_helper;
|
||||
@ -131,6 +130,11 @@ extern crate getopts;
|
||||
extern crate num_cpus;
|
||||
extern crate toml;
|
||||
extern crate time;
|
||||
extern crate petgraph;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
@ -138,13 +142,15 @@ extern crate libc;
|
||||
use std::cell::{RefCell, Cell};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::fs::{self, OpenOptions, File};
|
||||
use std::io::{self, Seek, SeekFrom, Write, Read};
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::{self, Command};
|
||||
use std::slice;
|
||||
use std::str;
|
||||
|
||||
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
|
||||
use filetime::FileTime;
|
||||
|
||||
use util::{exe, libdir, OutputFolder, CiEnv};
|
||||
|
||||
@ -198,12 +204,22 @@ use toolstate::ToolState;
|
||||
/// Each compiler has a `stage` that it is associated with and a `host` that
|
||||
/// corresponds to the platform the compiler runs on. This structure is used as
|
||||
/// a parameter to many methods below.
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)]
|
||||
pub struct Compiler {
|
||||
stage: u32,
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum DocTests {
|
||||
// Default, run normal tests and doc tests.
|
||||
Yes,
|
||||
// Do not run any doc tests.
|
||||
No,
|
||||
// Only run doc tests.
|
||||
Only,
|
||||
}
|
||||
|
||||
/// Global configuration for the build system.
|
||||
///
|
||||
/// This structure transitively contains all configuration for the build system.
|
||||
@ -227,7 +243,7 @@ pub struct Build {
|
||||
rustfmt_info: channel::GitInfo,
|
||||
local_rebuild: bool,
|
||||
fail_fast: bool,
|
||||
doc_tests: bool,
|
||||
doc_tests: DocTests,
|
||||
verbosity: usize,
|
||||
|
||||
// Targets for which to build.
|
||||
@ -288,7 +304,7 @@ impl Crate {
|
||||
///
|
||||
/// These entries currently correspond to the various output directories of the
|
||||
/// build system, with each mod generating output in a different directory.
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Mode {
|
||||
/// Build the standard library, placing output in the "stageN-std" directory.
|
||||
Libstd,
|
||||
@ -309,9 +325,8 @@ impl Build {
|
||||
///
|
||||
/// By default all build output will be placed in the current directory.
|
||||
pub fn new(config: Config) -> Build {
|
||||
let cwd = t!(env::current_dir());
|
||||
let src = config.src.clone();
|
||||
let out = cwd.join("build");
|
||||
let out = config.out.clone();
|
||||
|
||||
let is_sudo = match env::var_os("SUDO_USER") {
|
||||
Some(sudo_user) => {
|
||||
@ -327,7 +342,7 @@ impl Build {
|
||||
let rls_info = channel::GitInfo::new(&config, &src.join("src/tools/rls"));
|
||||
let rustfmt_info = channel::GitInfo::new(&config, &src.join("src/tools/rustfmt"));
|
||||
|
||||
Build {
|
||||
let mut build = Build {
|
||||
initial_rustc: config.initial_rustc.clone(),
|
||||
initial_cargo: config.initial_cargo.clone(),
|
||||
local_rebuild: config.local_rebuild,
|
||||
@ -358,7 +373,30 @@ impl Build {
|
||||
delayed_failures: RefCell::new(Vec::new()),
|
||||
prerelease_version: Cell::new(None),
|
||||
tool_artifacts: Default::default(),
|
||||
};
|
||||
|
||||
build.verbose("finding compilers");
|
||||
cc_detect::find(&mut build);
|
||||
build.verbose("running sanity check");
|
||||
sanity::check(&mut build);
|
||||
|
||||
// If local-rust is the same major.minor as the current version, then force a
|
||||
// local-rebuild
|
||||
let local_version_verbose = output(
|
||||
Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
|
||||
let local_release = local_version_verbose
|
||||
.lines().filter(|x| x.starts_with("release:"))
|
||||
.next().unwrap().trim_left_matches("release:").trim();
|
||||
let my_version = channel::CFG_RELEASE_NUM;
|
||||
if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
|
||||
build.verbose(&format!("auto-detected local-rebuild {}", local_release));
|
||||
build.local_rebuild = true;
|
||||
}
|
||||
|
||||
build.verbose("learning about cargo");
|
||||
metadata::build(&mut build);
|
||||
|
||||
build
|
||||
}
|
||||
|
||||
pub fn build_triple(&self) -> &[Interned<String>] {
|
||||
@ -377,25 +415,28 @@ impl Build {
|
||||
return clean::clean(self, all);
|
||||
}
|
||||
|
||||
self.verbose("finding compilers");
|
||||
cc_detect::find(self);
|
||||
self.verbose("running sanity check");
|
||||
sanity::check(self);
|
||||
// If local-rust is the same major.minor as the current version, then force a local-rebuild
|
||||
let local_version_verbose = output(
|
||||
Command::new(&self.initial_rustc).arg("--version").arg("--verbose"));
|
||||
let local_release = local_version_verbose
|
||||
.lines().filter(|x| x.starts_with("release:"))
|
||||
.next().unwrap().trim_left_matches("release:").trim();
|
||||
let my_version = channel::CFG_RELEASE_NUM;
|
||||
if local_release.split('.').take(2).eq(my_version.split('.').take(2)) {
|
||||
self.verbose(&format!("auto-detected local-rebuild {}", local_release));
|
||||
self.local_rebuild = true;
|
||||
{
|
||||
let builder = builder::Builder::new(&self);
|
||||
if let Some(path) = builder.paths.get(0) {
|
||||
if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.verbose("learning about cargo");
|
||||
metadata::build(self);
|
||||
|
||||
builder::Builder::run(&self);
|
||||
if !self.config.dry_run {
|
||||
{
|
||||
self.config.dry_run = true;
|
||||
let builder = builder::Builder::new(&self);
|
||||
builder.execute_cli();
|
||||
}
|
||||
self.config.dry_run = false;
|
||||
let builder = builder::Builder::new(&self);
|
||||
builder.execute_cli();
|
||||
} else {
|
||||
let builder = builder::Builder::new(&self);
|
||||
let _ = builder.execute_cli();
|
||||
}
|
||||
|
||||
// Check for postponed failures from `test --no-fail-fast`.
|
||||
let failures = self.delayed_failures.borrow();
|
||||
@ -586,12 +627,14 @@ impl Build {
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run(&self, cmd: &mut Command) {
|
||||
if self.config.dry_run { return; }
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_silent(cmd)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run_quiet(&self, cmd: &mut Command) {
|
||||
if self.config.dry_run { return; }
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_suppressed(cmd)
|
||||
}
|
||||
@ -600,6 +643,7 @@ impl Build {
|
||||
/// Exits if the command failed to execute at all, otherwise returns its
|
||||
/// `status.success()`.
|
||||
fn try_run(&self, cmd: &mut Command) -> bool {
|
||||
if self.config.dry_run { return true; }
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_silent(cmd)
|
||||
}
|
||||
@ -608,6 +652,7 @@ impl Build {
|
||||
/// Exits if the command failed to execute at all, otherwise returns its
|
||||
/// `status.success()`.
|
||||
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
|
||||
if self.config.dry_run { return true; }
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_suppressed(cmd)
|
||||
}
|
||||
@ -623,6 +668,11 @@ impl Build {
|
||||
}
|
||||
}
|
||||
|
||||
fn info(&self, msg: &str) {
|
||||
if self.config.dry_run { return; }
|
||||
println!("{}", msg);
|
||||
}
|
||||
|
||||
/// Returns the number of parallel jobs that have been configured for this
|
||||
/// build.
|
||||
fn jobs(&self) -> u32 {
|
||||
@ -709,6 +759,12 @@ impl Build {
|
||||
.map(|p| &**p)
|
||||
}
|
||||
|
||||
/// Returns true if this is a no-std `target`, if defined
|
||||
fn no_std(&self, target: Interned<String>) -> Option<bool> {
|
||||
self.config.target_config.get(&target)
|
||||
.map(|t| t.no_std)
|
||||
}
|
||||
|
||||
/// Returns whether the target will be tested using the `remote-test-client`
|
||||
/// and `remote-test-server` binaries.
|
||||
fn remote_tested(&self, target: Interned<String>) -> bool {
|
||||
@ -930,7 +986,7 @@ impl Build {
|
||||
pub fn fold_output<D, F>(&self, name: F) -> Option<OutputFolder>
|
||||
where D: Into<String>, F: FnOnce() -> D
|
||||
{
|
||||
if self.ci_env == CiEnv::Travis {
|
||||
if !self.config.dry_run && self.ci_env == CiEnv::Travis {
|
||||
Some(OutputFolder::new(name().into()))
|
||||
} else {
|
||||
None
|
||||
@ -978,8 +1034,173 @@ impl Build {
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn read_stamp_file(&self, stamp: &Path) -> Vec<PathBuf> {
|
||||
if self.config.dry_run {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut paths = Vec::new();
|
||||
let mut contents = Vec::new();
|
||||
t!(t!(File::open(stamp)).read_to_end(&mut contents));
|
||||
// This is the method we use for extracting paths from the stamp file passed to us. See
|
||||
// run_cargo for more information (in compile.rs).
|
||||
for part in contents.split(|b| *b == 0) {
|
||||
if part.is_empty() {
|
||||
continue
|
||||
}
|
||||
let path = PathBuf::from(t!(str::from_utf8(part)));
|
||||
paths.push(path);
|
||||
}
|
||||
paths
|
||||
}
|
||||
|
||||
/// Copies a file from `src` to `dst`
|
||||
pub fn copy(&self, src: &Path, dst: &Path) {
|
||||
if self.config.dry_run { return; }
|
||||
let _ = fs::remove_file(&dst);
|
||||
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
|
||||
// windows), but if that fails just fall back to a slow `copy` operation.
|
||||
if let Ok(()) = fs::hard_link(src, dst) {
|
||||
return
|
||||
}
|
||||
if let Err(e) = fs::copy(src, dst) {
|
||||
panic!("failed to copy `{}` to `{}`: {}", src.display(),
|
||||
dst.display(), e)
|
||||
}
|
||||
let metadata = t!(src.metadata());
|
||||
t!(fs::set_permissions(dst, metadata.permissions()));
|
||||
let atime = FileTime::from_last_access_time(&metadata);
|
||||
let mtime = FileTime::from_last_modification_time(&metadata);
|
||||
t!(filetime::set_file_times(dst, atime, mtime));
|
||||
}
|
||||
|
||||
/// Search-and-replaces within a file. (Not maximally efficiently: allocates a
|
||||
/// new string for each replacement.)
|
||||
pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) {
|
||||
if self.config.dry_run { return; }
|
||||
let mut contents = String::new();
|
||||
let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
|
||||
t!(file.read_to_string(&mut contents));
|
||||
for &(target, replacement) in replacements {
|
||||
contents = contents.replace(target, replacement);
|
||||
}
|
||||
t!(file.seek(SeekFrom::Start(0)));
|
||||
t!(file.set_len(0));
|
||||
t!(file.write_all(contents.as_bytes()));
|
||||
}
|
||||
|
||||
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
|
||||
/// when this function is called.
|
||||
pub fn cp_r(&self, src: &Path, dst: &Path) {
|
||||
if self.config.dry_run { return; }
|
||||
for f in t!(fs::read_dir(src)) {
|
||||
let f = t!(f);
|
||||
let path = f.path();
|
||||
let name = path.file_name().unwrap();
|
||||
let dst = dst.join(name);
|
||||
if t!(f.file_type()).is_dir() {
|
||||
t!(fs::create_dir_all(&dst));
|
||||
self.cp_r(&path, &dst);
|
||||
} else {
|
||||
let _ = fs::remove_file(&dst);
|
||||
self.copy(&path, &dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
|
||||
/// when this function is called. Unwanted files or directories can be skipped
|
||||
/// by returning `false` from the filter function.
|
||||
pub fn cp_filtered(&self, src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) {
|
||||
// Immediately recurse with an empty relative path
|
||||
self.recurse_(src, dst, Path::new(""), filter)
|
||||
}
|
||||
|
||||
// Inner function does the actual work
|
||||
fn recurse_(&self, src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) {
|
||||
for f in self.read_dir(src) {
|
||||
let path = f.path();
|
||||
let name = path.file_name().unwrap();
|
||||
let dst = dst.join(name);
|
||||
let relative = relative.join(name);
|
||||
// Only copy file or directory if the filter function returns true
|
||||
if filter(&relative) {
|
||||
if t!(f.file_type()).is_dir() {
|
||||
let _ = fs::remove_dir_all(&dst);
|
||||
self.create_dir(&dst);
|
||||
self.recurse_(&path, &dst, &relative, filter);
|
||||
} else {
|
||||
let _ = fs::remove_file(&dst);
|
||||
self.copy(&path, &dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_to_folder(&self, src: &Path, dest_folder: &Path) {
|
||||
let file_name = src.file_name().unwrap();
|
||||
let dest = dest_folder.join(file_name);
|
||||
self.copy(src, &dest);
|
||||
}
|
||||
|
||||
fn install(&self, src: &Path, dstdir: &Path, perms: u32) {
|
||||
if self.config.dry_run { return; }
|
||||
let dst = dstdir.join(src.file_name().unwrap());
|
||||
t!(fs::create_dir_all(dstdir));
|
||||
drop(fs::remove_file(&dst));
|
||||
{
|
||||
let mut s = t!(fs::File::open(&src));
|
||||
let mut d = t!(fs::File::create(&dst));
|
||||
io::copy(&mut s, &mut d).expect("failed to copy");
|
||||
}
|
||||
chmod(&dst, perms);
|
||||
}
|
||||
|
||||
fn create(&self, path: &Path, s: &str) {
|
||||
if self.config.dry_run { return; }
|
||||
t!(fs::write(path, s));
|
||||
}
|
||||
|
||||
fn read(&self, path: &Path) -> String {
|
||||
if self.config.dry_run { return String::new(); }
|
||||
t!(fs::read_to_string(path))
|
||||
}
|
||||
|
||||
fn create_dir(&self, dir: &Path) {
|
||||
if self.config.dry_run { return; }
|
||||
t!(fs::create_dir_all(dir))
|
||||
}
|
||||
|
||||
fn remove_dir(&self, dir: &Path) {
|
||||
if self.config.dry_run { return; }
|
||||
t!(fs::remove_dir_all(dir))
|
||||
}
|
||||
|
||||
fn read_dir(&self, dir: &Path) -> impl Iterator<Item=fs::DirEntry> {
|
||||
let iter = match fs::read_dir(dir) {
|
||||
Ok(v) => v,
|
||||
Err(_) if self.config.dry_run => return vec![].into_iter(),
|
||||
Err(err) => panic!("could not read dir {:?}: {:?}", dir, err),
|
||||
};
|
||||
iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
|
||||
fn remove(&self, f: &Path) {
|
||||
if self.config.dry_run { return; }
|
||||
fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn chmod(path: &Path, perms: u32) {
|
||||
use std::os::unix::fs::*;
|
||||
t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn chmod(_path: &Path, _perms: u32) {}
|
||||
|
||||
|
||||
impl<'a> Compiler {
|
||||
pub fn with_stage(mut self, stage: u32) -> Compiler {
|
||||
self.stage = stage;
|
||||
|
||||
@ -29,7 +29,6 @@ use build_helper::output;
|
||||
use cmake;
|
||||
use cc;
|
||||
|
||||
use Build;
|
||||
use util::{self, exe};
|
||||
use build_helper::up_to_date;
|
||||
use builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
@ -60,39 +59,38 @@ impl Step for Llvm {
|
||||
|
||||
/// Compile LLVM for `target`.
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
let emscripten = self.emscripten;
|
||||
|
||||
// If we're using a custom LLVM bail out here, but we can only use a
|
||||
// custom LLVM for the build triple.
|
||||
if !self.emscripten {
|
||||
if let Some(config) = build.config.target_config.get(&target) {
|
||||
if let Some(config) = builder.config.target_config.get(&target) {
|
||||
if let Some(ref s) = config.llvm_config {
|
||||
check_llvm_version(build, s);
|
||||
check_llvm_version(builder, s);
|
||||
return s.to_path_buf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rebuild_trigger = build.src.join("src/rustllvm/llvm-rebuild-trigger");
|
||||
let rebuild_trigger = builder.src.join("src/rustllvm/llvm-rebuild-trigger");
|
||||
let mut rebuild_trigger_contents = String::new();
|
||||
t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));
|
||||
|
||||
let (out_dir, llvm_config_ret_dir) = if emscripten {
|
||||
let dir = build.emscripten_llvm_out(target);
|
||||
let dir = builder.emscripten_llvm_out(target);
|
||||
let config_dir = dir.join("bin");
|
||||
(dir, config_dir)
|
||||
} else {
|
||||
let mut dir = build.llvm_out(build.config.build);
|
||||
if !build.config.build.contains("msvc") || build.config.ninja {
|
||||
let mut dir = builder.llvm_out(builder.config.build);
|
||||
if !builder.config.build.contains("msvc") || builder.config.ninja {
|
||||
dir.push("build");
|
||||
}
|
||||
(build.llvm_out(target), dir.join("bin"))
|
||||
(builder.llvm_out(target), dir.join("bin"))
|
||||
};
|
||||
let done_stamp = out_dir.join("llvm-finished-building");
|
||||
let build_llvm_config = llvm_config_ret_dir
|
||||
.join(exe("llvm-config", &*build.config.build));
|
||||
.join(exe("llvm-config", &*builder.config.build));
|
||||
if done_stamp.exists() {
|
||||
let mut done_contents = String::new();
|
||||
t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
|
||||
@ -104,17 +102,17 @@ impl Step for Llvm {
|
||||
}
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "llvm");
|
||||
let _folder = builder.fold_output(|| "llvm");
|
||||
let descriptor = if emscripten { "Emscripten " } else { "" };
|
||||
println!("Building {}LLVM for {}", descriptor, target);
|
||||
let _time = util::timeit();
|
||||
builder.info(&format!("Building {}LLVM for {}", descriptor, target));
|
||||
let _time = util::timeit(&builder);
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
|
||||
// http://llvm.org/docs/CMake.html
|
||||
let root = if self.emscripten { "src/llvm-emscripten" } else { "src/llvm" };
|
||||
let mut cfg = cmake::Config::new(build.src.join(root));
|
||||
let mut cfg = cmake::Config::new(builder.src.join(root));
|
||||
|
||||
let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
|
||||
let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
|
||||
(false, _) => "Debug",
|
||||
(true, false) => "Release",
|
||||
(true, true) => "RelWithDebInfo",
|
||||
@ -125,7 +123,7 @@ impl Step for Llvm {
|
||||
let llvm_targets = if self.emscripten {
|
||||
"JSBackend"
|
||||
} else {
|
||||
match build.config.llvm_targets {
|
||||
match builder.config.llvm_targets {
|
||||
Some(ref s) => s,
|
||||
None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;MSP430;Sparc;NVPTX;Hexagon",
|
||||
}
|
||||
@ -134,10 +132,10 @@ impl Step for Llvm {
|
||||
let llvm_exp_targets = if self.emscripten {
|
||||
""
|
||||
} else {
|
||||
&build.config.llvm_experimental_targets[..]
|
||||
&builder.config.llvm_experimental_targets[..]
|
||||
};
|
||||
|
||||
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
||||
let assertions = if builder.config.llvm_assertions {"ON"} else {"OFF"};
|
||||
|
||||
cfg.out_dir(&out_dir)
|
||||
.profile(profile)
|
||||
@ -151,7 +149,8 @@ impl Step for Llvm {
|
||||
.define("WITH_POLLY", "OFF")
|
||||
.define("LLVM_ENABLE_TERMINFO", "OFF")
|
||||
.define("LLVM_ENABLE_LIBEDIT", "OFF")
|
||||
.define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string())
|
||||
.define("LLVM_ENABLE_LIBXML2", "OFF")
|
||||
.define("LLVM_PARALLEL_COMPILE_JOBS", builder.jobs().to_string())
|
||||
.define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
|
||||
.define("LLVM_DEFAULT_TARGET_TRIPLE", target);
|
||||
|
||||
@ -183,22 +182,22 @@ impl Step for Llvm {
|
||||
cfg.define("LLVM_BUILD_32_BITS", "ON");
|
||||
}
|
||||
|
||||
if let Some(num_linkers) = build.config.llvm_link_jobs {
|
||||
if let Some(num_linkers) = builder.config.llvm_link_jobs {
|
||||
if num_linkers > 0 {
|
||||
cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// http://llvm.org/docs/HowToCrossCompileLLVM.html
|
||||
if target != build.build && !emscripten {
|
||||
if target != builder.config.build && !emscripten {
|
||||
builder.ensure(Llvm {
|
||||
target: build.build,
|
||||
target: builder.config.build,
|
||||
emscripten: false,
|
||||
});
|
||||
// FIXME: if the llvm root for the build triple is overridden then we
|
||||
// should use llvm-tblgen from there, also should verify that it
|
||||
// actually exists most of the time in normal installs of LLVM.
|
||||
let host = build.llvm_out(build.build).join("bin/llvm-tblgen");
|
||||
let host = builder.llvm_out(builder.config.build).join("bin/llvm-tblgen");
|
||||
cfg.define("CMAKE_CROSSCOMPILING", "True")
|
||||
.define("LLVM_TABLEGEN", &host);
|
||||
|
||||
@ -208,15 +207,20 @@ impl Step for Llvm {
|
||||
cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
|
||||
}
|
||||
|
||||
cfg.define("LLVM_NATIVE_BUILD", build.llvm_out(build.build).join("build"));
|
||||
cfg.define("LLVM_NATIVE_BUILD", builder.llvm_out(builder.config.build).join("build"));
|
||||
}
|
||||
|
||||
configure_cmake(build, target, &mut cfg, false);
|
||||
configure_cmake(builder, target, &mut cfg, false);
|
||||
|
||||
// FIXME: we don't actually need to build all LLVM tools and all LLVM
|
||||
// libraries here, e.g. we just want a few components and a few
|
||||
// tools. Figure out how to filter them down and only build the right
|
||||
// tools and libs on all platforms.
|
||||
|
||||
if builder.config.dry_run {
|
||||
return build_llvm_config;
|
||||
}
|
||||
|
||||
cfg.build();
|
||||
|
||||
t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
|
||||
@ -225,11 +229,15 @@ impl Step for Llvm {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_llvm_version(build: &Build, llvm_config: &Path) {
|
||||
if !build.config.llvm_version_check {
|
||||
fn check_llvm_version(builder: &Builder, llvm_config: &Path) {
|
||||
if !builder.config.llvm_version_check {
|
||||
return
|
||||
}
|
||||
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(llvm_config);
|
||||
let version = output(cmd.arg("--version"));
|
||||
let mut parts = version.split('.').take(2)
|
||||
@ -242,15 +250,15 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) {
|
||||
panic!("\n\nbad LLVM version: {}, need >=3.9\n\n", version)
|
||||
}
|
||||
|
||||
fn configure_cmake(build: &Build,
|
||||
fn configure_cmake(builder: &Builder,
|
||||
target: Interned<String>,
|
||||
cfg: &mut cmake::Config,
|
||||
building_dist_binaries: bool) {
|
||||
if build.config.ninja {
|
||||
if builder.config.ninja {
|
||||
cfg.generator("Ninja");
|
||||
}
|
||||
cfg.target(&target)
|
||||
.host(&build.config.build);
|
||||
.host(&builder.config.build);
|
||||
|
||||
let sanitize_cc = |cc: &Path| {
|
||||
if target.contains("msvc") {
|
||||
@ -263,29 +271,29 @@ fn configure_cmake(build: &Build,
|
||||
// MSVC with CMake uses msbuild by default which doesn't respect these
|
||||
// vars that we'd otherwise configure. In that case we just skip this
|
||||
// entirely.
|
||||
if target.contains("msvc") && !build.config.ninja {
|
||||
if target.contains("msvc") && !builder.config.ninja {
|
||||
return
|
||||
}
|
||||
|
||||
let cc = build.cc(target);
|
||||
let cxx = build.cxx(target).unwrap();
|
||||
let cc = builder.cc(target);
|
||||
let cxx = builder.cxx(target).unwrap();
|
||||
|
||||
// Handle msvc + ninja + ccache specially (this is what the bots use)
|
||||
if target.contains("msvc") &&
|
||||
build.config.ninja &&
|
||||
build.config.ccache.is_some() {
|
||||
builder.config.ninja &&
|
||||
builder.config.ccache.is_some() {
|
||||
let mut cc = env::current_exe().expect("failed to get cwd");
|
||||
cc.set_file_name("sccache-plus-cl.exe");
|
||||
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(&cc));
|
||||
cfg.env("SCCACHE_PATH",
|
||||
build.config.ccache.as_ref().unwrap())
|
||||
builder.config.ccache.as_ref().unwrap())
|
||||
.env("SCCACHE_TARGET", target);
|
||||
|
||||
// If ccache is configured we inform the build a little differently hwo
|
||||
// to invoke ccache while also invoking our compilers.
|
||||
} else if let Some(ref ccache) = build.config.ccache {
|
||||
} else if let Some(ref ccache) = builder.config.ccache {
|
||||
cfg.define("CMAKE_C_COMPILER", ccache)
|
||||
.define("CMAKE_C_COMPILER_ARG1", sanitize_cc(cc))
|
||||
.define("CMAKE_CXX_COMPILER", ccache)
|
||||
@ -295,16 +303,16 @@ fn configure_cmake(build: &Build,
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(cxx));
|
||||
}
|
||||
|
||||
cfg.build_arg("-j").build_arg(build.jobs().to_string());
|
||||
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
|
||||
let mut cxxflags = build.cflags(target).join(" ");
|
||||
cfg.build_arg("-j").build_arg(builder.jobs().to_string());
|
||||
cfg.define("CMAKE_C_FLAGS", builder.cflags(target).join(" "));
|
||||
let mut cxxflags = builder.cflags(target).join(" ");
|
||||
if building_dist_binaries {
|
||||
if build.config.llvm_static_stdcpp && !target.contains("windows") {
|
||||
if builder.config.llvm_static_stdcpp && !target.contains("windows") {
|
||||
cxxflags.push_str(" -static-libstdc++");
|
||||
}
|
||||
}
|
||||
cfg.define("CMAKE_CXX_FLAGS", cxxflags);
|
||||
if let Some(ar) = build.ar(target) {
|
||||
if let Some(ar) = builder.ar(target) {
|
||||
if ar.is_absolute() {
|
||||
// LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
|
||||
// tries to resolve this path in the LLVM build directory.
|
||||
@ -336,27 +344,29 @@ impl Step for Lld {
|
||||
|
||||
/// Compile LLVM for `target`.
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
if builder.config.dry_run {
|
||||
return PathBuf::from("lld-out-dir-test-gen");
|
||||
}
|
||||
let target = self.target;
|
||||
let build = builder.build;
|
||||
|
||||
let llvm_config = builder.ensure(Llvm {
|
||||
target: self.target,
|
||||
emscripten: false,
|
||||
});
|
||||
|
||||
let out_dir = build.lld_out(target);
|
||||
let out_dir = builder.lld_out(target);
|
||||
let done_stamp = out_dir.join("lld-finished-building");
|
||||
if done_stamp.exists() {
|
||||
return out_dir
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "lld");
|
||||
println!("Building LLD for {}", target);
|
||||
let _time = util::timeit();
|
||||
let _folder = builder.fold_output(|| "lld");
|
||||
builder.info(&format!("Building LLD for {}", target));
|
||||
let _time = util::timeit(&builder);
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
|
||||
let mut cfg = cmake::Config::new(build.src.join("src/tools/lld"));
|
||||
configure_cmake(build, target, &mut cfg, true);
|
||||
let mut cfg = cmake::Config::new(builder.src.join("src/tools/lld"));
|
||||
configure_cmake(builder, target, &mut cfg, true);
|
||||
|
||||
cfg.out_dir(&out_dir)
|
||||
.profile("Release")
|
||||
@ -389,16 +399,18 @@ impl Step for TestHelpers {
|
||||
/// Compiles the `rust_test_helpers.c` library which we used in various
|
||||
/// `run-pass` test suites for ABI testing.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
}
|
||||
let target = self.target;
|
||||
let dst = build.test_helpers_out(target);
|
||||
let src = build.src.join("src/test/auxiliary/rust_test_helpers.c");
|
||||
let dst = builder.test_helpers_out(target);
|
||||
let src = builder.src.join("src/test/auxiliary/rust_test_helpers.c");
|
||||
if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
|
||||
return
|
||||
}
|
||||
|
||||
let _folder = build.fold_output(|| "build_test_helpers");
|
||||
println!("Building test helpers");
|
||||
let _folder = builder.fold_output(|| "build_test_helpers");
|
||||
builder.info(&format!("Building test helpers"));
|
||||
t!(fs::create_dir_all(&dst));
|
||||
let mut cfg = cc::Build::new();
|
||||
|
||||
@ -406,20 +418,20 @@ impl Step for TestHelpers {
|
||||
// extra configuration, so inform gcc of these compilers. Note, though, that
|
||||
// on MSVC we still need gcc's detection of env vars (ugh).
|
||||
if !target.contains("msvc") {
|
||||
if let Some(ar) = build.ar(target) {
|
||||
if let Some(ar) = builder.ar(target) {
|
||||
cfg.archiver(ar);
|
||||
}
|
||||
cfg.compiler(build.cc(target));
|
||||
cfg.compiler(builder.cc(target));
|
||||
}
|
||||
|
||||
cfg.cargo_metadata(false)
|
||||
.out_dir(&dst)
|
||||
.target(&target)
|
||||
.host(&build.build)
|
||||
.host(&builder.config.build)
|
||||
.opt_level(0)
|
||||
.warnings(false)
|
||||
.debug(false)
|
||||
.file(build.src.join("src/test/auxiliary/rust_test_helpers.c"))
|
||||
.file(builder.src.join("src/test/auxiliary/rust_test_helpers.c"))
|
||||
.compile("rust_test_helpers");
|
||||
}
|
||||
}
|
||||
@ -441,9 +453,11 @@ impl Step for Openssl {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
}
|
||||
let target = self.target;
|
||||
let out = match build.openssl_dir(target) {
|
||||
let out = match builder.openssl_dir(target) {
|
||||
Some(dir) => dir,
|
||||
None => return,
|
||||
};
|
||||
@ -479,7 +493,8 @@ impl Step for Openssl {
|
||||
}
|
||||
|
||||
// Ensure the hash is correct.
|
||||
let mut shasum = if target.contains("apple") || build.build.contains("netbsd") {
|
||||
let mut shasum = if target.contains("apple") ||
|
||||
builder.config.build.contains("netbsd") {
|
||||
let mut cmd = Command::new("shasum");
|
||||
cmd.arg("-a").arg("256");
|
||||
cmd
|
||||
@ -512,10 +527,10 @@ impl Step for Openssl {
|
||||
t!(fs::rename(&tmp, &tarball));
|
||||
}
|
||||
let obj = out.join(format!("openssl-{}", OPENSSL_VERS));
|
||||
let dst = build.openssl_install_dir(target).unwrap();
|
||||
let dst = builder.openssl_install_dir(target).unwrap();
|
||||
drop(fs::remove_dir_all(&obj));
|
||||
drop(fs::remove_dir_all(&dst));
|
||||
build.run(Command::new("tar").arg("zxf").arg(&tarball).current_dir(&out));
|
||||
builder.run(Command::new("tar").arg("zxf").arg(&tarball).current_dir(&out));
|
||||
|
||||
let mut configure = Command::new("perl");
|
||||
configure.arg(obj.join("Configure"));
|
||||
@ -565,8 +580,8 @@ impl Step for Openssl {
|
||||
_ => panic!("don't know how to configure OpenSSL for {}", target),
|
||||
};
|
||||
configure.arg(os);
|
||||
configure.env("CC", build.cc(target));
|
||||
for flag in build.cflags(target) {
|
||||
configure.env("CC", builder.cc(target));
|
||||
for flag in builder.cflags(target) {
|
||||
configure.arg(flag);
|
||||
}
|
||||
// There is no specific os target for android aarch64 or x86_64,
|
||||
@ -578,7 +593,7 @@ impl Step for Openssl {
|
||||
if target == "sparc64-unknown-netbsd" {
|
||||
// Need -m64 to get assembly generated correctly for sparc64.
|
||||
configure.arg("-m64");
|
||||
if build.build.contains("netbsd") {
|
||||
if builder.config.build.contains("netbsd") {
|
||||
// Disable sparc64 asm on NetBSD builders, it uses
|
||||
// m4(1)'s -B flag, which NetBSD m4 does not support.
|
||||
configure.arg("no-asm");
|
||||
@ -591,12 +606,12 @@ impl Step for Openssl {
|
||||
configure.arg("no-asm");
|
||||
}
|
||||
configure.current_dir(&obj);
|
||||
println!("Configuring openssl for {}", target);
|
||||
build.run_quiet(&mut configure);
|
||||
println!("Building openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
|
||||
println!("Installing openssl for {}", target);
|
||||
build.run_quiet(Command::new("make").arg("install").arg("-j1").current_dir(&obj));
|
||||
builder.info(&format!("Configuring openssl for {}", target));
|
||||
builder.run_quiet(&mut configure);
|
||||
builder.info(&format!("Building openssl for {}", target));
|
||||
builder.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
|
||||
builder.info(&format!("Installing openssl for {}", target));
|
||||
builder.run_quiet(Command::new("make").arg("install").arg("-j1").current_dir(&obj));
|
||||
|
||||
let mut f = t!(File::create(&stamp));
|
||||
t!(f.write_all(OPENSSL_VERS.as_bytes()));
|
||||
|
||||
@ -140,14 +140,18 @@ pub fn check(build: &mut Build) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd_finder.must_have(build.cc(*target));
|
||||
if let Some(ar) = build.ar(*target) {
|
||||
cmd_finder.must_have(ar);
|
||||
if !build.config.dry_run {
|
||||
cmd_finder.must_have(build.cc(*target));
|
||||
if let Some(ar) = build.ar(*target) {
|
||||
cmd_finder.must_have(ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for host in &build.hosts {
|
||||
cmd_finder.must_have(build.cxx(*host).unwrap());
|
||||
if !build.config.dry_run {
|
||||
cmd_finder.must_have(build.cxx(*host).unwrap());
|
||||
}
|
||||
|
||||
// The msvc hosts don't use jemalloc, turn it off globally to
|
||||
// avoid packaging the dummy liballoc_jemalloc on that platform.
|
||||
@ -169,6 +173,19 @@ pub fn check(build: &mut Build) {
|
||||
panic!("the iOS target is only supported on macOS");
|
||||
}
|
||||
|
||||
if target.contains("-none-") {
|
||||
if build.no_std(*target).is_none() {
|
||||
let target = build.config.target_config.entry(target.clone())
|
||||
.or_insert(Default::default());
|
||||
|
||||
target.no_std = true;
|
||||
}
|
||||
|
||||
if build.no_std(*target) == Some(false) {
|
||||
panic!("All the *-none-* targets are no-std targets")
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure musl-root is valid
|
||||
if target.contains("musl") {
|
||||
// If this is a native target (host is also musl) and no musl-root is given,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,14 +10,14 @@
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::iter;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, exit};
|
||||
use std::slice::SliceConcatExt;
|
||||
|
||||
use Mode;
|
||||
use Compiler;
|
||||
use builder::{Step, RunConfig, ShouldRun, Builder};
|
||||
use util::{copy, exe, add_lib_path};
|
||||
use util::{exe, add_lib_path};
|
||||
use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
|
||||
use native;
|
||||
use channel::GitInfo;
|
||||
@ -39,7 +39,6 @@ impl Step for CleanTools {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let mode = self.mode;
|
||||
@ -47,7 +46,7 @@ impl Step for CleanTools {
|
||||
// This is for the original compiler, but if we're forced to use stage 1, then
|
||||
// std/test/rustc stamps won't exist in stage 2, so we need to get those from stage 1, since
|
||||
// we copy the libs forward.
|
||||
let tools_dir = build.stage_out(compiler, Mode::Tool);
|
||||
let tools_dir = builder.stage_out(compiler, Mode::Tool);
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
@ -56,13 +55,13 @@ impl Step for CleanTools {
|
||||
|
||||
for &cur_mode in &[Mode::Libstd, Mode::Libtest, Mode::Librustc] {
|
||||
let stamp = match cur_mode {
|
||||
Mode::Libstd => libstd_stamp(build, compiler, target),
|
||||
Mode::Libtest => libtest_stamp(build, compiler, target),
|
||||
Mode::Librustc => librustc_stamp(build, compiler, target),
|
||||
Mode::Libstd => libstd_stamp(builder, compiler, target),
|
||||
Mode::Libtest => libtest_stamp(builder, compiler, target),
|
||||
Mode::Librustc => librustc_stamp(builder, compiler, target),
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
if build.clear_if_dirty(&tools_dir, &stamp) {
|
||||
if builder.clear_if_dirty(&tools_dir, &stamp) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -98,7 +97,6 @@ impl Step for ToolBuild {
|
||||
/// This will build the specified tool with the specified `host` compiler in
|
||||
/// `stage` into the normal cargo output directory.
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
let build = builder.build;
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let tool = self.tool;
|
||||
@ -115,10 +113,10 @@ impl Step for ToolBuild {
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
||||
cargo.arg("--features").arg(self.extra_features.join(" "));
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
|
||||
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
|
||||
let _folder = builder.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
|
||||
builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
|
||||
let mut duplicates = Vec::new();
|
||||
let is_expected = compile::stream_cargo(build, &mut cargo, &mut |msg| {
|
||||
let is_expected = compile::stream_cargo(builder, &mut cargo, &mut |msg| {
|
||||
// Only care about big things like the RLS/Cargo for now
|
||||
if tool != "rls" && tool != "cargo" {
|
||||
return
|
||||
@ -157,7 +155,7 @@ impl Step for ToolBuild {
|
||||
}
|
||||
}
|
||||
|
||||
let mut artifacts = build.tool_artifacts.borrow_mut();
|
||||
let mut artifacts = builder.tool_artifacts.borrow_mut();
|
||||
let prev_artifacts = artifacts
|
||||
.entry(target)
|
||||
.or_insert_with(Default::default);
|
||||
@ -191,7 +189,7 @@ impl Step for ToolBuild {
|
||||
panic!("tools should not compile multiple copies of the same crate");
|
||||
}
|
||||
|
||||
build.save_toolstate(tool, if is_expected {
|
||||
builder.save_toolstate(tool, if is_expected {
|
||||
ToolState::TestFail
|
||||
} else {
|
||||
ToolState::BuildFail
|
||||
@ -204,10 +202,10 @@ impl Step for ToolBuild {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
let cargo_out = build.cargo_out(compiler, Mode::Tool, target)
|
||||
let cargo_out = builder.cargo_out(compiler, Mode::Tool, target)
|
||||
.join(exe(tool, &compiler.host));
|
||||
let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host));
|
||||
copy(&cargo_out, &bin);
|
||||
let bin = builder.tools_dir(compiler).join(exe(tool, &compiler.host));
|
||||
builder.copy(&cargo_out, &bin);
|
||||
Some(bin)
|
||||
}
|
||||
}
|
||||
@ -220,16 +218,15 @@ pub fn prepare_tool_cargo(
|
||||
command: &'static str,
|
||||
path: &'static str,
|
||||
) -> Command {
|
||||
let build = builder.build;
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, target, command);
|
||||
let dir = build.src.join(path);
|
||||
let dir = builder.src.join(path);
|
||||
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
|
||||
|
||||
// We don't want to build tools dynamically as they'll be running across
|
||||
// stages and such and it's just easier if they're not dynamically linked.
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
|
||||
if let Some(dir) = build.openssl_install_dir(target) {
|
||||
if let Some(dir) = builder.openssl_install_dir(target) {
|
||||
cargo.env("OPENSSL_STATIC", "1");
|
||||
cargo.env("OPENSSL_DIR", dir);
|
||||
cargo.env("LIBZ_SYS_STATIC", "1");
|
||||
@ -239,10 +236,10 @@ pub fn prepare_tool_cargo(
|
||||
// own copy
|
||||
cargo.env("LZMA_API_STATIC", "1");
|
||||
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel);
|
||||
cargo.env("CFG_VERSION", build.rust_version());
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel);
|
||||
cargo.env("CFG_VERSION", builder.rust_version());
|
||||
|
||||
let info = GitInfo::new(&build.config, &dir);
|
||||
let info = GitInfo::new(&builder.config, &dir);
|
||||
if let Some(sha) = info.sha() {
|
||||
cargo.env("CFG_COMMIT_HASH", sha);
|
||||
}
|
||||
@ -270,8 +267,8 @@ macro_rules! tool {
|
||||
match tool {
|
||||
$(Tool::$name =>
|
||||
self.ensure($name {
|
||||
compiler: self.compiler(stage, self.build.build),
|
||||
target: self.build.build,
|
||||
compiler: self.compiler(stage, self.config.build),
|
||||
target: self.config.build,
|
||||
}),
|
||||
)+
|
||||
}
|
||||
@ -305,7 +302,7 @@ macro_rules! tool {
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
@ -355,7 +352,7 @@ impl Step for RemoteTestServer {
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(RemoteTestServer {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
@ -394,26 +391,25 @@ impl Step for Rustdoc {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> PathBuf {
|
||||
let build = builder.build;
|
||||
let target_compiler = builder.compiler(builder.top_stage, self.host);
|
||||
let target = target_compiler.host;
|
||||
let build_compiler = if target_compiler.stage == 0 {
|
||||
builder.compiler(0, builder.build.build)
|
||||
builder.compiler(0, builder.config.build)
|
||||
} else if target_compiler.stage >= 2 {
|
||||
// Past stage 2, we consider the compiler to be ABI-compatible and hence capable of
|
||||
// building rustdoc itself.
|
||||
builder.compiler(target_compiler.stage, builder.build.build)
|
||||
builder.compiler(target_compiler.stage, builder.config.build)
|
||||
} else {
|
||||
// Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
|
||||
// we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage
|
||||
// compilers, which isn't what we want.
|
||||
builder.compiler(target_compiler.stage - 1, builder.build.build)
|
||||
builder.compiler(target_compiler.stage - 1, builder.config.build)
|
||||
};
|
||||
|
||||
builder.ensure(compile::Rustc { compiler: build_compiler, target });
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: build_compiler,
|
||||
target: builder.build.build,
|
||||
target: builder.config.build,
|
||||
});
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder,
|
||||
@ -426,14 +422,15 @@ impl Step for Rustdoc {
|
||||
cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
|
||||
.env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
|
||||
|
||||
let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
|
||||
println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host);
|
||||
build.run(&mut cargo);
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
|
||||
builder.info(&format!("Building rustdoc for stage{} ({})",
|
||||
target_compiler.stage, target_compiler.host));
|
||||
builder.run(&mut cargo);
|
||||
|
||||
// Cargo adds a number of paths to the dylib search path on windows, which results in
|
||||
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
|
||||
// rustdoc a different name.
|
||||
let tool_rustdoc = build.cargo_out(build_compiler, Mode::Tool, target)
|
||||
let tool_rustdoc = builder.cargo_out(build_compiler, Mode::Tool, target)
|
||||
.join(exe("rustdoc-tool-binary", &target_compiler.host));
|
||||
|
||||
// don't create a stage0-sysroot/bin directory.
|
||||
@ -443,7 +440,7 @@ impl Step for Rustdoc {
|
||||
t!(fs::create_dir_all(&bindir));
|
||||
let bin_rustdoc = bindir.join(exe("rustdoc", &*target_compiler.host));
|
||||
let _ = fs::remove_file(&bin_rustdoc);
|
||||
copy(&tool_rustdoc, &bin_rustdoc);
|
||||
builder.copy(&tool_rustdoc, &bin_rustdoc);
|
||||
bin_rustdoc
|
||||
} else {
|
||||
tool_rustdoc
|
||||
@ -464,12 +461,12 @@ impl Step for Cargo {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/cargo").default_condition(builder.build.config.extended)
|
||||
run.path("src/tools/cargo").default_condition(builder.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Cargo {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
@ -482,7 +479,7 @@ impl Step for Cargo {
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
target: builder.config.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
@ -518,12 +515,12 @@ macro_rules! tool_extended {
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path($path).default_condition(builder.build.config.extended)
|
||||
run.path($path).default_condition(builder.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||
target: run.target,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
@ -554,20 +551,11 @@ tool_extended!((self, builder),
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
target: builder.config.build,
|
||||
});
|
||||
};
|
||||
Miri, miri, "src/tools/miri", "miri", {};
|
||||
Rls, rls, "src/tools/rls", "rls", {
|
||||
let clippy = builder.ensure(Clippy {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
let channel = &builder.config.channel;
|
||||
if clippy.is_some() && channel != "stable" && channel != "beta" {
|
||||
self.extra_features.push("clippy".to_owned());
|
||||
}
|
||||
builder.ensure(native::Openssl {
|
||||
target: self.target,
|
||||
});
|
||||
@ -575,7 +563,7 @@ tool_extended!((self, builder),
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
target: builder.config.build,
|
||||
});
|
||||
};
|
||||
Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {};
|
||||
@ -586,7 +574,7 @@ impl<'a> Builder<'a> {
|
||||
/// `host`.
|
||||
pub fn tool_cmd(&self, tool: Tool) -> Command {
|
||||
let mut cmd = Command::new(self.tool_exe(tool));
|
||||
let compiler = self.compiler(self.tool_default_stage(tool), self.build.build);
|
||||
let compiler = self.compiler(self.tool_default_stage(tool), self.config.build);
|
||||
self.prepare_tool_cmd(compiler, &mut cmd);
|
||||
cmd
|
||||
}
|
||||
@ -597,7 +585,7 @@ impl<'a> Builder<'a> {
|
||||
/// right location to run `compiler`.
|
||||
fn prepare_tool_cmd(&self, compiler: Compiler, cmd: &mut Command) {
|
||||
let host = &compiler.host;
|
||||
let mut paths: Vec<PathBuf> = vec![
|
||||
let mut lib_paths: Vec<PathBuf> = vec![
|
||||
PathBuf::from(&self.sysroot_libdir(compiler, compiler.host)),
|
||||
self.cargo_out(compiler, Mode::Tool, *host).join("deps"),
|
||||
];
|
||||
@ -614,11 +602,46 @@ impl<'a> Builder<'a> {
|
||||
}
|
||||
for path in env::split_paths(v) {
|
||||
if !curpaths.contains(&path) {
|
||||
paths.push(path);
|
||||
lib_paths.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_lib_path(paths, cmd);
|
||||
|
||||
// Add the llvm/bin directory to PATH since it contains lots of
|
||||
// useful, platform-independent tools
|
||||
if let Some(llvm_bin_path) = self.llvm_bin_path() {
|
||||
if host.contains("windows") {
|
||||
// On Windows, PATH and the dynamic library path are the same,
|
||||
// so we just add the LLVM bin path to lib_path
|
||||
lib_paths.push(llvm_bin_path);
|
||||
} else {
|
||||
let old_path = env::var_os("PATH").unwrap_or_default();
|
||||
let new_path = env::join_paths(iter::once(llvm_bin_path)
|
||||
.chain(env::split_paths(&old_path)))
|
||||
.expect("Could not add LLVM bin path to PATH");
|
||||
cmd.env("PATH", new_path);
|
||||
}
|
||||
}
|
||||
|
||||
add_lib_path(lib_paths, cmd);
|
||||
}
|
||||
|
||||
fn llvm_bin_path(&self) -> Option<PathBuf> {
|
||||
if self.config.llvm_enabled && !self.config.dry_run {
|
||||
let llvm_config = self.ensure(native::Llvm {
|
||||
target: self.config.build,
|
||||
emscripten: false,
|
||||
});
|
||||
|
||||
// Add the llvm/bin directory to PATH since it contains lots of
|
||||
// useful, platform-independent tools
|
||||
let llvm_bin_path = llvm_config.parent()
|
||||
.expect("Expected llvm-config to be contained in directory");
|
||||
assert!(llvm_bin_path.is_dir());
|
||||
Some(llvm_bin_path.to_path_buf())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,13 +15,14 @@
|
||||
|
||||
use std::env;
|
||||
use std::str;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, Read, Write, Seek, SeekFrom};
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{SystemTime, Instant};
|
||||
|
||||
use filetime::{self, FileTime};
|
||||
use config::Config;
|
||||
use builder::Builder;
|
||||
|
||||
/// Returns the `name` as the filename of a static library for `target`.
|
||||
pub fn staticlib(name: &str, target: &str) -> String {
|
||||
@ -32,102 +33,6 @@ pub fn staticlib(name: &str, target: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies a file from `src` to `dst`
|
||||
pub fn copy(src: &Path, dst: &Path) {
|
||||
let _ = fs::remove_file(&dst);
|
||||
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
|
||||
// windows), but if that fails just fall back to a slow `copy` operation.
|
||||
if let Ok(()) = fs::hard_link(src, dst) {
|
||||
return
|
||||
}
|
||||
if let Err(e) = fs::copy(src, dst) {
|
||||
panic!("failed to copy `{}` to `{}`: {}", src.display(),
|
||||
dst.display(), e)
|
||||
}
|
||||
let metadata = t!(src.metadata());
|
||||
t!(fs::set_permissions(dst, metadata.permissions()));
|
||||
let atime = FileTime::from_last_access_time(&metadata);
|
||||
let mtime = FileTime::from_last_modification_time(&metadata);
|
||||
t!(filetime::set_file_times(dst, atime, mtime));
|
||||
}
|
||||
|
||||
/// Search-and-replaces within a file. (Not maximally efficiently: allocates a
|
||||
/// new string for each replacement.)
|
||||
pub fn replace_in_file(path: &Path, replacements: &[(&str, &str)]) {
|
||||
let mut contents = String::new();
|
||||
let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
|
||||
t!(file.read_to_string(&mut contents));
|
||||
for &(target, replacement) in replacements {
|
||||
contents = contents.replace(target, replacement);
|
||||
}
|
||||
t!(file.seek(SeekFrom::Start(0)));
|
||||
t!(file.set_len(0));
|
||||
t!(file.write_all(contents.as_bytes()));
|
||||
}
|
||||
|
||||
pub fn read_stamp_file(stamp: &Path) -> Vec<PathBuf> {
|
||||
let mut paths = Vec::new();
|
||||
let mut contents = Vec::new();
|
||||
t!(t!(File::open(stamp)).read_to_end(&mut contents));
|
||||
// This is the method we use for extracting paths from the stamp file passed to us. See
|
||||
// run_cargo for more information (in compile.rs).
|
||||
for part in contents.split(|b| *b == 0) {
|
||||
if part.is_empty() {
|
||||
continue
|
||||
}
|
||||
let path = PathBuf::from(t!(str::from_utf8(part)));
|
||||
paths.push(path);
|
||||
}
|
||||
paths
|
||||
}
|
||||
|
||||
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
|
||||
/// when this function is called.
|
||||
pub fn cp_r(src: &Path, dst: &Path) {
|
||||
for f in t!(fs::read_dir(src)) {
|
||||
let f = t!(f);
|
||||
let path = f.path();
|
||||
let name = path.file_name().unwrap();
|
||||
let dst = dst.join(name);
|
||||
if t!(f.file_type()).is_dir() {
|
||||
t!(fs::create_dir_all(&dst));
|
||||
cp_r(&path, &dst);
|
||||
} else {
|
||||
let _ = fs::remove_file(&dst);
|
||||
copy(&path, &dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
|
||||
/// when this function is called. Unwanted files or directories can be skipped
|
||||
/// by returning `false` from the filter function.
|
||||
pub fn cp_filtered(src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) {
|
||||
// Inner function does the actual work
|
||||
fn recurse(src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) {
|
||||
for f in t!(fs::read_dir(src)) {
|
||||
let f = t!(f);
|
||||
let path = f.path();
|
||||
let name = path.file_name().unwrap();
|
||||
let dst = dst.join(name);
|
||||
let relative = relative.join(name);
|
||||
// Only copy file or directory if the filter function returns true
|
||||
if filter(&relative) {
|
||||
if t!(f.file_type()).is_dir() {
|
||||
let _ = fs::remove_dir_all(&dst);
|
||||
t!(fs::create_dir(&dst));
|
||||
recurse(&path, &dst, &relative, filter);
|
||||
} else {
|
||||
let _ = fs::remove_file(&dst);
|
||||
copy(&path, &dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Immediately recurse with an empty relative path
|
||||
recurse(src, dst, Path::new(""), filter)
|
||||
}
|
||||
|
||||
/// Given an executable called `name`, return the filename for the
|
||||
/// executable for a particular target.
|
||||
pub fn exe(name: &str, target: &str) -> String {
|
||||
@ -196,25 +101,28 @@ pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf {
|
||||
buf
|
||||
}
|
||||
|
||||
pub struct TimeIt(Instant);
|
||||
pub struct TimeIt(bool, Instant);
|
||||
|
||||
/// Returns an RAII structure that prints out how long it took to drop.
|
||||
pub fn timeit() -> TimeIt {
|
||||
TimeIt(Instant::now())
|
||||
pub fn timeit(builder: &Builder) -> TimeIt {
|
||||
TimeIt(builder.config.dry_run, Instant::now())
|
||||
}
|
||||
|
||||
impl Drop for TimeIt {
|
||||
fn drop(&mut self) {
|
||||
let time = self.0.elapsed();
|
||||
println!("\tfinished in {}.{:03}",
|
||||
time.as_secs(),
|
||||
time.subsec_nanos() / 1_000_000);
|
||||
let time = self.1.elapsed();
|
||||
if !self.0 {
|
||||
println!("\tfinished in {}.{:03}",
|
||||
time.as_secs(),
|
||||
time.subsec_nanos() / 1_000_000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Symlinks two directories, using junctions on Windows and normal symlinks on
|
||||
/// Unix.
|
||||
pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
|
||||
pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
|
||||
if config.dry_run { return Ok(()); }
|
||||
let _ = fs::remove_dir(dest);
|
||||
return symlink_dir_inner(src, dest);
|
||||
|
||||
|
||||
@ -8,13 +8,11 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{fs, env};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::{env, fs};
|
||||
|
||||
/// A helper macro to `unwrap` a result except also print out details like:
|
||||
///
|
||||
@ -26,10 +24,12 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
/// using a `Result` with `try!`, but this may change one day...
|
||||
#[macro_export]
|
||||
macro_rules! t {
|
||||
($e:expr) => (match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {}", stringify!($e), e),
|
||||
})
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn run(cmd: &mut Command) {
|
||||
@ -46,14 +46,17 @@ pub fn run_silent(cmd: &mut Command) {
|
||||
pub fn try_run_silent(cmd: &mut Command) -> bool {
|
||||
let status = match cmd.status() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
Err(e) => fail(&format!(
|
||||
"failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e
|
||||
)),
|
||||
};
|
||||
if !status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status);
|
||||
println!(
|
||||
"\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd, status
|
||||
);
|
||||
}
|
||||
status.success()
|
||||
}
|
||||
@ -67,18 +70,22 @@ pub fn run_suppressed(cmd: &mut Command) {
|
||||
pub fn try_run_suppressed(cmd: &mut Command) -> bool {
|
||||
let output = match cmd.output() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
Err(e) => fail(&format!(
|
||||
"failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e
|
||||
)),
|
||||
};
|
||||
if !output.status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n\
|
||||
stdout ----\n{}\n\
|
||||
stderr ----\n{}\n\n",
|
||||
cmd,
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr));
|
||||
println!(
|
||||
"\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n\
|
||||
stdout ----\n{}\n\
|
||||
stderr ----\n{}\n\n",
|
||||
cmd,
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
output.status.success()
|
||||
}
|
||||
@ -94,9 +101,9 @@ pub fn gnu_target(target: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn make(host: &str) -> PathBuf {
|
||||
if host.contains("bitrig") || host.contains("dragonfly") ||
|
||||
host.contains("freebsd") || host.contains("netbsd") ||
|
||||
host.contains("openbsd") {
|
||||
if host.contains("bitrig") || host.contains("dragonfly") || host.contains("freebsd")
|
||||
|| host.contains("netbsd") || host.contains("openbsd")
|
||||
{
|
||||
PathBuf::from("gmake")
|
||||
} else {
|
||||
PathBuf::from("make")
|
||||
@ -106,23 +113,27 @@ pub fn make(host: &str) -> PathBuf {
|
||||
pub fn output(cmd: &mut Command) -> String {
|
||||
let output = match cmd.stderr(Stdio::inherit()).output() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
Err(e) => fail(&format!(
|
||||
"failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e
|
||||
)),
|
||||
};
|
||||
if !output.status.success() {
|
||||
panic!("command did not execute successfully: {:?}\n\
|
||||
expected success, got: {}",
|
||||
cmd,
|
||||
output.status);
|
||||
panic!(
|
||||
"command did not execute successfully: {:?}\n\
|
||||
expected success, got: {}",
|
||||
cmd, output.status
|
||||
);
|
||||
}
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
|
||||
let mut stack = dir.read_dir().unwrap()
|
||||
.map(|e| e.unwrap())
|
||||
.filter(|e| &*e.file_name() != ".git")
|
||||
.collect::<Vec<_>>();
|
||||
let mut stack = dir.read_dir()
|
||||
.unwrap()
|
||||
.map(|e| e.unwrap())
|
||||
.filter(|e| &*e.file_name() != ".git")
|
||||
.collect::<Vec<_>>();
|
||||
while let Some(entry) = stack.pop() {
|
||||
let path = entry.path();
|
||||
if entry.file_type().unwrap().is_dir() {
|
||||
@ -135,7 +146,9 @@ pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
|
||||
|
||||
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
|
||||
pub fn mtime(path: &Path) -> SystemTime {
|
||||
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
|
||||
fs::metadata(path)
|
||||
.and_then(|f| f.modified())
|
||||
.unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// Returns whether `dst` is up to date given that the file or files in `src`
|
||||
@ -176,11 +189,12 @@ impl Drop for NativeLibBoilerplate {
|
||||
// If Err is returned, then everything is up-to-date and further build actions can be skipped.
|
||||
// Timestamps are created automatically when the result of `native_lib_boilerplate` goes out
|
||||
// of scope, so all the build actions should be completed until then.
|
||||
pub fn native_lib_boilerplate(src_name: &str,
|
||||
out_name: &str,
|
||||
link_name: &str,
|
||||
search_subdir: &str)
|
||||
-> Result<NativeLibBoilerplate, ()> {
|
||||
pub fn native_lib_boilerplate(
|
||||
src_name: &str,
|
||||
out_name: &str,
|
||||
link_name: &str,
|
||||
search_subdir: &str,
|
||||
) -> Result<NativeLibBoilerplate, ()> {
|
||||
let current_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let src_dir = current_dir.join("..").join(src_name);
|
||||
rerun_if_changed_anything_in_dir(&src_dir);
|
||||
@ -193,11 +207,17 @@ pub fn native_lib_boilerplate(src_name: &str,
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=static={}", link_name);
|
||||
}
|
||||
println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display());
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
out_dir.join(search_subdir).display()
|
||||
);
|
||||
|
||||
let timestamp = out_dir.join("rustbuild.timestamp");
|
||||
if !up_to_date(Path::new("build.rs"), ×tamp) || !up_to_date(&src_dir, ×tamp) {
|
||||
Ok(NativeLibBoilerplate { src_dir: src_dir, out_dir: out_dir })
|
||||
Ok(NativeLibBoilerplate {
|
||||
src_dir: src_dir,
|
||||
out_dir: out_dir,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
@ -215,10 +235,12 @@ pub fn sanitizer_lib_boilerplate(sanitizer_name: &str) -> Result<NativeLibBoiler
|
||||
),
|
||||
_ => return Err(()),
|
||||
};
|
||||
native_lib_boilerplate("libcompiler_builtins/compiler-rt",
|
||||
sanitizer_name,
|
||||
&link_name,
|
||||
search_path)
|
||||
native_lib_boilerplate(
|
||||
"libcompiler_builtins/compiler-rt",
|
||||
sanitizer_name,
|
||||
&link_name,
|
||||
search_path,
|
||||
)
|
||||
}
|
||||
|
||||
fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
|
||||
|
||||
@ -20,7 +20,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bzip2 \
|
||||
patch \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
pkg-config \
|
||||
gcc-arm-none-eabi \
|
||||
libnewlib-arm-none-eabi
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@ -41,6 +43,10 @@ ENV STAGING_DIR=/tmp
|
||||
|
||||
COPY scripts/musl.sh /build
|
||||
RUN env \
|
||||
CC=arm-linux-gnueabi-gcc CFLAGS="-march=armv5te -marm -mfloat-abi=soft" \
|
||||
CXX=arm-linux-gnueabi-g++ CXXFLAGS="-march=armv5te -marm -mfloat-abi=soft" \
|
||||
bash musl.sh armv5te && \
|
||||
env \
|
||||
CC=arm-linux-gnueabi-gcc CFLAGS="-march=armv6 -marm" \
|
||||
CXX=arm-linux-gnueabi-g++ CXXFLAGS="-march=armv6 -marm" \
|
||||
bash musl.sh arm && \
|
||||
@ -66,6 +72,14 @@ RUN env \
|
||||
bash musl.sh mipsel && \
|
||||
rm -rf /build/*
|
||||
|
||||
# FIXME(mozilla/sccache#235) this shouldn't be necessary but is currently
|
||||
# necessary to disambiguate the mips compiler with the mipsel compiler. We want
|
||||
# to give these two wrapper scripts (currently identical ones) different hashes
|
||||
# to ensure that sccache understands that they're different compilers.
|
||||
RUN \
|
||||
echo "# a" >> /usr/local/mips-linux-musl/bin/mips-openwrt-linux-musl-wrapper.sh && \
|
||||
echo "# b" >> /usr/local/mipsel-linux-musl/bin/mipsel-openwrt-linux-musl-wrapper.sh
|
||||
|
||||
ENV TARGETS=asmjs-unknown-emscripten
|
||||
ENV TARGETS=$TARGETS,wasm32-unknown-emscripten
|
||||
ENV TARGETS=$TARGETS,x86_64-rumprun-netbsd
|
||||
@ -74,10 +88,15 @@ ENV TARGETS=$TARGETS,mipsel-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi
|
||||
ENV TARGETS=$TARGETS,armv5te-unknown-linux-musleabi
|
||||
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-redox
|
||||
ENV TARGETS=$TARGETS,thumbv6m-none-eabi
|
||||
ENV TARGETS=$TARGETS,thumbv7m-none-eabi
|
||||
ENV TARGETS=$TARGETS,thumbv7em-none-eabi
|
||||
ENV TARGETS=$TARGETS,thumbv7em-none-eabihf
|
||||
|
||||
# FIXME: remove armv5te vars after https://github.com/alexcrichton/cc-rs/issues/271
|
||||
# get fixed and cc update
|
||||
@ -86,9 +105,12 @@ ENV CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \
|
||||
CC_sparc64_unknown_linux_gnu=sparc64-linux-gnu-gcc \
|
||||
CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc \
|
||||
CC_armv5te_unknown_linux_gnueabi=arm-linux-gnueabi-gcc \
|
||||
CFLAGS_armv5te_unknown_linux_gnueabi="-march=armv5te -marm -mfloat-abi=soft"
|
||||
CFLAGS_armv5te_unknown_linux_gnueabi="-march=armv5te -marm -mfloat-abi=soft" \
|
||||
CC_armv5te_unknown_linux_musleabi=arm-linux-gnueabi-gcc \
|
||||
CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft"
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--musl-root-armv5te=/musl-armv5te \
|
||||
--musl-root-arm=/musl-arm \
|
||||
--musl-root-armhf=/musl-armhf \
|
||||
--musl-root-armv7=/musl-armv7 \
|
||||
|
||||
23
src/ci/docker/mingw-check/Dockerfile
Normal file
23
src/ci/docker/mingw-check/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ \
|
||||
make \
|
||||
file \
|
||||
curl \
|
||||
ca-certificates \
|
||||
python2.7 \
|
||||
git \
|
||||
cmake \
|
||||
sudo \
|
||||
gdb \
|
||||
xz-utils \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
mingw-w64
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
|
||||
ENV SCRIPT python2.7 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
|
||||
@ -104,8 +104,6 @@ if [ "$SCCACHE_BUCKET" != "" ]; then
|
||||
args="$args --env SCCACHE_REGION"
|
||||
args="$args --env AWS_ACCESS_KEY_ID"
|
||||
args="$args --env AWS_SECRET_ACCESS_KEY"
|
||||
args="$args --env SCCACHE_ERROR_LOG=/tmp/sccache/sccache.log"
|
||||
args="$args --volume $objdir/tmp:/tmp/sccache"
|
||||
else
|
||||
mkdir -p $HOME/.cache/sccache
|
||||
args="$args --env SCCACHE_DIR=/sccache --volume $HOME/.cache/sccache:/sccache"
|
||||
@ -132,6 +130,7 @@ exec docker \
|
||||
--env TRAVIS \
|
||||
--env TRAVIS_BRANCH \
|
||||
--env TOOLSTATE_REPO_ACCESS_TOKEN \
|
||||
--env CI_JOB_NAME="${CI_JOB_NAME-$IMAGE}" \
|
||||
--volume "$HOME/.cargo:/cargo" \
|
||||
--volume "$HOME/rustsrc:$HOME/rustsrc" \
|
||||
--init \
|
||||
|
||||
@ -40,7 +40,7 @@ if [ ! -d $MUSL ]; then
|
||||
fi
|
||||
|
||||
cd $MUSL
|
||||
./configure --disable-shared --prefix=/musl-$TAG $@
|
||||
./configure --enable-optimize --enable-debug --disable-shared --prefix=/musl-$TAG $@
|
||||
if [ "$TAG" = "i586" -o "$TAG" = "i686" ]; then
|
||||
hide_output make -j$(nproc) AR=ar RANLIB=ranlib
|
||||
else
|
||||
|
||||
@ -13,6 +13,6 @@
|
||||
set -ex
|
||||
|
||||
curl -fo /usr/local/bin/sccache \
|
||||
https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-05-12-sccache-x86_64-unknown-linux-musl
|
||||
https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2018-04-02-sccache-x86_64-unknown-linux-musl
|
||||
|
||||
chmod +x /usr/local/bin/sccache
|
||||
|
||||
@ -25,6 +25,12 @@ ENV RUST_CONFIGURE_ARGS \
|
||||
--set build.nodejs=/node-v9.2.0-linux-x64/bin/node \
|
||||
--set rust.lld
|
||||
|
||||
# Some run-make tests have assertions about code size, and enabling debug
|
||||
# assertions in libstd causes the binary to be much bigger than it would
|
||||
# otherwise normally be. We already test libstd with debug assertions in lots of
|
||||
# other contexts as well
|
||||
ENV NO_DEBUG_ASSERTIONS=1
|
||||
|
||||
ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
|
||||
src/test/run-make \
|
||||
src/test/ui \
|
||||
@ -34,4 +40,3 @@ ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
|
||||
src/test/mir-opt \
|
||||
src/test/codegen-units \
|
||||
src/libcore \
|
||||
src/libstd_unicode/ \
|
||||
|
||||
@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--enable-debug \
|
||||
|
||||
@ -30,8 +30,7 @@ python2.7 "$X_PY" test --no-fail-fast \
|
||||
src/doc/reference \
|
||||
src/doc/rust-by-example \
|
||||
src/tools/rls \
|
||||
src/tools/rustfmt \
|
||||
src/tools/clippy
|
||||
src/tools/rustfmt
|
||||
set -e
|
||||
|
||||
cat "$TOOLSTATE_FILE"
|
||||
@ -65,8 +64,6 @@ verify_status reference src/doc/reference
|
||||
verify_status rust-by-example src/doc/rust-by-example
|
||||
verify_status rls src/tool/rls
|
||||
verify_status rustfmt src/tool/rustfmt
|
||||
verify_status clippy-driver src/tool/clippy
|
||||
#verify_status miri src/tool/miri
|
||||
|
||||
if [ "$RUST_RELEASE_CHANNEL" = nightly -a -n "${TOOLSTATE_REPO_ACCESS_TOKEN+is_set}" ]; then
|
||||
. "$(dirname $0)/repo.sh"
|
||||
|
||||
@ -11,6 +11,10 @@
|
||||
|
||||
set -e
|
||||
|
||||
if [ -n "$CI_JOB_NAME" ]; then
|
||||
echo "[CI_JOB_NAME=$CI_JOB_NAME]"
|
||||
fi
|
||||
|
||||
if [ "$NO_CHANGE_USER" = "" ]; then
|
||||
if [ "$LOCAL_USER_ID" != "" ]; then
|
||||
useradd --shell /bin/bash -u $LOCAL_USER_ID -o -c "" -m user
|
||||
@ -74,9 +78,9 @@ fi
|
||||
# sccache server at the start of the build, but no need to worry if this fails.
|
||||
SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true
|
||||
|
||||
if [ "$PARALLEL_CHECK" != "" ]; then
|
||||
if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
|
||||
$SRC/configure --enable-experimental-parallel-queries
|
||||
python2.7 ../x.py check
|
||||
CARGO_INCREMENTAL=0 python2.7 ../x.py check
|
||||
rm -f config.toml
|
||||
rm -rf build
|
||||
fi
|
||||
|
||||
@ -18,3 +18,4 @@ opt-level = 'z'
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
allocator-api = []
|
||||
|
||||
@ -100,24 +100,24 @@ impl Drop for Instance {
|
||||
|
||||
unsafe impl<'a> Alloc for &'a GlobalDlmalloc {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
get().alloc(layout)
|
||||
Alloc::alloc(&mut *get(), layout)
|
||||
}
|
||||
|
||||
unsafe fn alloc_zeroed(&mut self, layout: Layout)
|
||||
-> Result<*mut u8, AllocErr>
|
||||
{
|
||||
get().alloc_zeroed(layout)
|
||||
Alloc::alloc_zeroed(&mut *get(), layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
get().dealloc(ptr, layout)
|
||||
Alloc::dealloc(&mut *get(), ptr, layout)
|
||||
}
|
||||
|
||||
unsafe fn realloc(&mut self,
|
||||
ptr: *mut u8,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
get().realloc(ptr, old_layout, new_layout)
|
||||
Alloc::realloc(&mut *get(), ptr, old_layout, new_layout)
|
||||
}
|
||||
|
||||
// fn oom(&mut self, err: AllocErr) -> ! {
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
#![feature(allocator_api, alloc)]
|
||||
#![cfg_attr(feature = "allocator-api", feature(allocator_api, alloc))]
|
||||
#![cfg_attr(target_arch = "wasm32", feature(link_llvm_intrinsics))]
|
||||
#![cfg_attr(not(feature = "allocator-api"), allow(dead_code))]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "allocator-api")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "allocator-api")]
|
||||
use alloc::heap::{Alloc, Layout, AllocErr};
|
||||
use core::cmp;
|
||||
use core::ptr;
|
||||
|
||||
#[cfg(feature = "allocator-api")]
|
||||
pub use self::global::GlobalDlmalloc;
|
||||
|
||||
#[cfg(feature = "allocator-api")]
|
||||
mod global;
|
||||
mod dlmalloc;
|
||||
|
||||
pub struct Dlmalloc(dlmalloc::Dlmalloc);
|
||||
|
||||
pub const DLMALLOC_INIT: Dlmalloc = Dlmalloc(dlmalloc::DLMALLOC_INIT);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[path = "wasm.rs"]
|
||||
mod sys;
|
||||
@ -31,16 +38,56 @@ impl Dlmalloc {
|
||||
pub fn new() -> Dlmalloc {
|
||||
Dlmalloc(dlmalloc::Dlmalloc::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn malloc(&mut self, size: usize, align: usize) -> *mut u8 {
|
||||
if align <= self.0.malloc_alignment() {
|
||||
self.0.malloc(size)
|
||||
} else {
|
||||
self.0.memalign(align, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn calloc(&mut self, size: usize, align: usize) -> *mut u8 {
|
||||
let ptr = self.malloc(size, align);
|
||||
if !ptr.is_null() && self.0.calloc_must_clear(ptr) {
|
||||
ptr::write_bytes(ptr, 0, size);
|
||||
}
|
||||
ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn free(&mut self, ptr: *mut u8, size: usize, align: usize) {
|
||||
drop((size, align));
|
||||
self.0.free(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn realloc(&mut self,
|
||||
ptr: *mut u8,
|
||||
old_size: usize,
|
||||
old_align: usize,
|
||||
new_size: usize) -> *mut u8 {
|
||||
if old_align <= self.0.malloc_alignment() {
|
||||
self.0.realloc(ptr, new_size)
|
||||
} else {
|
||||
let res = self.malloc(new_size, old_align);
|
||||
if !res.is_null() {
|
||||
let size = cmp::min(old_size, new_size);
|
||||
ptr::copy_nonoverlapping(ptr, res, size);
|
||||
self.free(ptr, old_size, old_align);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "allocator-api")]
|
||||
unsafe impl Alloc for Dlmalloc {
|
||||
#[inline]
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
let ptr = if layout.align() <= self.0.malloc_alignment() {
|
||||
self.0.malloc(layout.size())
|
||||
} else {
|
||||
self.0.memalign(layout.align(), layout.size())
|
||||
};
|
||||
let ptr = <Dlmalloc>::malloc(self, layout.size(), layout.align());
|
||||
if ptr.is_null() {
|
||||
Err(AllocErr::Exhausted { request: layout })
|
||||
} else {
|
||||
@ -52,18 +99,17 @@ unsafe impl Alloc for Dlmalloc {
|
||||
unsafe fn alloc_zeroed(&mut self, layout: Layout)
|
||||
-> Result<*mut u8, AllocErr>
|
||||
{
|
||||
let size = layout.size();
|
||||
let ptr = self.alloc(layout)?;
|
||||
if self.0.calloc_must_clear(ptr) {
|
||||
ptr::write_bytes(ptr, 0, size);
|
||||
let ptr = <Dlmalloc>::calloc(self, layout.size(), layout.align());
|
||||
if ptr.is_null() {
|
||||
Err(AllocErr::Exhausted { request: layout })
|
||||
} else {
|
||||
Ok(ptr)
|
||||
}
|
||||
Ok(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
drop(layout);
|
||||
self.0.free(ptr)
|
||||
<Dlmalloc>::free(self, ptr, layout.size(), layout.align())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -76,91 +122,19 @@ unsafe impl Alloc for Dlmalloc {
|
||||
details: "cannot change alignment on `realloc`",
|
||||
})
|
||||
}
|
||||
let ptr = <Dlmalloc>::realloc(
|
||||
self,
|
||||
ptr,
|
||||
old_layout.size(),
|
||||
old_layout.align(),
|
||||
new_layout.size(),
|
||||
);
|
||||
|
||||
if new_layout.align() <= self.0.malloc_alignment() {
|
||||
let ptr = self.0.realloc(ptr, new_layout.size());
|
||||
if !ptr.is_null() {
|
||||
Ok(ptr as *mut u8)
|
||||
} else {
|
||||
Err(AllocErr::Exhausted { request: new_layout })
|
||||
}
|
||||
|
||||
if ptr.is_null() {
|
||||
Err(AllocErr::Exhausted { request: new_layout })
|
||||
} else {
|
||||
let res = self.alloc(new_layout.clone());
|
||||
if let Ok(new_ptr) = res {
|
||||
let size = cmp::min(old_layout.size(), new_layout.size());
|
||||
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||
self.dealloc(ptr, old_layout);
|
||||
}
|
||||
res
|
||||
Ok(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// fn oom(&mut self, err: AllocErr) -> ! {
|
||||
// System.oom(err)
|
||||
// }
|
||||
|
||||
// #[inline]
|
||||
// fn usable_size(&self, layout: &Layout) -> (usize, usize) {
|
||||
// (&self).usable_size(layout)
|
||||
// }
|
||||
//
|
||||
// #[inline]
|
||||
// unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
|
||||
// (&*self).alloc_excess(layout)
|
||||
// }
|
||||
//
|
||||
// #[inline]
|
||||
// unsafe fn realloc_excess(&mut self,
|
||||
// ptr: *mut u8,
|
||||
// layout: Layout,
|
||||
// new_layout: Layout) -> Result<Excess, AllocErr> {
|
||||
// (&*self).realloc_excess(ptr, layout, new_layout)
|
||||
// }
|
||||
//
|
||||
// #[inline]
|
||||
// unsafe fn grow_in_place(&mut self,
|
||||
// ptr: *mut u8,
|
||||
// layout: Layout,
|
||||
// new_layout: Layout) -> Result<(), CannotReallocInPlace> {
|
||||
// (&*self).grow_in_place(ptr, layout, new_layout)
|
||||
// }
|
||||
//
|
||||
// #[inline]
|
||||
// unsafe fn shrink_in_place(&mut self,
|
||||
// ptr: *mut u8,
|
||||
// layout: Layout,
|
||||
// new_layout: Layout) -> Result<(), CannotReallocInPlace> {
|
||||
// (&*self).shrink_in_place(ptr, layout, new_layout)
|
||||
// }
|
||||
}
|
||||
|
||||
// unsafe impl<'a> Alloc for &'a Dlmalloc {
|
||||
// #[inline]
|
||||
// unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
// panic!()
|
||||
// }
|
||||
//
|
||||
// // #[inline]
|
||||
// // unsafe fn alloc_zeroed(&mut self, layout: Layout)
|
||||
// // -> Result<*mut u8, AllocErr>
|
||||
// // {
|
||||
// // panic!()
|
||||
// // }
|
||||
//
|
||||
// #[inline]
|
||||
// unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
// panic!()
|
||||
// }
|
||||
//
|
||||
// // #[inline]
|
||||
// // unsafe fn realloc(&mut self,
|
||||
// // ptr: *mut u8,
|
||||
// // old_layout: Layout,
|
||||
// // new_layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
// // panic!()
|
||||
// // }
|
||||
//
|
||||
// fn oom(&mut self, err: AllocErr) -> ! {
|
||||
// System.oom(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -59,7 +59,7 @@ fn stress() {
|
||||
for i in 0..cmp::min(old.size(), new.size()) {
|
||||
tmp.push(*ptr.offset(i as isize));
|
||||
}
|
||||
let ptr = a.realloc(ptr, old, new.clone()).unwrap_or_else(|e| {
|
||||
let ptr = Alloc::realloc(&mut a, ptr, old, new.clone()).unwrap_or_else(|e| {
|
||||
System.oom(e)
|
||||
});
|
||||
for (i, byte) in tmp.iter().enumerate() {
|
||||
|
||||
15
src/doc/book/.github/ISSUE_TEMPLATE.md
vendored
15
src/doc/book/.github/ISSUE_TEMPLATE.md
vendored
@ -1,12 +1,11 @@
|
||||
## What to expect when you file an issue here
|
||||
|
||||
### First edition
|
||||
Thank you for caring about the quality of the book! Each edition has
|
||||
different types of issues we can accept, please read on for details.
|
||||
|
||||
The first edition of the book is no longer being actively worked on.
|
||||
### 2018 edition
|
||||
|
||||
Issues for the first edition of the book are worthwhile if you are planning to
|
||||
submit a pull request, and want to discuss it first. But as we aren't actively
|
||||
working on the first edition, general bugs will be closed.
|
||||
This version of the book is under development, please file issues liberally!
|
||||
|
||||
### Second edition
|
||||
|
||||
@ -23,4 +22,10 @@ to see which chapters have been frozen.
|
||||
|
||||
Please see CONTRIBUTING.md for more details.
|
||||
|
||||
### First edition
|
||||
|
||||
The first edition of the book is frozen, and bugs filed against it will
|
||||
be closed.
|
||||
|
||||
|
||||
Thank you for reading, you may now delete this text!
|
||||
|
||||
17
src/doc/book/.github/PULL_REQUEST_TEMPLATE.md
vendored
17
src/doc/book/.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,10 +1,13 @@
|
||||
## What to expect when you open a pull request here
|
||||
|
||||
### First edition
|
||||
### 2018 Edition
|
||||
|
||||
The first edition is no longer being actively worked on. We accept pull
|
||||
requests for the first edition, but prefer small tweaks to large changes, as
|
||||
larger work should be spent improving the second edition.
|
||||
The 2018 is a "living" edition; it's not scheduled for in-print publication at
|
||||
this time, and so is able to be updated at any time. We'd love pull requests to
|
||||
fix issues with this edition, but we're not interested in extremely large
|
||||
changes without discussing them first. If you'd like to make a big change,
|
||||
please open an issue first! We'd hate for you to do some hard work that we
|
||||
ultimately wouldn't accept.
|
||||
|
||||
### Second edition
|
||||
|
||||
@ -21,4 +24,10 @@ to see which chapters have been frozen.
|
||||
|
||||
Please see CONTRIBUTING.md for more details.
|
||||
|
||||
### First edition
|
||||
|
||||
The first edition is frozen, and no longer accepting changes. Pull requests
|
||||
made against it will be closed.
|
||||
|
||||
|
||||
Thank you for reading, you may now delete this text!
|
||||
|
||||
@ -13,6 +13,6 @@ addons:
|
||||
- aspell
|
||||
- aspell-en
|
||||
before_script:
|
||||
- (cargo install mdbook --vers 0.1.2 --force || true)
|
||||
- (cargo install mdbook --vers 0.1.5 --force || true)
|
||||
script:
|
||||
- bash ci/build.sh
|
||||
|
||||
142
src/doc/book/2018-edition/Cargo.lock
generated
Normal file
142
src/doc/book/2018-edition/Cargo.lock
generated
Normal file
@ -0,0 +1,142 @@
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "0.6.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rust-book"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7ef30445607f6fc8720f0a0a2c7442284b629cf0d049286860fae23e71c4d9"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "236eb37a62591d4a41a89b7763d7de3e06ca02d5ab2815446a8bae5d2f8c2d57"
|
||||
"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
36
src/doc/book/2018-edition/Cargo.toml
Normal file
36
src/doc/book/2018-edition/Cargo.toml
Normal file
@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "rust-book"
|
||||
version = "0.0.1"
|
||||
authors = ["Steve Klabnik <steve@steveklabnik.com>"]
|
||||
description = "The Rust Book"
|
||||
|
||||
[[bin]]
|
||||
name = "concat_chapters"
|
||||
path = "tools/src/bin/concat_chapters.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "lfp"
|
||||
path = "tools/src/bin/lfp.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "link2print"
|
||||
path = "tools/src/bin/link2print.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "remove_links"
|
||||
path = "tools/src/bin/remove_links.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "remove_markup"
|
||||
path = "tools/src/bin/remove_markup.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "convert_quotes"
|
||||
path = "tools/src/bin/convert_quotes.rs"
|
||||
|
||||
[dependencies]
|
||||
walkdir = "0.1.5"
|
||||
docopt = "0.6.82"
|
||||
rustc-serialize = "0.3.19"
|
||||
regex = "0.1.73"
|
||||
lazy_static = "0.2.1"
|
||||
201
src/doc/book/2018-edition/LICENSE-APACHE
Normal file
201
src/doc/book/2018-edition/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2010-2017 The Rust Project Developers
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
src/doc/book/2018-edition/LICENSE-MIT
Normal file
25
src/doc/book/2018-edition/LICENSE-MIT
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2010-2017 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
3
src/doc/book/2018-edition/book.toml
Normal file
3
src/doc/book/2018-edition/book.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[book]
|
||||
title = "The Rust Programming Language"
|
||||
author = "Steve Klabnik and Carol Nichols, with Contributions from the Rust Community"
|
||||
22
src/doc/book/2018-edition/convert-quotes.sh
Normal file
22
src/doc/book/2018-edition/convert-quotes.sh
Normal file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2017 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.
|
||||
|
||||
set -eu
|
||||
|
||||
dir=$1
|
||||
|
||||
mkdir -p "tmp/$dir"
|
||||
|
||||
for f in $dir/*.md
|
||||
do
|
||||
cat "$f" | cargo run --bin convert_quotes > "tmp/$f"
|
||||
mv "tmp/$f" "$f"
|
||||
done
|
||||
502
src/doc/book/2018-edition/dictionary.txt
Normal file
502
src/doc/book/2018-edition/dictionary.txt
Normal file
@ -0,0 +1,502 @@
|
||||
personal_ws-1.1 en 0 utf-8
|
||||
abcabcabc
|
||||
abcd
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
adaptor
|
||||
adaptors
|
||||
AddAssign
|
||||
Addr
|
||||
aggregator
|
||||
AGraph
|
||||
aliasability
|
||||
alignof
|
||||
alloc
|
||||
allocator
|
||||
Amir
|
||||
anotherusername
|
||||
APIs
|
||||
app's
|
||||
aren
|
||||
args
|
||||
associativity
|
||||
async
|
||||
atomics
|
||||
AveragedCollection
|
||||
backend
|
||||
backported
|
||||
backtrace
|
||||
backtraces
|
||||
BACKTRACE
|
||||
Backtraces
|
||||
Baz’s
|
||||
benchmarking
|
||||
bioinformatics
|
||||
bitand
|
||||
BitAnd
|
||||
BitAndAssign
|
||||
bitor
|
||||
BitOr
|
||||
BitOrAssign
|
||||
bitwise
|
||||
Bitwise
|
||||
bitxor
|
||||
BitXor
|
||||
BitXorAssign
|
||||
Bjarne
|
||||
Boehm
|
||||
bool
|
||||
Boolean
|
||||
Booleans
|
||||
Bors
|
||||
BorrowMutError
|
||||
BTreeSet
|
||||
BuildHasher
|
||||
Cacher
|
||||
Cagain
|
||||
callsite
|
||||
CamelCase
|
||||
cargodoc
|
||||
ChangeColor
|
||||
ChangeColorMessage
|
||||
charset
|
||||
choo
|
||||
chXX
|
||||
chYY
|
||||
coercions
|
||||
combinator
|
||||
ConcreteType
|
||||
config
|
||||
Config
|
||||
const
|
||||
constant's
|
||||
copyeditor
|
||||
couldn
|
||||
CPUs
|
||||
cratesio
|
||||
CRLF
|
||||
cryptocurrencies
|
||||
cryptographic
|
||||
cryptographically
|
||||
CStr
|
||||
CString
|
||||
ctrl
|
||||
Ctrl
|
||||
customizable
|
||||
CustomSmartPointer
|
||||
CustomSmartPointers
|
||||
data’s
|
||||
deallocate
|
||||
deallocated
|
||||
deallocating
|
||||
deallocation
|
||||
debuginfo
|
||||
decrementing
|
||||
deps
|
||||
deref
|
||||
Deref
|
||||
dereference
|
||||
Dereference
|
||||
dereferenced
|
||||
dereferences
|
||||
dereferencing
|
||||
DerefMut
|
||||
DeriveInput
|
||||
destructor
|
||||
destructure
|
||||
destructured
|
||||
destructures
|
||||
destructuring
|
||||
Destructuring
|
||||
deterministically
|
||||
DevOps
|
||||
didn
|
||||
Dobrý
|
||||
doccargo
|
||||
doccratesio
|
||||
DOCTYPE
|
||||
doesn
|
||||
disambiguating
|
||||
DivAssign
|
||||
DraftPost
|
||||
DSTs
|
||||
ebooks
|
||||
Edsger
|
||||
egular
|
||||
else's
|
||||
emoji
|
||||
encodings
|
||||
enum
|
||||
Enum
|
||||
enums
|
||||
enum's
|
||||
Enums
|
||||
eprintln
|
||||
Erlang
|
||||
ErrorKind
|
||||
Executables
|
||||
expr
|
||||
extern
|
||||
favicon
|
||||
FFFD
|
||||
FFFF
|
||||
figcaption
|
||||
fieldname
|
||||
filename
|
||||
Filename
|
||||
filesystem
|
||||
Filesystem
|
||||
filesystems
|
||||
Firefox
|
||||
FnBox
|
||||
FnMut
|
||||
FnOnce
|
||||
formatter
|
||||
formatters
|
||||
FrenchToast
|
||||
FromIterator
|
||||
frontend
|
||||
getter
|
||||
GGraph
|
||||
GitHub
|
||||
gitignore
|
||||
grapheme
|
||||
Grapheme
|
||||
growable
|
||||
gzip
|
||||
hardcode
|
||||
hardcoded
|
||||
hardcoding
|
||||
hasher
|
||||
hashers
|
||||
HashMap
|
||||
HashSet
|
||||
Haskell
|
||||
hasn
|
||||
HelloMacro
|
||||
helloworld
|
||||
HelloWorld
|
||||
HelloWorldName
|
||||
Hmmm
|
||||
Hoare
|
||||
Hola
|
||||
homogenous
|
||||
html
|
||||
hyperoptimize
|
||||
Iceburgh
|
||||
ident
|
||||
IDE
|
||||
IDEs
|
||||
IDE's
|
||||
IEEE
|
||||
impl
|
||||
implementor
|
||||
implementors
|
||||
ImportantExcerpt
|
||||
incrementing
|
||||
IndexMut
|
||||
indices
|
||||
init
|
||||
initializer
|
||||
inline
|
||||
instantiation
|
||||
internet
|
||||
IntoIterator
|
||||
InvalidDigit
|
||||
invariants
|
||||
ioerror
|
||||
iokind
|
||||
ioresult
|
||||
iostdin
|
||||
IpAddr
|
||||
IpAddrKind
|
||||
irst
|
||||
isize
|
||||
iter
|
||||
iterator's
|
||||
JavaScript
|
||||
JoinHandle
|
||||
Kay's
|
||||
kinded
|
||||
lang
|
||||
latin
|
||||
liballoc
|
||||
libc
|
||||
libcollections
|
||||
libcore
|
||||
libpanic
|
||||
librarys
|
||||
libreoffice
|
||||
libstd
|
||||
lifecycle
|
||||
LimitTracker
|
||||
LLVM
|
||||
lobally
|
||||
locators
|
||||
LockResult
|
||||
login
|
||||
lookup
|
||||
loopback
|
||||
lossy
|
||||
lval
|
||||
macOS
|
||||
Matsakis
|
||||
mathematic
|
||||
memoization
|
||||
metadata
|
||||
Metadata
|
||||
metaprogramming
|
||||
mibbit
|
||||
Mibbit
|
||||
millis
|
||||
minigrep
|
||||
mixup
|
||||
mkdir
|
||||
MockMessenger
|
||||
modifiability
|
||||
modularity
|
||||
monomorphization
|
||||
Monomorphization
|
||||
monomorphized
|
||||
MoveMessage
|
||||
Mozilla
|
||||
mpsc
|
||||
msvc
|
||||
MulAssign
|
||||
multibyte
|
||||
multithreaded
|
||||
mutex
|
||||
mutex's
|
||||
Mutex
|
||||
mutexes
|
||||
Mutexes
|
||||
MutexGuard
|
||||
MyBox
|
||||
namespace
|
||||
namespaced
|
||||
namespaces
|
||||
namespacing
|
||||
natively
|
||||
newfound
|
||||
NewJob
|
||||
NewsArticle
|
||||
NewThread
|
||||
newtype
|
||||
newtypes
|
||||
nitty
|
||||
nocapture
|
||||
nomicon
|
||||
nondeterministic
|
||||
nonequality
|
||||
nongeneric
|
||||
NotFound
|
||||
null's
|
||||
OCaml
|
||||
offsetof
|
||||
online
|
||||
OpenGL
|
||||
optimizations
|
||||
OptionalFloatingPointNumber
|
||||
OptionalNumber
|
||||
OsStr
|
||||
OsString
|
||||
other's
|
||||
OutlinePrint
|
||||
overloadable
|
||||
overread
|
||||
param
|
||||
parameterize
|
||||
ParseIntError
|
||||
PartialEq
|
||||
PartialOrd
|
||||
PendingReview
|
||||
PendingReviewPost
|
||||
PlaceholderType
|
||||
polymorphism
|
||||
PoolCreationError
|
||||
portia
|
||||
powershell
|
||||
PowerShell
|
||||
powi
|
||||
preprocessing
|
||||
Preprocessing
|
||||
preprocessor
|
||||
PrimaryColor
|
||||
println
|
||||
priv
|
||||
proc
|
||||
pthreads
|
||||
pushups
|
||||
QuitMessage
|
||||
quux
|
||||
RAII
|
||||
randcrate
|
||||
RangeFrom
|
||||
RangeTo
|
||||
RangeFull
|
||||
README
|
||||
READMEs
|
||||
rect
|
||||
recurse
|
||||
recv
|
||||
redeclaring
|
||||
Refactoring
|
||||
refactor
|
||||
refactoring
|
||||
refcell
|
||||
RefCell
|
||||
RefMut
|
||||
refutability
|
||||
reimplement
|
||||
RemAssign
|
||||
repr
|
||||
representable
|
||||
request's
|
||||
resizes
|
||||
resizing
|
||||
retweet
|
||||
rewordings
|
||||
rint
|
||||
ripgrep
|
||||
runnable
|
||||
runtime
|
||||
runtimes
|
||||
Rustacean
|
||||
Rustaceans
|
||||
rUsT
|
||||
rustc
|
||||
rustdoc
|
||||
Rustonomicon
|
||||
rustfmt
|
||||
rustup
|
||||
screenshot
|
||||
searchstring
|
||||
SecondaryColor
|
||||
SelectBox
|
||||
semver
|
||||
SemVer
|
||||
serde
|
||||
ShlAssign
|
||||
ShrAssign
|
||||
shouldn
|
||||
Simula
|
||||
situps
|
||||
sizeof
|
||||
Smalltalk
|
||||
snuck
|
||||
someproject
|
||||
someusername
|
||||
SPDX
|
||||
spdx
|
||||
SpreadsheetCell
|
||||
sqrt
|
||||
stackoverflow
|
||||
startup
|
||||
StaticRef
|
||||
stderr
|
||||
stdin
|
||||
Stdin
|
||||
stdlib
|
||||
stdout
|
||||
steveklabnik's
|
||||
stringify
|
||||
Stroustrup
|
||||
Stroustrup's
|
||||
struct
|
||||
Struct
|
||||
structs
|
||||
struct's
|
||||
Structs
|
||||
SubAssign
|
||||
subclasses
|
||||
subcommand
|
||||
subcommands
|
||||
subdirectories
|
||||
subdirectory
|
||||
submodule
|
||||
submodules
|
||||
Submodules
|
||||
suboptimal
|
||||
substring
|
||||
subteams
|
||||
subtree
|
||||
subtyping
|
||||
supertrait
|
||||
supertraits
|
||||
TcpListener
|
||||
TcpStream
|
||||
templating
|
||||
test's
|
||||
TextField
|
||||
That'd
|
||||
there'd
|
||||
ThreadPool
|
||||
timestamp
|
||||
Tiếng
|
||||
timeline
|
||||
tlborm
|
||||
TODO
|
||||
TokenStream
|
||||
toml
|
||||
TOML
|
||||
toolchain
|
||||
toolchains
|
||||
ToString
|
||||
tradeoff
|
||||
tradeoffs
|
||||
TrafficLight
|
||||
transcoding
|
||||
trpl
|
||||
tuesday
|
||||
tuple
|
||||
tuples
|
||||
turbofish
|
||||
Turon
|
||||
typeof
|
||||
TypeName
|
||||
UFCS
|
||||
unary
|
||||
Unary
|
||||
uncomment
|
||||
Uncomment
|
||||
unevaluated
|
||||
Uninstalling
|
||||
uninstall
|
||||
unix
|
||||
unpopulated
|
||||
unoptimized
|
||||
UnsafeCell
|
||||
unsafety
|
||||
unsized
|
||||
unsynchronized
|
||||
URIs
|
||||
UsefulType
|
||||
username
|
||||
USERPROFILE
|
||||
usize
|
||||
UsState
|
||||
utils
|
||||
vals
|
||||
variable's
|
||||
variant's
|
||||
vers
|
||||
versa
|
||||
Versioning
|
||||
visualstudio
|
||||
Vlissides
|
||||
vtable
|
||||
wasn
|
||||
WeatherForecast
|
||||
WebSocket
|
||||
whitespace
|
||||
wildcard
|
||||
wildcards
|
||||
workflow
|
||||
workspace
|
||||
workspaces
|
||||
Workspaces
|
||||
wouldn
|
||||
writeln
|
||||
WriteMessage
|
||||
xpression
|
||||
yyyy
|
||||
ZipImpl
|
||||
26
src/doc/book/2018-edition/dot/trpl04-01.dot
Normal file
26
src/doc/book/2018-edition/dot/trpl04-01.dot
Normal file
@ -0,0 +1,26 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
||||
35
src/doc/book/2018-edition/dot/trpl04-02.dot
Normal file
35
src/doc/book/2018-edition/dot/trpl04-02.dot
Normal file
@ -0,0 +1,35 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
||||
44
src/doc/book/2018-edition/dot/trpl04-03.dot
Normal file
44
src/doc/book/2018-edition/dot/trpl04-03.dot
Normal file
@ -0,0 +1,44 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table4:pointee;
|
||||
}
|
||||
|
||||
35
src/doc/book/2018-edition/dot/trpl04-04.dot
Normal file
35
src/doc/book/2018-edition/dot/trpl04-04.dot
Normal file
@ -0,0 +1,35 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" BGCOLOR="gray">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s2</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer:c -> table1:pointee;
|
||||
table3:pointer:c -> table1:pointee;
|
||||
}
|
||||
|
||||
32
src/doc/book/2018-edition/dot/trpl04-05.dot
Normal file
32
src/doc/book/2018-edition/dot/trpl04-05.dot
Normal file
@ -0,0 +1,32 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="borrower"></TD></TR>
|
||||
</TABLE>>];
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s1</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="borrowee">ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
edge[tailclip="false"];
|
||||
table1:pointer:c -> table2:pointee;
|
||||
table0:borrower:c -> table1:borrowee;
|
||||
}
|
||||
|
||||
41
src/doc/book/2018-edition/dot/trpl04-06.dot
Normal file
41
src/doc/book/2018-edition/dot/trpl04-06.dot
Normal file
@ -0,0 +1,41 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">world</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer2"></TD></TR>
|
||||
<TR><TD>len</TD><TD>5</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">s</TD></TR>
|
||||
<TR><TD>name</TD><TD>value</TD></TR>
|
||||
<TR><TD>ptr</TD><TD PORT="pointer"></TD></TR>
|
||||
<TR><TD>len</TD><TD>11</TD></TR>
|
||||
<TR><TD>capacity</TD><TD>11</TD></TR>
|
||||
</TABLE>>];
|
||||
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD>index</TD><TD>value</TD></TR>
|
||||
<TR><TD PORT="pointee">0</TD><TD>h</TD></TR>
|
||||
<TR><TD>1</TD><TD>e</TD></TR>
|
||||
<TR><TD>2</TD><TD>l</TD></TR>
|
||||
<TR><TD>3</TD><TD>l</TD></TR>
|
||||
<TR><TD>4</TD><TD>o</TD></TR>
|
||||
<TR><TD>5</TD><TD> </TD></TR>
|
||||
<TR><TD PORT="pointee2">6</TD><TD>w</TD></TR>
|
||||
<TR><TD>7</TD><TD>o</TD></TR>
|
||||
<TR><TD>8</TD><TD>r</TD></TR>
|
||||
<TR><TD>9</TD><TD>l</TD></TR>
|
||||
<TR><TD>10</TD><TD>d</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:pointer2:c -> table4:pointee2;
|
||||
table3:pointer:c -> table4:pointee;
|
||||
}
|
||||
|
||||
24
src/doc/book/2018-edition/dot/trpl15-01.dot
Normal file
24
src/doc/book/2018-edition/dot/trpl15-01.dot
Normal file
@ -0,0 +1,24 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD>∞</TD></TR>
|
||||
</TABLE></TD></TR>
|
||||
</TABLE></TD></TR>
|
||||
</TABLE></TD></TR>
|
||||
</TABLE></TD></TR>
|
||||
</TABLE>>];
|
||||
}
|
||||
|
||||
18
src/doc/book/2018-edition/dot/trpl15-02.dot
Normal file
18
src/doc/book/2018-edition/dot/trpl15-02.dot
Normal file
@ -0,0 +1,18 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table0[label=<
|
||||
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
|
||||
<TR><TD>i32</TD><TD>
|
||||
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD SIDES="B">Box</TD></TR>
|
||||
<TR><TD>usize</TD></TR>
|
||||
</TABLE>
|
||||
</TD></TR>
|
||||
</TABLE>>];
|
||||
}
|
||||
|
||||
51
src/doc/book/2018-edition/dot/trpl15-03.dot
Normal file
51
src/doc/book/2018-edition/dot/trpl15-03.dot
Normal file
@ -0,0 +1,51 @@
|
||||
digraph {
|
||||
rankdir=LR;
|
||||
overlap=false;
|
||||
dpi=300.0;
|
||||
node [shape="plaintext"];
|
||||
|
||||
table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD SIDES="B">b</TD><TD SIDES="B" PORT="ptr4"></TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table5[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD PORT="pte4">3</TD><TD PORT="ptr5"> </TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD SIDES="B">a</TD><TD SIDES="B" PORT="ptr0"></TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD PORT="pte0">5</TD><TD PORT="ptr1"> </TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD PORT="pte1">10</TD><TD PORT="ptr2"> </TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD PORT="pte2">Nil</TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
table6[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD SIDES="B">c</TD><TD SIDES="B" PORT="ptr6"></TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
table7[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
|
||||
<TR><TD PORT="pte6">4</TD><TD PORT="ptr7"> </TD></TR>
|
||||
</TABLE>>];
|
||||
|
||||
|
||||
edge[tailclip="false"];
|
||||
table0:ptr0:c -> table1:pte0;
|
||||
table1:ptr1:c -> table2:pte1;
|
||||
table2:ptr2:c -> table3:pte2;
|
||||
table4:ptr4:c -> table5:pte4;
|
||||
table5:ptr5:c -> table1:pte0;
|
||||
table6:ptr6:c -> table7:pte6;
|
||||
table7:ptr7:c -> table1:pte0;
|
||||
}
|
||||
|
||||
101
src/doc/book/2018-edition/spellcheck.sh
Normal file
101
src/doc/book/2018-edition/spellcheck.sh
Normal file
@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2016 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.
|
||||
|
||||
aspell --version
|
||||
|
||||
# Checks project markdown files for spell errors
|
||||
|
||||
# Notes:
|
||||
|
||||
# This script needs dictionary file ($dict_filename) with project-specific
|
||||
# valid words. If this file is missing, first invocation of a script generates
|
||||
# a file of words considered typos at the moment. User should remove real typos
|
||||
# from this file and leave only valid words. When script generates false
|
||||
# positive after source modification, new valid word should be added
|
||||
# to dictionary file.
|
||||
|
||||
# Default mode of this script is interactive. Each source file is scanned for
|
||||
# typos. aspell opens window, suggesting fixes for each found typo. Original
|
||||
# files with errors will be backed up to files with format "filename.md.bak".
|
||||
|
||||
# When running in CI, this script should be run in "list" mode (pass "list"
|
||||
# as first argument). In this mode script scans all files and reports found
|
||||
# errors. Exit code in this case depends on scan result:
|
||||
# 1 if any errors found,
|
||||
# 0 if all is clear.
|
||||
|
||||
# Script skips words with length less than or equal to 3. This helps to avoid
|
||||
# some false positives.
|
||||
|
||||
# We can consider skipping source code in markdown files (```code```) to reduce
|
||||
# rate of false positives, but then we lose ability to detect typos in code
|
||||
# comments/strings etc.
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
dict_filename=./dictionary.txt
|
||||
markdown_sources=(./src/*.md)
|
||||
mode="check"
|
||||
|
||||
# aspell repeatedly modifies personal dictionary for some purpose,
|
||||
# so we should use a copy of our dictionary
|
||||
dict_path="/tmp/$dict_filename"
|
||||
|
||||
if [[ "$1" == "list" ]]; then
|
||||
mode="list"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$dict_filename" ]]; then
|
||||
# Pre-check mode: generates dictionary of words aspell consider typos.
|
||||
# After user validates that this file contains only valid words, we can
|
||||
# look for typos using this dictionary and some default aspell dictionary.
|
||||
echo "Scanning files to generate dictionary file '$dict_filename'."
|
||||
echo "Please check that it doesn't contain any misspellings."
|
||||
|
||||
echo "personal_ws-1.1 en 0 utf-8" > "$dict_filename"
|
||||
cat "${markdown_sources[@]}" | aspell --ignore 3 list | sort -u >> "$dict_filename"
|
||||
elif [[ "$mode" == "list" ]]; then
|
||||
# List (default) mode: scan all files, report errors
|
||||
declare -i retval=0
|
||||
|
||||
cp "$dict_filename" "$dict_path"
|
||||
|
||||
if [ ! -f $dict_path ]; then
|
||||
retval=1
|
||||
exit "$retval"
|
||||
fi
|
||||
|
||||
for fname in "${markdown_sources[@]}"; do
|
||||
command=$(aspell --ignore 3 --personal="$dict_path" "$mode" < "$fname")
|
||||
if [[ -n "$command" ]]; then
|
||||
for error in $command; do
|
||||
# FIXME: Find more correct way to get line number
|
||||
# (ideally from aspell). Now it can make some false positives,
|
||||
# because it is just a grep
|
||||
grep --with-filename --line-number --color=always "$error" "$fname"
|
||||
done
|
||||
retval=1
|
||||
fi
|
||||
done
|
||||
exit "$retval"
|
||||
elif [[ "$mode" == "check" ]]; then
|
||||
# Interactive mode: fix typos
|
||||
cp "$dict_filename" "$dict_path"
|
||||
|
||||
if [ ! -f $dict_path ]; then
|
||||
retval=1
|
||||
exit "$retval"
|
||||
fi
|
||||
|
||||
for fname in "${markdown_sources[@]}"; do
|
||||
aspell --ignore 3 --dont-backup --personal="$dict_path" "$mode" "$fname"
|
||||
done
|
||||
fi
|
||||
132
src/doc/book/2018-edition/src/SUMMARY.md
Normal file
132
src/doc/book/2018-edition/src/SUMMARY.md
Normal file
@ -0,0 +1,132 @@
|
||||
# The Rust Programming Language
|
||||
|
||||
[Foreword](foreword.md)
|
||||
[Introduction](ch00-00-introduction.md)
|
||||
|
||||
## Getting started
|
||||
|
||||
- [Getting Started](ch01-00-getting-started.md)
|
||||
- [Installation](ch01-01-installation.md)
|
||||
- [Hello, World!](ch01-02-hello-world.md)
|
||||
- [Hello, Cargo!](ch01-03-hello-cargo.md)
|
||||
|
||||
- [Programming a Guessing Game](ch02-00-guessing-game-tutorial.md)
|
||||
|
||||
- [Common Programming Concepts](ch03-00-common-programming-concepts.md)
|
||||
- [Variables and Mutability](ch03-01-variables-and-mutability.md)
|
||||
- [Data Types](ch03-02-data-types.md)
|
||||
- [How Functions Work](ch03-03-how-functions-work.md)
|
||||
- [Comments](ch03-04-comments.md)
|
||||
- [Control Flow](ch03-05-control-flow.md)
|
||||
|
||||
- [Understanding Ownership](ch04-00-understanding-ownership.md)
|
||||
- [What is Ownership?](ch04-01-what-is-ownership.md)
|
||||
- [References & Borrowing](ch04-02-references-and-borrowing.md)
|
||||
- [Slices](ch04-03-slices.md)
|
||||
|
||||
- [Using Structs to Structure Related Data](ch05-00-structs.md)
|
||||
- [Defining and Instantiating Structs](ch05-01-defining-structs.md)
|
||||
- [An Example Program Using Structs](ch05-02-example-structs.md)
|
||||
- [Method Syntax](ch05-03-method-syntax.md)
|
||||
|
||||
- [Enums and Pattern Matching](ch06-00-enums.md)
|
||||
- [Defining an Enum](ch06-01-defining-an-enum.md)
|
||||
- [The `match` Control Flow Operator](ch06-02-match.md)
|
||||
- [Concise Control Flow with `if let`](ch06-03-if-let.md)
|
||||
|
||||
## Basic Rust Literacy
|
||||
|
||||
- [Modules](ch07-00-modules.md)
|
||||
- [`mod` and the Filesystem](ch07-01-mod-and-the-filesystem.md)
|
||||
- [Controlling Visibility with `pub`](ch07-02-controlling-visibility-with-pub.md)
|
||||
- [Referring to Names in Different Modules](ch07-03-importing-names-with-use.md)
|
||||
|
||||
- [Common Collections](ch08-00-common-collections.md)
|
||||
- [Vectors](ch08-01-vectors.md)
|
||||
- [Strings](ch08-02-strings.md)
|
||||
- [Hash Maps](ch08-03-hash-maps.md)
|
||||
|
||||
- [Error Handling](ch09-00-error-handling.md)
|
||||
- [Unrecoverable Errors with `panic!`](ch09-01-unrecoverable-errors-with-panic.md)
|
||||
- [Recoverable Errors with `Result`](ch09-02-recoverable-errors-with-result.md)
|
||||
- [To `panic!` or Not To `panic!`](ch09-03-to-panic-or-not-to-panic.md)
|
||||
|
||||
- [Generic Types, Traits, and Lifetimes](ch10-00-generics.md)
|
||||
- [Generic Data Types](ch10-01-syntax.md)
|
||||
- [Traits: Defining Shared Behavior](ch10-02-traits.md)
|
||||
- [Validating References with Lifetimes](ch10-03-lifetime-syntax.md)
|
||||
|
||||
- [Testing](ch11-00-testing.md)
|
||||
- [Writing tests](ch11-01-writing-tests.md)
|
||||
- [Running tests](ch11-02-running-tests.md)
|
||||
- [Test Organization](ch11-03-test-organization.md)
|
||||
|
||||
- [An I/O Project: Building a Command Line Program](ch12-00-an-io-project.md)
|
||||
- [Accepting Command Line Arguments](ch12-01-accepting-command-line-arguments.md)
|
||||
- [Reading a File](ch12-02-reading-a-file.md)
|
||||
- [Refactoring to Improve Modularity and Error Handling](ch12-03-improving-error-handling-and-modularity.md)
|
||||
- [Developing the Library’s Functionality with Test Driven Development](ch12-04-testing-the-librarys-functionality.md)
|
||||
- [Working with Environment Variables](ch12-05-working-with-environment-variables.md)
|
||||
- [Writing Error Messages to Standard Error Instead of Standard Output](ch12-06-writing-to-stderr-instead-of-stdout.md)
|
||||
|
||||
## Thinking in Rust
|
||||
|
||||
- [Functional Language Features: Iterators and Closures](ch13-00-functional-features.md)
|
||||
- [Closures: Anonymous Functions that Can Capture Their Environment](ch13-01-closures.md)
|
||||
- [Processing a Series of Items with Iterators](ch13-02-iterators.md)
|
||||
- [Improving Our I/O Project](ch13-03-improving-our-io-project.md)
|
||||
- [Comparing Performance: Loops vs. Iterators](ch13-04-performance.md)
|
||||
|
||||
- [More about Cargo and Crates.io](ch14-00-more-about-cargo.md)
|
||||
- [Customizing Builds with Release Profiles](ch14-01-release-profiles.md)
|
||||
- [Publishing a Crate to Crates.io](ch14-02-publishing-to-crates-io.md)
|
||||
- [Cargo Workspaces](ch14-03-cargo-workspaces.md)
|
||||
- [Installing Binaries from Crates.io with `cargo install`](ch14-04-installing-binaries.md)
|
||||
- [Extending Cargo with Custom Commands](ch14-05-extending-cargo.md)
|
||||
|
||||
- [Smart Pointers](ch15-00-smart-pointers.md)
|
||||
- [`Box<T>` Points to Data on the Heap and Has a Known Size](ch15-01-box.md)
|
||||
- [The `Deref` Trait Allows Access to the Data Through a Reference](ch15-02-deref.md)
|
||||
- [The `Drop` Trait Runs Code on Cleanup](ch15-03-drop.md)
|
||||
- [`Rc<T>`, the Reference Counted Smart Pointer](ch15-04-rc.md)
|
||||
- [`RefCell<T>` and the Interior Mutability Pattern](ch15-05-interior-mutability.md)
|
||||
- [Creating Reference Cycles and Leaking Memory is Safe](ch15-06-reference-cycles.md)
|
||||
|
||||
- [Fearless Concurrency](ch16-00-concurrency.md)
|
||||
- [Threads](ch16-01-threads.md)
|
||||
- [Message Passing](ch16-02-message-passing.md)
|
||||
- [Shared State](ch16-03-shared-state.md)
|
||||
- [Extensible Concurrency: `Sync` and `Send`](ch16-04-extensible-concurrency-sync-and-send.md)
|
||||
|
||||
- [Object Oriented Programming Features of Rust](ch17-00-oop.md)
|
||||
- [Characteristics of Object-Oriented Languages](ch17-01-what-is-oo.md)
|
||||
- [Using Trait Objects that Allow for Values of Different Types](ch17-02-trait-objects.md)
|
||||
- [Implementing an Object-Oriented Design Pattern](ch17-03-oo-design-patterns.md)
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
- [Patterns Match the Structure of Values](ch18-00-patterns.md)
|
||||
- [All the Places Patterns May be Used](ch18-01-all-the-places-for-patterns.md)
|
||||
- [Refutability: Whether a Pattern Might Fail to Match](ch18-02-refutability.md)
|
||||
- [All the Pattern Syntax](ch18-03-pattern-syntax.md)
|
||||
|
||||
- [Advanced Features](ch19-00-advanced-features.md)
|
||||
- [Unsafe Rust](ch19-01-unsafe-rust.md)
|
||||
- [Advanced Lifetimes](ch19-02-advanced-lifetimes.md)
|
||||
- [Advanced Traits](ch19-03-advanced-traits.md)
|
||||
- [Advanced Types](ch19-04-advanced-types.md)
|
||||
- [Advanced Functions & Closures](ch19-05-advanced-functions-and-closures.md)
|
||||
|
||||
- [Final Project: Building a Multithreaded Web Server](ch20-00-final-project-a-web-server.md)
|
||||
- [A Single Threaded Web Server](ch20-01-single-threaded.md)
|
||||
- [Turning our Single Threaded Server into a Multithreaded Server](ch20-02-multithreaded.md)
|
||||
- [Graceful Shutdown and Cleanup](ch20-03-graceful-shutdown-and-cleanup.md)
|
||||
|
||||
- [Appendix](appendix-00.md)
|
||||
- [A - Keywords](appendix-01-keywords.md)
|
||||
- [B - Operators and Symbols](appendix-02-operators.md)
|
||||
- [C - Derivable Traits](appendix-03-derivable-traits.md)
|
||||
- [D - Macros](appendix-04-macros.md)
|
||||
- [E - Translations](appendix-05-translation.md)
|
||||
- [F - Newest Features](appendix-06-newest-features.md)
|
||||
- [G - How Rust is Made and “Nightly Rust”](appendix-07-nightly-rust.md)
|
||||
4
src/doc/book/2018-edition/src/appendix-00.md
Normal file
4
src/doc/book/2018-edition/src/appendix-00.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Appendix
|
||||
|
||||
The following sections contain reference material you may find useful in your
|
||||
Rust journey.
|
||||
80
src/doc/book/2018-edition/src/appendix-01-keywords.md
Normal file
80
src/doc/book/2018-edition/src/appendix-01-keywords.md
Normal file
@ -0,0 +1,80 @@
|
||||
## Appendix A: Keywords
|
||||
|
||||
The following is a list of keywords that are reserved for current or future use
|
||||
by the Rust language. As such, these may not be used as identifiers, such as
|
||||
names of functions, variables, parameters, struct fields, modules, crates,
|
||||
constants, macros, static values, attributes, types, traits, or lifetimes.
|
||||
|
||||
### Keywords Currently in Use
|
||||
|
||||
* `as` - perform primitive casting, disambiguate the specific trait
|
||||
containing an item, or rename items in `use` and `extern crate` statements
|
||||
* `break` - exit a loop immediately
|
||||
* `const` - define constant items or constant raw pointers
|
||||
* `continue` - continue to the next loop iteration
|
||||
* `crate` - link an external crate or a macro variable representing the crate
|
||||
in which the macro is defined
|
||||
* `else` - fallback for `if` and `if let` control flow constructs
|
||||
* `enum` - define an enumeration
|
||||
* `extern` - link an external crate, function, or variable
|
||||
* `false` - Boolean false literal
|
||||
* `fn` - define a function or the function pointer type
|
||||
* `for` - loop over items from an iterator, implement a trait, or specify a
|
||||
higher-ranked lifetime
|
||||
* `if` - branch based on the result of a conditional expression
|
||||
* `impl` - implement inherent or trait functionality
|
||||
* `in` - part of `for` loop syntax
|
||||
* `let` - bind a variable
|
||||
* `loop` - loop unconditionally
|
||||
* `match` - match a value to patterns
|
||||
* `mod` - define a module
|
||||
* `move` - make a closure take ownership of all its captures
|
||||
* `mut` - denote mutability in references, raw pointers, or pattern bindings
|
||||
* `pub` - denote public visibility in struct fields, `impl` blocks, or modules
|
||||
* `ref` - bind by reference
|
||||
* `return` - return from function
|
||||
* `Self` - a type alias for the type implementing a trait
|
||||
* `self` - method subject or current module
|
||||
* `static` - global variable or lifetime lasting the entire program execution
|
||||
* `struct` - define a structure
|
||||
* `super` - parent module of the current module
|
||||
* `trait` - define a trait
|
||||
* `true` - Boolean true literal
|
||||
* `type` - define a type alias or associated type
|
||||
* `unsafe` - denote unsafe code, functions, traits, or implementations
|
||||
* `use` - import symbols into scope
|
||||
* `where` - denote clauses that constrain a type
|
||||
* `while` - loop conditionally based on the result of an expression
|
||||
|
||||
<!-- we should make sure the definitions for each keyword are consistently
|
||||
phrased, so for example for enum we say "defining an enumeration" but for fn we
|
||||
passively call it a "function definition" -- perhaps a good medium would be
|
||||
"define an enumeration" and "define a function"? Can you go through and make
|
||||
those consistent? I've attempted it for a few, but am wary of changing meaning.
|
||||
Also, you may decide to go the passive definition route, which is fine by me,
|
||||
as long as it's consistent-->
|
||||
<!-- I've tried, I'm not sure how to be active for keywords that are nouns
|
||||
though. Please let me know if any still seem inconsistent /Carol -->
|
||||
|
||||
### Keywords Reserved for Future Use
|
||||
|
||||
These keywords do not have any functionality, but are reserved by Rust for
|
||||
potential future use.
|
||||
|
||||
* `abstract`
|
||||
* `alignof`
|
||||
* `become`
|
||||
* `box`
|
||||
* `do`
|
||||
* `final`
|
||||
* `macro`
|
||||
* `offsetof`
|
||||
* `override`
|
||||
* `priv`
|
||||
* `proc`
|
||||
* `pure`
|
||||
* `sizeof`
|
||||
* `typeof`
|
||||
* `unsized`
|
||||
* `virtual`
|
||||
* `yield`
|
||||
172
src/doc/book/2018-edition/src/appendix-02-operators.md
Normal file
172
src/doc/book/2018-edition/src/appendix-02-operators.md
Normal file
@ -0,0 +1,172 @@
|
||||
## Appendix B: Operators and Symbols
|
||||
|
||||
<!-- We try not to stack headings even in the appendix, can you add some intro
|
||||
text about what this appendix contains? Quick example below -->
|
||||
<!-- Done! /Carol -->
|
||||
|
||||
This appendix is a glossary of Rust’s syntax, including operators and other
|
||||
symbols that appear by themselves or in the context of paths, generics, trait
|
||||
bounds, macros, attributes, comments, tuples, and brackets.
|
||||
|
||||
### Operators
|
||||
|
||||
The following lists the operators in Rust, an example of how the operator would
|
||||
appear in context, a short explanation, and whether that operator is
|
||||
overloadable. If an operator is overloadable, the relevant trait to use to
|
||||
overload that operator is listed.
|
||||
|
||||
<!-- PROD: I'm not sure how to handle this, would it be too big for a table? I
|
||||
think some structure with aligned columns would make it a great reference -->
|
||||
|
||||
* `!` (`ident!(…)`, `ident!{…}`, `ident![…]`): denotes macro expansion.
|
||||
* `!` (`!expr`): bitwise or logical complement. Overloadable (`Not`).
|
||||
* `!=` (`var != expr`): nonequality comparison. Overloadable (`PartialEq`).
|
||||
* `%` (`expr % expr`): arithmetic remainder. Overloadable (`Rem`).
|
||||
* `%=` (`var %= expr`): arithmetic remainder and assignment. Overloadable (`RemAssign`).
|
||||
* `&` (`&expr`, `&mut expr`): borrow.
|
||||
* `&` (`&type`, `&mut type`, `&'a type`, `&'a mut type`): borrowed pointer type.
|
||||
* `&` (`expr & expr`): bitwise AND. Overloadable (`BitAnd`).
|
||||
* `&=` (`var &= expr`): bitwise AND and assignment. Overloadable (`BitAndAssign`).
|
||||
* `&&` (`expr && expr`): logical AND.
|
||||
* `*` (`expr * expr`): arithmetic multiplication. Overloadable (`Mul`).
|
||||
* `*` (`*expr`): dereference.
|
||||
* `*` (`*const type`, `*mut type`): raw pointer.
|
||||
* `*=` (`var *= expr`): arithmetic multiplication and assignment. Overloadable (`MulAssign`).
|
||||
* `+` (`trait + trait`, `'a + trait`): compound type constraint.
|
||||
* `+` (`expr + expr`): arithmetic addition. Overloadable (`Add`).
|
||||
* `+=` (`var += expr`): arithmetic addition and assignment. Overloadable (`AddAssign`).
|
||||
* `,`: argument and element separator.
|
||||
* `-` (`- expr`): arithmetic negation. Overloadable (`Neg`).
|
||||
* `-` (`expr - expr`): arithmetic subtraction. Overloadable (`Sub`).
|
||||
* `-=` (`var -= expr`): arithmetic subtraction and assignment. Overloadable (`SubAssign`).
|
||||
* `->` (`fn(…) -> type`, `|…| -> type`): function and closure return type.
|
||||
* `.` (`expr.ident`): member access.
|
||||
* `..` (`..`, `expr..`, `..expr`, `expr..expr`): right-exclusive range literal.
|
||||
* `..` (`..expr`): struct literal update syntax.
|
||||
* `..` (`variant(x, ..)`, `struct_type { x, .. }`): “and the rest” pattern binding.
|
||||
* `...` (`expr...expr`) *in a pattern*: inclusive range pattern.
|
||||
* `/` (`expr / expr`): arithmetic division. Overloadable (`Div`).
|
||||
* `/=` (`var /= expr`): arithmetic division and assignment. Overloadable (`DivAssign`).
|
||||
* `:` (`pat: type`, `ident: type`): constraints.
|
||||
* `:` (`ident: expr`): struct field initializer.
|
||||
* `:` (`'a: loop {…}`): loop label.
|
||||
* `;`: statement and item terminator.
|
||||
* `;` (`[…; len]`): part of fixed-size array syntax
|
||||
* `<<` (`expr << expr`): left-shift. Overloadable (`Shl`).
|
||||
* `<<=` (`var <<= expr`): left-shift and assignment. Overloadable (`ShlAssign`).
|
||||
* `<` (`expr < expr`): less-than comparison. Overloadable (`PartialOrd`).
|
||||
* `<=` (`expr <= expr`): less-than or equal-to comparison. Overloadable (`PartialOrd`).
|
||||
* `=` (`var = expr`, `ident = type`): assignment/equivalence.
|
||||
* `==` (`expr == expr`): equality comparison. Overloadable (`PartialEq`).
|
||||
* `=>` (`pat => expr`): part of match arm syntax.
|
||||
* `>` (`expr > expr`): greater-than comparison. Overloadable (`PartialOrd`).
|
||||
* `>=` (`expr >= expr`): greater-than or equal-to comparison. Overloadable (`PartialOrd`).
|
||||
* `>>` (`expr >> expr`): right-shift. Overloadable (`Shr`).
|
||||
* `>>=` (`var >>= expr`): right-shift and assignment. Overloadable (`ShrAssign`).
|
||||
* `@` (`ident @ pat`): pattern binding.
|
||||
* `^` (`expr ^ expr`): bitwise exclusive OR. Overloadable (`BitXor`).
|
||||
* `^=` (`var ^= expr`): bitwise exclusive OR and assignment. Overloadable (`BitXorAssign`).
|
||||
* `|` (`pat | pat`): pattern alternatives.
|
||||
* `|` (`|…| expr`): closures.
|
||||
* `|` (`expr | expr`): bitwise OR. Overloadable (`BitOr`).
|
||||
* `|=` (`var |= expr`): bitwise OR and assignment. Overloadable (`BitOrAssign`).
|
||||
* `||` (`expr || expr`): logical OR.
|
||||
* `_`: “ignored” pattern binding. Also used to make integer-literals readable.
|
||||
* `?` (`expr?`): Error propagation.
|
||||
|
||||
### Non-operator Symbols
|
||||
|
||||
<!-- And maybe a quick explanation of what you mean by non-operator
|
||||
symbols/what counts as a non-operator symbol? -->
|
||||
<!-- I've tried but it's hard to explain, it's the kind of thing you know when
|
||||
you see it? /Carol -->
|
||||
|
||||
The following lists all non-letters that don’t function as operators; that is,
|
||||
they don’t behave like a function or method call.
|
||||
|
||||
#### Standalone Syntax
|
||||
|
||||
* `'ident`: named lifetime or loop label
|
||||
* `…u8`, `…i32`, `…f64`, `…usize`, *etc.*: numeric literal of specific type.
|
||||
* `"…"`: string literal.
|
||||
* `r"…"`, `r#"…"#`, `r##"…"##`, *etc.*: raw string literal, escape characters are not processed.
|
||||
* `b"…"`: byte string literal, constructs a `[u8]` instead of a string.
|
||||
* `br"…"`, `br#"…"#`, `br##"…"##`, *etc.*: raw byte string literal, combination of raw and byte string literal.
|
||||
* `'…'`: character literal.
|
||||
* `b'…'`: ASCII byte literal.
|
||||
* `|…| expr`: closure.
|
||||
* `!`: always empty bottom type for diverging functions.
|
||||
|
||||
#### Path-related Syntax
|
||||
|
||||
* `ident::ident`: namespace path.
|
||||
* `::path`: path relative to the crate root (*i.e.* an explicitly absolute path).
|
||||
* `self::path`: path relative to the current module (*i.e.* an explicitly relative path).
|
||||
* `super::path`: path relative to the parent of the current module.
|
||||
* `type::ident`, `<type as trait>::ident`: associated constants, functions, and types.
|
||||
* `<type>::…`: associated item for a type which cannot be directly named (*e.g.* `<&T>::…`, `<[T]>::…`, *etc.*).
|
||||
* `trait::method(…)`: disambiguating a method call by naming the trait which defines it.
|
||||
* `type::method(…)`: disambiguating a method call by naming the type for which it’s defined.
|
||||
* `<type as trait>::method(…)`: disambiguating a method call by naming the trait *and* type.
|
||||
|
||||
#### Generics
|
||||
|
||||
* `path<…>` (*e.g.* `Vec<u8>`): specifies parameters to generic type *in a type*.
|
||||
* `path::<…>`, `method::<…>` (*e.g.* `"42".parse::<i32>()`): specifies parameters to generic type, function, or method *in an expression*. Often referred to as *turbofish*.
|
||||
* `fn ident<…> …`: define generic function.
|
||||
* `struct ident<…> …`: define generic structure.
|
||||
* `enum ident<…> …`: define generic enumeration.
|
||||
* `impl<…> …`: define generic implementation.
|
||||
* `for<…> type`: higher-ranked lifetime bounds.
|
||||
* `type<ident=type>` (*e.g.* `Iterator<Item=T>`): a generic type where one or more associated types have specific assignments.
|
||||
|
||||
#### Trait Bound Constraints
|
||||
|
||||
* `T: U`: generic parameter `T` constrained to types that implement `U`.
|
||||
* `T: 'a`: generic type `T` must outlive lifetime `'a`. When we say that a type ‘outlives’ the lifetime, we mean that it cannot transitively contain any references with lifetimes shorter than `'a`.
|
||||
* `T : 'static`: The generic type `T` contains no borrowed references other than `'static` ones.
|
||||
* `'b: 'a`: generic lifetime `'b` must outlive lifetime `'a`.
|
||||
* `T: ?Sized`: allow generic type parameter to be a dynamically-sized type.
|
||||
* `'a + trait`, `trait + trait`: compound type constraint.
|
||||
|
||||
#### Macros and Attributes
|
||||
|
||||
* `#[meta]`: outer attribute.
|
||||
* `#![meta]`: inner attribute.
|
||||
* `$ident`: macro substitution.
|
||||
* `$ident:kind`: macro capture.
|
||||
* `$(…)…`: macro repetition.
|
||||
|
||||
#### Comments
|
||||
|
||||
* `//`: line comment.
|
||||
* `//!`: inner line doc comment.
|
||||
* `///`: outer line doc comment.
|
||||
* `/*…*/`: block comment.
|
||||
* `/*!…*/`: inner block doc comment.
|
||||
* `/**…*/`: outer block doc comment.
|
||||
|
||||
#### Tuples
|
||||
|
||||
* `()`: empty tuple (*a.k.a.* unit), both literal and type.
|
||||
* `(expr)`: parenthesized expression.
|
||||
* `(expr,)`: single-element tuple expression.
|
||||
* `(type,)`: single-element tuple type.
|
||||
* `(expr, …)`: tuple expression.
|
||||
* `(type, …)`: tuple type.
|
||||
* `expr(expr, …)`: function call expression. Also used to initialize tuple `struct`s and tuple `enum` variants.
|
||||
* `ident!(…)`, `ident!{…}`, `ident![…]`: macro invocation.
|
||||
* `expr.0`, `expr.1`, …: tuple indexing.
|
||||
|
||||
#### Curly Brackets
|
||||
|
||||
* `{…}`: block expression.
|
||||
* `Type {…}`: `struct` literal.
|
||||
|
||||
#### Square Brackets
|
||||
|
||||
* `[…]`: array literal.
|
||||
* `[expr; len]`: array literal containing `len` copies of `expr`.
|
||||
* `[type; len]`: array type containing `len` instances of `type`.
|
||||
* `expr[expr]`: collection indexing. Overloadable (`Index`, `IndexMut`).
|
||||
* `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]`: collection indexing pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, `RangeFull` as the “index”.
|
||||
230
src/doc/book/2018-edition/src/appendix-03-derivable-traits.md
Normal file
230
src/doc/book/2018-edition/src/appendix-03-derivable-traits.md
Normal file
@ -0,0 +1,230 @@
|
||||
## C - Derivable Traits
|
||||
|
||||
In various places in the book, we’ve discussed the `derive` attribute
|
||||
that you can apply to a struct or enum definition.
|
||||
|
||||
<!-- Above -- I wasn't clear throughout whether the derive attribute is
|
||||
something passively applied to structs and enums by Rust, or something the
|
||||
reader applies. I've experimented with making the tone more active, but may
|
||||
have misinterpreted -- can you make it clear here? Should this be "we've
|
||||
discussed the `derive` attribute you can apply to a struct or enum"? -->
|
||||
<!-- Rust never edits your source code file for you. I'm curious to know what
|
||||
parts of the book have given you that impression... I've tried to clarify here
|
||||
but now I'm worried about other places in the book... /Carol -->
|
||||
|
||||
<!-- Below -- Can you lay out what it is we're showing them about derivable
|
||||
traits in this appendix, just showing them some common ones and how to use
|
||||
them? -->
|
||||
<!-- No, we're showing *all* of the derivable traits provided by the standard
|
||||
library. I guess explaining what we mean by "derivable" was too much of a
|
||||
tangent for the beginning of this section? I'm not sure where that would fit
|
||||
instead, so I took it out. So now the text that we had under the "standard
|
||||
library traits that can be derived" section is here where it seems like you
|
||||
were expecting it to be /Carol -->
|
||||
|
||||
The `derive` attribute generates code that will implement a trait with its own
|
||||
default implementation, on the type you have annotated with the `derive`
|
||||
syntax. In this appendix, we provide a reference of all of the traits in the
|
||||
standard library that can be used with `derive`. Each section covers:
|
||||
|
||||
- What operators and methods deriving this trait will enable
|
||||
- What the implementation of the trait provided by `derive` does
|
||||
- What implementing the trait signifies about the type
|
||||
- The conditions in which you’re allowed or not allowed to implement the trait
|
||||
- Examples of operations that require the trait
|
||||
|
||||
If you want different behavior than that provided by the `derive` attribute,
|
||||
consult the standard library documentation for each trait for details of how to
|
||||
manually implement them.
|
||||
|
||||
<!-- Liz: I've incorporated the small sections that were after the list of
|
||||
traits here and then moved the section headings out a level, what do you think?
|
||||
/Carol -->
|
||||
|
||||
The rest of the traits defined in the standard library can’t be implemented on
|
||||
your types using `derive`. These traits don’t have sensible default behavior,
|
||||
so it’s up to you to implement them in the way that makes sense for what you’re
|
||||
trying to accomplish.
|
||||
|
||||
An example of a trait that can’t be derived is `Display`, which handles
|
||||
formatting for end users. You should always put thought into the appropriate
|
||||
way to display a type to an end user: what parts of the type should an end user
|
||||
be allowed to see? What parts would they find relevant? What format of the data
|
||||
would be most relevant to them? The Rust compiler doesn’t have this insight and
|
||||
so can’t provide appropriate default behavior for you.
|
||||
|
||||
The list of derivable traits provided in this appendix is not comprehensive:
|
||||
libraries can implement `derive` for their own traits! In this way, the list of
|
||||
traits you can use `derive` with is truly open-ended. Implementing `derive`
|
||||
involves using a procedural macro, which is covered in Appendix D, “Macros.”
|
||||
|
||||
### `Debug` for Programmer Output
|
||||
|
||||
The `Debug` trait enables debug formatting in format strings, indicated by
|
||||
adding `:?` within `{}` placeholders.
|
||||
|
||||
The `Debug` trait allows you to print instances of a type for debugging
|
||||
purposes, so you and other programmers using your type can inspect an instance
|
||||
at a particular point in a program’s execution.
|
||||
|
||||
`Debug` is required, for example, in use of the `assert_eq!` macro, which
|
||||
prints the values of instances given as arguments if the equality assertion
|
||||
fails so programmers can see why the two instances weren’t equal.
|
||||
|
||||
### `PartialEq` and `Eq` for Equality Comparisons
|
||||
|
||||
<!-- I've tried to phrase these definitions in a more active way, it seems like
|
||||
we're saying using these traits gives us this capabilities --- apologies if
|
||||
I've misunderstood, feel free to change the phrasing back to the "signifies
|
||||
that..." version -->
|
||||
<!-- More active is fine. I feel like it lost a tiny bit of meaning-- not only
|
||||
can we use these capabilities on our own types, but other programmers using our
|
||||
types can use these capabilities too. I've tried to reinsert that sentiment
|
||||
occasionally. /Carol -->
|
||||
|
||||
The `PartialEq` trait allows you to compare instances of a type to check for
|
||||
equality, and enables use of the `==` and `!=` operators.
|
||||
|
||||
Deriving `PartialEq` implements the `eq` method. When `PartialEq` is derived on
|
||||
structs, two instances are equal only if *all* fields are equal, and not equal
|
||||
if any fields are not equal. When derived on enums, each variant is equal to
|
||||
itself and not equal to the other variants.
|
||||
|
||||
`PartialEq` is required, for example, with the use of the `assert_eq!` macro,
|
||||
which needs to be able to compare two instances of a type for equality.
|
||||
|
||||
The `Eq` trait has no methods. Its purpose is to signal that for every value of
|
||||
the annotated type, the value is equal to itself. The `Eq` trait can only be
|
||||
applied to types that also implement `PartialEq`, though not all types that
|
||||
implement `PartialEq` can implement `Eq`. One example of this is floating point
|
||||
number types: the implementation of floating point numbers says that two
|
||||
instances of the not-a-number value, `NaN`, are not equal to each other.
|
||||
|
||||
An example of when `Eq` is required is for keys in a `HashMap` so that the
|
||||
`HashMap` can tell whether two keys are the same.
|
||||
|
||||
### `PartialOrd` and `Ord` for Ordering Comparisons
|
||||
|
||||
The `PartialOrd` trait allows you to compare instances of a type for sorting
|
||||
purposes. A type that implements `PartialOrd` may be used with the `<`, `>`,
|
||||
`<=`, and `>=` operators. The `PartialOrd` trait can only be applied to types
|
||||
that also implement `PartialEq`.
|
||||
|
||||
Deriving `PartialOrd` implements the `partial_cmp` method, which returns an
|
||||
`Option<Ordering>` that will be `None` when the values given do not produce an
|
||||
ordering. An example of a value that doesn’t produce an ordering, even though
|
||||
most values of that type can be compared, is the not-a-number (`NaN`) floating
|
||||
point value. Calling `partial_cmp` with any floating point number and the `NaN`
|
||||
floating point value will return `None`.
|
||||
|
||||
<!-- Above -- you mean when the values cannot be ordered, for example if they
|
||||
are of types that can't be compared? -->
|
||||
<!-- No, if they're *types* that can't be compared, then the PartialOrd trait
|
||||
doesn't apply at all. I've tried to clarify and added an example /Carol-->
|
||||
|
||||
When derived on structs, `PartialOrd` compares two instances by comparing the
|
||||
value in each field in the order in which the fields appear in the struct
|
||||
definition. When derived on enums, variants of the enum declared earlier in the
|
||||
enum definition are considered greater than the variants listed later.
|
||||
|
||||
`PartialOrd` is required, for example, for the `gen_range` method from the
|
||||
`rand` crate that generates a random value in the range specified by a low
|
||||
value and a high value.
|
||||
|
||||
The `Ord` trait allows you to know that for any two values of the annotated
|
||||
type, a valid ordering will exist. The `Ord` trait implements the `cmp` method,
|
||||
which returns an `Ordering` rather than an `Option<Ordering>` because a valid
|
||||
ordering will always be possible. The `Ord` trait can only be applied to types
|
||||
that also implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When
|
||||
derived on structs and enums, `cmp` behaves the same way as the derived
|
||||
implementation for `partial_cmp` does with `PartialOrd`.
|
||||
|
||||
An example of when `Ord` is required is when storing values in a `BTreeSet<T>`,
|
||||
a data structure that stores data based on the sort order of the values.
|
||||
|
||||
### `Clone` and `Copy` for Duplicating Values
|
||||
|
||||
<!-- Below -- I wasn't clear on the arbitrary code section of this explanation.
|
||||
Are we saying using Clone (as opposed to copy) risks bringing it arbitrary
|
||||
code? Why use Clone over copy? (I think we might have covered this in an
|
||||
earlier chapter, so feel free to cross ref there too if that's an easier
|
||||
explanation) -->
|
||||
<!-- Yes, we covered this in chapter 4 and I've added a cross reference. /Carol
|
||||
-->
|
||||
|
||||
The `Clone` trait allows you to explicitly create a deep copy of a value, and
|
||||
the duplication process might involve running arbitrary code and copying heap
|
||||
data. See the “Ways Variables and Data Interact: Clone” section in Chapter 4
|
||||
for more information on `Clone`.
|
||||
|
||||
Deriving `Clone` implements the `clone` method which, when implemented for the
|
||||
whole type, calls `clone` on each of the parts of the type. This means all of
|
||||
the fields or values in the type must also implement `Clone` to derive `Clone`.
|
||||
|
||||
An example of when `Clone` is required is when calling the `to_vec` method on a
|
||||
slice. The slice doesn’t own the type instances it contains, but the vector
|
||||
returned from `to_vec` will need to own its instances, so `to_vec` calls
|
||||
`clone` on each item. Thus, the type stored in the slice must implement `Clone`.
|
||||
|
||||
The `Copy` trait allows you to duplicate a value by only copying bits stored on
|
||||
the stack; no arbitrary code is necessary. See the “Stack-Only Data: Copy”
|
||||
section in Chapter 4 for more information on `Copy`.
|
||||
|
||||
<!-- I'm not clear on why the clone trait uses arbitrary code but copy doesn't
|
||||
-- is this important to make clear? -->
|
||||
<!-- We discussed this in chapter 4; I've added a cross ref. /Carol -->
|
||||
|
||||
The `Copy` trait does not define any methods to prevent programmers from
|
||||
overloading those methods violating the assumption that no arbitrary code is
|
||||
being run. That way, all programmers can assume that copying a value will be
|
||||
very fast.
|
||||
|
||||
<!-- above -- I couldn't follow this either, what does that mean practically
|
||||
for the programmer? What does overloading methods that violate the assumption
|
||||
mean? -->
|
||||
<!-- I added a sentence at the end of the paragraph, does that clear it up?
|
||||
/Carol -->
|
||||
|
||||
You can derive `Copy` on any type whose parts all implement `Copy`. The `Copy`
|
||||
trait can only be applied to types that also implement `Clone`, as a type that
|
||||
implements `Copy` has a trivial implementation of `Clone`, doing the same thing
|
||||
as `Copy`.
|
||||
|
||||
`Copy` is rarely required; types implement `Copy` have optimizations available
|
||||
mean you don’t have to call `clone`, making the code more concise.
|
||||
|
||||
<!-- By "nicer" do you mean more efficient and understandable? -->
|
||||
<!-- concise, I've changed /Carol -->
|
||||
|
||||
Everything possible with `Copy` can also be accomplished with `Clone`, but the
|
||||
code might be slower or have to use `clone` in places.
|
||||
|
||||
### `Hash` for Mapping a Value to a Value of Fixed Size
|
||||
|
||||
The `Hash` trait allows you to take an instance of a type of arbitrary size and
|
||||
map that instance to a value of fixed size, using a hash function. Deriving
|
||||
`Hash` implements the `hash` method. The derived implementation of the `hash`
|
||||
method combines the result of calling `hash` on each of the parts of the type,
|
||||
meaning all fields or values must also implement `Hash` to derive `Hash`.
|
||||
|
||||
An example of when `Hash` is required is in storing keys in a `HashMap`, in
|
||||
order to store data efficiently.
|
||||
|
||||
### `Default` for Default Values
|
||||
|
||||
The `Default` trait allows you to create a default value for a type. Deriving
|
||||
`Default` implements the `default` method. The derived implementation of the
|
||||
`default` method calls the `default` method on each part of the type, meaning
|
||||
all fields or values in the type must also implement `Default` to derive
|
||||
`Default.`
|
||||
|
||||
`Default::default` is commonly used in combination with the struct update
|
||||
syntax discussed in the “Creating Instances From Other Instances With Struct
|
||||
Update Syntax” section in Chapter 5. You can customize a few fields of a struct
|
||||
and then set and use a default value for the rest of the fields by using
|
||||
`..Default::default()`.
|
||||
|
||||
`Default` is required when, for example, you use the `unwrap_or_default` method
|
||||
on `Option<T>` instances. If the `Option<T>` is `None`, the `unwrap_or_default`
|
||||
method will return the result of `Default::default` for the type `T` stored in
|
||||
the `Option<T>`.
|
||||
504
src/doc/book/2018-edition/src/appendix-04-macros.md
Normal file
504
src/doc/book/2018-edition/src/appendix-04-macros.md
Normal file
@ -0,0 +1,504 @@
|
||||
## Appendix D: Macros
|
||||
|
||||
We’ve used macros like `println!` throughout this book, but haven’t fully
|
||||
explored what a macro is and how it works. This appendix will explain:
|
||||
|
||||
- What macros are and how they differ from functions
|
||||
- How to define a declarative macro to do metaprogramming
|
||||
- How to define a procedural macro to create custom `derive` traits
|
||||
|
||||
We’re covering the details of macros in an appendix because they’re still
|
||||
evolving in Rust. Macros have changed and, in the near future, will change at a
|
||||
quicker rate than the rest of the language and standard library since Rust 1.0,
|
||||
so this section is more likely to date than the rest of the book. The code
|
||||
shown here will still continue to work with future versions, due to Rust’s
|
||||
stability guarantees, but there may be additional capabilities or easier ways
|
||||
to write macros that weren’t available at the time of this publication. Bear
|
||||
that in mind if you try to implement anything from this appendix.
|
||||
|
||||
### The Difference Between Macros and Functions
|
||||
|
||||
Fundamentally, macros are a way of writing code that writes other code, known
|
||||
as *metaprogramming*. In Appendix C, we discussed the `derive` attribute, which
|
||||
generates an implementation of various traits for you. We’ve also used the
|
||||
`println!` and `vec!` macros throughout the book. All of these macros *expand*
|
||||
to produce more code than the code you’ve written yourself.
|
||||
|
||||
Metaprogramming is useful for reducing the amount of code you have to write and
|
||||
maintain, which is also one of the roles of functions. However, macros have
|
||||
some additional powers that functions don’t have.
|
||||
|
||||
A function signature has to declare the number and type of parameters the
|
||||
function has. Macros, on the other hand, can take a variable number of
|
||||
parameters: we can call `println!("hello")` with one argument, or
|
||||
`println!("hello {}", name)` with two arguments. Also, macros are expanded
|
||||
before the compiler interprets the meaning of the code, so a macro can, for
|
||||
example, implement a trait on a given type. A function can’t, because it gets
|
||||
called at runtime and a trait needs to be implemented at compile time.
|
||||
|
||||
The downside to implementing a macro over a function is that macro definitions
|
||||
are more complex than function definitions because you’re writing Rust code
|
||||
that writes Rust code. Due to this indirection, macro definitions are generally
|
||||
more difficult to read, understand, and maintain than function definitions.
|
||||
|
||||
Another difference between macros and functions is that macro definitions
|
||||
aren’t namespaced within modules like function definitions are. In order to
|
||||
prevent unexpected name clashes when using external crates, you have to
|
||||
explicitly bring the macros into the scope of your project at the same time as
|
||||
bringing the external crate into scope, using the `#[macro_use]` annotation.
|
||||
The following example would bring all the macros defined in the `serde` crate
|
||||
into the scope of the current crate:
|
||||
|
||||
```rust,ignore
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
```
|
||||
|
||||
If `extern crate` was able to bring macros into scope by default without this
|
||||
explicit annotation, you would be prevented from using two crates that happened
|
||||
to define macros with the same name. In practice this conflict doesn’t come up
|
||||
much, but the more crates you use, the more likely it is.
|
||||
|
||||
One last important difference between macros and functions: macros must be
|
||||
defined or brought into scope *before* they’re called in a file, whereas
|
||||
functions can be defined anywhere and called anywhere.
|
||||
|
||||
### Declarative Macros with `macro_rules!` for General Metaprogramming
|
||||
|
||||
The most widely used form of macros in Rust are *declarative macros*. These are
|
||||
also sometimes referred to as *macros by example*, *`macro_rules!` macros*, or
|
||||
just plain *macros*. At their core, declarative macros allow you to write
|
||||
something similar to a Rust `match` expression. As discussed in Chapter 6,
|
||||
`match` expressions are control structures that take an expression, compare the
|
||||
resulting value of the expression to patterns, and then run the code associated
|
||||
with the matching pattern. Macros also compare a value to patterns that have
|
||||
code associated with them, but in this case the value is the literal Rust
|
||||
source code passed to the macro, the patterns are compared with the structure
|
||||
of that source code, and the code associated with each pattern is the code that
|
||||
replaces the code passed to the macro. This all happens during compilation.
|
||||
|
||||
To define a macro, you use the `macro_rules!` construct. Let’s explore how to
|
||||
use `macro_rules!` by taking a look at how the `vec!` macro is defined. Chapter
|
||||
8 covered how we can use the `vec!` macro to create a new vector with
|
||||
particular values. For example, this macro creates a new vector with three
|
||||
integers inside:
|
||||
|
||||
```rust
|
||||
let v: Vec<u32> = vec![1, 2, 3];
|
||||
```
|
||||
|
||||
We could also use the `vec!` macro to make a vector of two integers or a vector
|
||||
of five string slices---we wouldn’t be able to use a function to do the same
|
||||
because we wouldn’t know the number or type of values up front.
|
||||
|
||||
Let’s take a look at a slightly simplified definition of the `vec!` macro in
|
||||
Listing AD-1:
|
||||
|
||||
```rust
|
||||
#[macro_export]
|
||||
macro_rules! vec {
|
||||
( $( $x:expr ),* ) => {
|
||||
{
|
||||
let mut temp_vec = Vec::new();
|
||||
$(
|
||||
temp_vec.push($x);
|
||||
)*
|
||||
temp_vec
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Listing AD-1: A simplified version of the `vec!` macro definition
|
||||
|
||||
> Note: the actual definition of the `vec!` macro in the standard library
|
||||
> includes code to pre-allocate the correct amount of memory up-front. That
|
||||
> code is an optimization that we’ve chosen not to include here to make the
|
||||
> example simpler.
|
||||
|
||||
The `#[macro_export]` annotation indicates that this macro should be made
|
||||
available whenever the crate in which we’re defining the macro is imported.
|
||||
Without this annotation, even if someone depending on this crate uses the
|
||||
`#[macro_use]` annotation, the macro would not be brought into scope.
|
||||
|
||||
We then start the macro definition with `macro_rules!` and the name of the
|
||||
macro we’re defining *without* the exclamation mark. The name---in this case
|
||||
`vec`---is followed by curly brackets denoting the body of the macro definition.
|
||||
|
||||
The structure in the `vec!` body is similar to the structure of a `match`
|
||||
expression. Here we have one arm with the pattern `( $( $x:expr ),* )`,
|
||||
followed by `=>` and the block of code associated with this pattern. If the
|
||||
pattern matches, the associated block of code will be emitted. Given that this
|
||||
is the only pattern in this macro, there’s only one valid way to match; any
|
||||
other will be an error. More complex macros will have more than one arm.
|
||||
|
||||
Valid pattern syntax in macro definitions is different than the pattern syntax
|
||||
covered in Chapter 18 because macro patterns are matched against Rust code
|
||||
structure rather than values. Let’s walk through what the pieces of the pattern
|
||||
in Listing AD-1 mean; for the full macro pattern syntax, see [the reference].
|
||||
|
||||
[the reference]: ../../reference/macros.html
|
||||
|
||||
First, a set of parentheses encompasses the whole pattern. Next comes a dollar
|
||||
sign (`$`) followed by a set of parentheses, which captures values that match
|
||||
the pattern within the parentheses for use in the replacement code. Within
|
||||
`$()` is `$x:expr`, which matches any Rust expression and gives the expression
|
||||
the name `$x`.
|
||||
|
||||
The comma following `$()` indicates that a literal comma separator character
|
||||
could optionally appear after the code that matches the code captured in `$()`.
|
||||
The `*` following the comma specifies that the pattern matches zero or more of
|
||||
whatever precedes the `*`.
|
||||
|
||||
When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three
|
||||
times with the three expressions `1`, `2`, and `3`.
|
||||
|
||||
Now let’s look at the pattern in the body of the code associated with this arm:
|
||||
The `temp_vec.push()` code within the `$()*` part is generated for each part
|
||||
that matches `$()` in the pattern, zero or more times depending on how many
|
||||
times the pattern matches. The `$x` is replaced with each expression matched.
|
||||
When we call this macro with `vec![1, 2, 3];`, the code generated that replaces
|
||||
this macro call will be:
|
||||
|
||||
<!-- Above What about temp_vec.push, do you want to quickly mention that? Or do
|
||||
you mean "The `$()*` part and the content of the parentheses is generated for
|
||||
each part that matches `$()` in the pattern"-->
|
||||
<!-- The latter, I've tried to clarify /Carol -->
|
||||
|
||||
```rust,ignore
|
||||
let mut temp_vec = Vec::new();
|
||||
temp_vec.push(1);
|
||||
temp_vec.push(2);
|
||||
temp_vec.push(3);
|
||||
temp_vec
|
||||
```
|
||||
|
||||
We’ve defined a macro that can take any number of arguments of any type and can
|
||||
generate code to create a vector containing the specified elements.
|
||||
|
||||
Given that most Rust programmers will *use* macros more than *write* macros,
|
||||
that’s all we’ll discuss about `macro_rules!` in this book. To learn more about
|
||||
how to write macros, consult the online documentation or other resources such
|
||||
as [The Little Book of Rust Macros][tlborm].
|
||||
|
||||
[tlborm]: https://danielkeep.github.io/tlborm/book/index.html
|
||||
|
||||
### Procedural Macros for Custom `derive`
|
||||
|
||||
The second form of macros is called *procedural macros* because they’re more
|
||||
like functions (which are a type of procedure). Procedural macros accept some
|
||||
Rust code as an input, operate on that code, and produce some Rust code as an
|
||||
output, rather than matching against patterns and replacing the code with other
|
||||
code as declarative macros do. At the time of writing, you can only really
|
||||
define procedural macros to allow your traits to be implemented on a type by
|
||||
specifying the trait name in a `derive` annotation.
|
||||
|
||||
We’re going to create a crate named `hello_macro` that defines a trait named
|
||||
`HelloMacro` with one associated function named `hello_macro`. Rather than
|
||||
making users of our crate implement the `HelloMacro` trait for each of their
|
||||
types, we’ll provide a procedural macro so users can annotate their type with
|
||||
`#[derive(HelloMacro)]` to get a default implementation of the `hello_macro`
|
||||
function. The default implementation will print `Hello, Macro! My name is
|
||||
TypeName!` where `TypeName` is the name of the type on which this trait has
|
||||
been defined.
|
||||
|
||||
In other words, we’re going to write a crate that enables another programmer to
|
||||
write code like Listing AD-2 using our crate:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
extern crate hello_macro;
|
||||
#[macro_use]
|
||||
extern crate hello_macro_derive;
|
||||
|
||||
use hello_macro::HelloMacro;
|
||||
|
||||
#[derive(HelloMacro)]
|
||||
struct Pancakes;
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_macro();
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing AD-2: The code a user of our crate will be able
|
||||
to write with use of our procedural macro</span>
|
||||
|
||||
This code will print `Hello, Macro! My name is Pancakes!` when we’re done. Let’s
|
||||
get started! First we need to make a new library crate:
|
||||
|
||||
```text
|
||||
$ cargo new hello_macro --lib
|
||||
```
|
||||
|
||||
Now we’ll define the `HelloMacro` trait and its associated function:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
pub trait HelloMacro {
|
||||
fn hello_macro();
|
||||
}
|
||||
```
|
||||
|
||||
We have a trait and its function. At this point, a user of our crate would be
|
||||
able to implement the trait themselves to achieve the desired functionality,
|
||||
like so:
|
||||
|
||||
```rust,ignore
|
||||
extern crate hello_macro;
|
||||
|
||||
use hello_macro::HelloMacro;
|
||||
|
||||
struct Pancakes;
|
||||
|
||||
impl HelloMacro for Pancakes {
|
||||
fn hello_macro() {
|
||||
println!("Hello, Macro! My name is Pancakes!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_macro();
|
||||
}
|
||||
```
|
||||
|
||||
However, they would need to write out the implementation block for each type
|
||||
they wanted to use with `hello_macro`; we’d like to save them this work.
|
||||
|
||||
Additionally, we can’t yet provide a default implementation for the
|
||||
`hello_macro` function that will print out the name of the type the trait is
|
||||
implemented on: Rust doesn’t have reflection capabilities, so can’t look up the
|
||||
type’s name at runtime. We need a macro to generate code at compile time.
|
||||
|
||||
<!--Defining Procedural Macros Requires a Separate Crate--> <!-- Since this is
|
||||
a lone subheading, okay to merge with the general procedural macros section? -->
|
||||
<!-- Sure /Carol -->
|
||||
|
||||
The next step is to define the procedural macro. At the time of writing,
|
||||
procedural macros need to be in their own crate. Eventually, this restriction
|
||||
may be lifted. The convention for structuring crates and macro crates is as
|
||||
such: for a crate named `foo`, a custom derive procedural macro crate is called
|
||||
`foo-derive`. Let’s start a new crate called `hello_macro_derive` inside our
|
||||
`hello_macro` project:
|
||||
|
||||
```text
|
||||
$ cargo new hello_macro_derive --lib
|
||||
```
|
||||
|
||||
Our two crates are tightly related, so we create the procedural macro crate
|
||||
within the directory of our `hello_macro` crate. If we change the trait
|
||||
definition in `hello_macro`, we’ll have to change the implementation of the
|
||||
procedural macro in `hello_macro_derive` as well. The two crates will need to
|
||||
be published separately, though, and programmers using these crates will need
|
||||
to add both as dependencies and bring them both into scope. We could instead
|
||||
have the `hello_macro` crate use `hello_macro_derive` as a dependency and
|
||||
re-export the procedural macro code, but the way we’ve structured the project
|
||||
makes it possible for programmers to use `hello_macro` even if they don’t want
|
||||
the `derive` functionality.
|
||||
|
||||
We need to declare the `hello_macro_derive` crate as a procedural macro crate.
|
||||
We’ll also need functionality from the `syn` and `quote` crates, as we’ll see
|
||||
in a moment, so we need to add them as dependencies. Add the following to the
|
||||
*Cargo.toml* file for `hello_macro_derive`:
|
||||
|
||||
<span class="filename">Filename: hello_macro_derive/Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.11.11"
|
||||
quote = "0.3.15"
|
||||
```
|
||||
|
||||
To start defining the procedural macro, place the code from Listing AD-3 in
|
||||
your *src/lib.rs* for the `hello_macro_derive` crate. Note that this won’t
|
||||
compile until we add a definition for the `impl_hello_macro` function.
|
||||
|
||||
Note the way we’ve split the functions in AD-3; this will be the same for
|
||||
almost every procedural macro crate you see or create, as it makes writing a
|
||||
procedural macro more convenient. What you choose to do in the place where the
|
||||
`impl_hello_macro` function is called will be different depending on the
|
||||
purpose of your procedural macro.
|
||||
|
||||
<span class="filename">Filename: hello_macro_derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_derive(HelloMacro)]
|
||||
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let s = input.to_string();
|
||||
|
||||
// Parse the string representation
|
||||
let ast = syn::parse_derive_input(&s).unwrap();
|
||||
|
||||
// Build the impl
|
||||
let gen = impl_hello_macro(&ast);
|
||||
|
||||
// Return the generated impl
|
||||
gen.parse().unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing AD-3: Code that most procedural macro crates will
|
||||
need to have for processing Rust code</span>
|
||||
|
||||
We have introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The
|
||||
`proc_macro` crate comes with Rust, so we didn’t need to add that to the
|
||||
dependencies in *Cargo.toml*. The `proc_macro` crate allows us to convert Rust
|
||||
code into a string containing that Rust code. The `syn` crate parses Rust code
|
||||
from a string into a data structure that we can perform operations on. The
|
||||
`quote` crate takes `syn` data structures and turns them back into Rust code.
|
||||
These crates make it much simpler to parse any sort of Rust code we might want
|
||||
to handle: writing a full parser for Rust code is no simple task.
|
||||
|
||||
[`syn`]: https://crates.io/crates/syn
|
||||
[`quote`]: https://crates.io/crates/quote
|
||||
|
||||
The `hello_macro_derive` function will get called when a user of our library
|
||||
specifies `#[derive(HelloMacro)]` on a type, because we’ve annotated the
|
||||
`hello_macro_derive` function here with `proc_macro_derive` and specified the
|
||||
name, `HelloMacro`, which matches our trait name; that’s the convention most
|
||||
procedural macros follow.
|
||||
|
||||
This function first converts the `input` from a `TokenStream` to a `String` by
|
||||
calling `to_string`. This `String` is a string representation of the Rust code
|
||||
for which we are deriving `HelloMacro`. In the example in Listing AD-2, `s`
|
||||
will have the `String` value `struct Pancakes;` because that’s the Rust code we
|
||||
added the `#[derive(HelloMacro)]` annotation to.
|
||||
|
||||
<!-- I'm not sure why we convert to a string then to a structure we can use,
|
||||
will that be clear to the reader here? -->
|
||||
<!-- This is just how procedural macros work and what you have to do, which is
|
||||
why we have the next note. /Carol -->
|
||||
|
||||
> Note: At the time of writing, the only thing you can do with a `TokenStream`
|
||||
> is convert it to a string. A richer API will exist in the future.
|
||||
|
||||
Now we need to be able to parse the Rust code `String` into a data structure
|
||||
that we can then interpret and perform operations on. This is where `syn` comes
|
||||
to play. The `parse_derive_input` function in `syn` takes a `String` and
|
||||
returns a `DeriveInput` struct representing the parsed Rust code. The following
|
||||
shows the relevant parts of the `DeriveInput` struct we get from parsing the
|
||||
string `struct Pancakes;`:
|
||||
|
||||
```rust,ignore
|
||||
DeriveInput {
|
||||
// --snip--
|
||||
|
||||
ident: Ident(
|
||||
"Pancakes"
|
||||
),
|
||||
body: Struct(
|
||||
Unit
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The fields of this struct show that the Rust code we’ve parsed is a unit struct
|
||||
with the `ident` (identifier, meaning the name) of `Pancakes`. There are more
|
||||
fields on this struct for describing all sorts of Rust code; check the [`syn`
|
||||
API docs for `DeriveInput`][syn-docs] for more information.
|
||||
|
||||
[syn-docs]: https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html
|
||||
|
||||
At this point we haven’t defined the `impl_hello_macro` function, which is
|
||||
where we’ll build the new Rust code we want to include. Before we get to that,
|
||||
the last part of this `hello_macro_derive` function is using the `parse`
|
||||
function from the `quote` crate to turn the output of the `impl_hello_macro`
|
||||
function back into a `TokenStream`. The returned `TokenStream` is added to the
|
||||
code that users of our crate write so that when they compile their crate, they
|
||||
get extra functionality we provide.
|
||||
|
||||
You may have noticed that we’re calling `unwrap` to panic if the calls to the
|
||||
`parse_derive_input` or `parse` functions fail here. Panicking on errors is
|
||||
necessary in procedural macro code because `proc_macro_derive` functions must
|
||||
return `TokenStream` rather than `Result` in order to conform to the procedural
|
||||
macro API. We’ve chosen to keep this example simple by using `unwrap`; in
|
||||
production code you should provide more specific error messages about what went
|
||||
wrong by using `expect` or `panic!`.
|
||||
|
||||
Now that we have the code to turn the annotated Rust code from a `TokenStream`
|
||||
into a `String` and a `DeriveInput` instance, let’s generate the code
|
||||
implementing the `HelloMacro` trait on the annotated type:
|
||||
|
||||
<span class="filename">Filename: hello_macro_derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn impl_hello_macro(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let name = &ast.ident;
|
||||
quote! {
|
||||
impl HelloMacro for #name {
|
||||
fn hello_macro() {
|
||||
println!("Hello, Macro! My name is {}", stringify!(#name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We get an `Ident` struct instance containing the name (identifier) of the
|
||||
annotated type using `ast.ident`. With the code from Listing AD-2, `name` will
|
||||
be `Ident("Pancakes")`.
|
||||
|
||||
The `quote!` macro lets us write up the Rust code that we want to return and
|
||||
convert it into `quote::Tokens`. This macro also provides some really cool
|
||||
templating mechanics; we can write `#name` and `quote!` will replace it with
|
||||
the value in the variable named `name`. You can even do some repetition similar
|
||||
to the way regular macros work. Check out [the `quote` crate’s
|
||||
docs][quote-docs] for a thorough introduction.
|
||||
|
||||
[quote-docs]: https://docs.rs/quote
|
||||
|
||||
We want our procedural macro to generate an implementation of our `HelloMacro`
|
||||
trait for the type the user annotated, which we can get by using `#name`. The
|
||||
trait implementation has one function, `hello_macro`, whose body contains the
|
||||
functionality we want to provide: printing `Hello, Macro! My name is` and then
|
||||
the name of the annotated type.
|
||||
|
||||
The `stringify!` macro used here is built into Rust. It takes a Rust
|
||||
expression, such as `1 + 2`, and at compile time turns the expression into a
|
||||
string literal, such as `"1 + 2"`. This is different than `format!` or
|
||||
`println!`, which evaluate the expression and then turn the result into a
|
||||
`String`. There’s a possibility that the `#name` input might be an expression
|
||||
to print out literally so we use `stringify!`. Using `stringify!` also saves an
|
||||
allocation by converting `#name` to a string literal at compile time.
|
||||
|
||||
At this point, `cargo build` should complete successfully in both `hello_macro`
|
||||
and `hello_macro_derive`. Let’s hook these crates up to the code in Listing
|
||||
AD-2 to see it in action! Create a new binary project in your `projects`
|
||||
directory with `cargo new --bin pancakes`. We need to add both `hello_macro`
|
||||
and `hello_macro_derive` as dependencies in the `pancakes` crate’s
|
||||
*Cargo.toml*. If you’ve chosen to publish your versions of `hello_macro` and
|
||||
`hello_macro_derive` to *https://crates.io* they would be regular dependencies;
|
||||
if not, you can specify them as `path` dependencies as follows:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
hello_macro = { path = "../hello_macro" }
|
||||
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
|
||||
```
|
||||
|
||||
Put the code from Listing AD-2 into *src/main.rs*, and when you run `cargo run`
|
||||
it should print `Hello, Macro! My name is Pancakes!` The implementation of the
|
||||
`HelloMacro` trait from the procedural macro was included without the
|
||||
`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` took care
|
||||
of adding the trait implementation.
|
||||
|
||||
### The Future of Macros
|
||||
|
||||
In the future, we’ll be expanding both declarative and procedural macros. Rust
|
||||
will use better declarative macro system with the `macro` keyword, and we’ll
|
||||
add more types of procedural macros, for more powerful tasks than just
|
||||
`derive`. These systems are still under development at the time of publication;
|
||||
please consult the online Rust documentation for the latest information.
|
||||
22
src/doc/book/2018-edition/src/appendix-05-translation.md
Normal file
22
src/doc/book/2018-edition/src/appendix-05-translation.md
Normal file
@ -0,0 +1,22 @@
|
||||
## Appendix E: Translations of the Book
|
||||
|
||||
For resources in languages other than English. Most are still in progress; see
|
||||
[the Translations label][label] to help or let us know about a new translation!
|
||||
|
||||
[label]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations
|
||||
|
||||
- [Português](https://github.com/rust-br/rust-book-pt-br) (BR)
|
||||
- [Português](https://github.com/nunojesus/rust-book-pt-pt) (PT)
|
||||
- [Tiếng việt](https://github.com/hngnaig/rust-lang-book/tree/vi-VN)
|
||||
- [简体中文](http://www.broadview.com.cn/article/144), [alternate](https://github.com/KaiserY/trpl-zh-cn)
|
||||
- [Українська](https://github.com/pavloslav/rust-book-uk-ua)
|
||||
- [Español](https://github.com/thecodix/book)
|
||||
- [Italiano](https://github.com/CodelessFuture/trpl2-it)
|
||||
- [Русский](https://github.com/iDeBugger/rust-book-ru)
|
||||
- [한국어](https://github.com/rinthel/rust-lang-book-ko)
|
||||
- [日本語](https://github.com/hazama-yuinyan/book)
|
||||
- [Français](https://github.com/quadrifoglio/rust-book-fr)
|
||||
- [Polski](https://github.com/paytchoo/book-pl)
|
||||
- [עברית](https://github.com/idanmel/rust-book-heb)
|
||||
- [Cebuano](https://github.com/agentzero1/book)
|
||||
- [Tagalog](https://github.com/josephace135/book)
|
||||
128
src/doc/book/2018-edition/src/appendix-06-newest-features.md
Normal file
128
src/doc/book/2018-edition/src/appendix-06-newest-features.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Appendix F - Newest Features
|
||||
|
||||
This appendix documents features that have been added to stable Rust since the
|
||||
main part of the book was completed.
|
||||
|
||||
|
||||
## Field init shorthand
|
||||
|
||||
We can initialize a data structure (struct, enum, union) with named
|
||||
fields, by writing `fieldname` as a shorthand for `fieldname: fieldname`.
|
||||
This allows a compact syntax for initialization, with less duplication:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let name = String::from("Peter");
|
||||
let age = 27;
|
||||
|
||||
// Using full syntax:
|
||||
let peter = Person { name: name, age: age };
|
||||
|
||||
let name = String::from("Portia");
|
||||
let age = 27;
|
||||
|
||||
// Using field init shorthand:
|
||||
let portia = Person { name, age };
|
||||
|
||||
println!("{:?}", portia);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Returning from loops
|
||||
|
||||
One of the uses of a `loop` is to retry an operation you know can fail, such as
|
||||
checking if a thread completed its job. However, you might need to pass the
|
||||
result of that operation to the rest of your code. If you add it to the `break`
|
||||
expression you use to stop the loop, it will be returned by the broken loop:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut counter = 0;
|
||||
|
||||
let result = loop {
|
||||
counter += 1;
|
||||
|
||||
if counter == 10 {
|
||||
break counter * 2;
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(result, 20);
|
||||
}
|
||||
```
|
||||
|
||||
## Nested groups in `use` declarations
|
||||
|
||||
If you have a complex module tree with many different submodules and you need
|
||||
to import a few items from each one, it might be useful to group all the
|
||||
imports in the same declaration to keep your code clean and avoid repeating the
|
||||
base modules’ name.
|
||||
|
||||
The `use` declaration supports nesting to help you in those cases, both with
|
||||
simple imports and glob ones. For example this snippets imports `bar`, `Foo`,
|
||||
all the items in `baz` and `Bar`:
|
||||
|
||||
```rust
|
||||
# #![allow(unused_imports, dead_code)]
|
||||
#
|
||||
# mod foo {
|
||||
# pub mod bar {
|
||||
# pub type Foo = ();
|
||||
# }
|
||||
# pub mod baz {
|
||||
# pub mod quux {
|
||||
# pub type Bar = ();
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
use foo::{
|
||||
bar::{self, Foo},
|
||||
baz::{*, quux::Bar},
|
||||
};
|
||||
#
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
## Inclusive ranges
|
||||
|
||||
Previously, when a range (`..` or `...`) was used as an expression, it had to be
|
||||
`..`, which is exclusive of the upper bound, while patterns had to use `...`,
|
||||
which is inclusive of the upper bound. Now, `..=` is accepted as syntax for
|
||||
inclusive ranges in both expression and range context:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
for i in 0 ..= 10 {
|
||||
match i {
|
||||
0 ..= 5 => println!("{}: low", i),
|
||||
6 ..= 10 => println!("{}: high", i),
|
||||
_ => println!("{}: out of range", i),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `...` syntax is still accepted in matches, but it is not accepted in
|
||||
expressions. `..=` should be preferred.
|
||||
|
||||
## 128-bit integers
|
||||
|
||||
Rust 1.26.0 added 128-bit integer primitives:
|
||||
|
||||
- `u128`: A 128-bit unsigned integer with range [0, 2^128 - 1]
|
||||
- `i128`: A 128-bit signed integer with range [-(2^127), 2^127 - 1]
|
||||
|
||||
These primitives are implemented efficiently via LLVM support. They are
|
||||
available even on platforms that don’t natively support 128-bit integers and
|
||||
can be used like the other integer types.
|
||||
|
||||
These primitives can be very useful for algorithms that need to use very large
|
||||
integers efficiently, such as certain cryptographic algorithms.
|
||||
204
src/doc/book/2018-edition/src/appendix-07-nightly-rust.md
Normal file
204
src/doc/book/2018-edition/src/appendix-07-nightly-rust.md
Normal file
@ -0,0 +1,204 @@
|
||||
# Appendix G - How Rust is Made and “Nightly Rust”
|
||||
|
||||
This appendix is about how Rust is made and how that affects you as a Rust
|
||||
developer. We mentioned that the output in this book was generated by stable
|
||||
Rust 1.21.0, but any examples that compile should continue to compile in any
|
||||
stable version of Rust greater than that. This section is to explain how we
|
||||
ensure this is true!
|
||||
|
||||
### Stability Without Stagnation
|
||||
|
||||
As a language, Rust cares a *lot* about the stability of your code. We want
|
||||
Rust to be a rock-solid foundation you can build on, and if things were
|
||||
constantly changing, that would be impossible. At the same time, if we can’t
|
||||
experiment with new features, we may not find out important flaws until after
|
||||
their release, when we can no longer change things.
|
||||
|
||||
Our solution to this problem is what we call “stability without stagnation”,
|
||||
and our guiding principle is this: you should never have to fear upgrading to a
|
||||
new version of stable Rust. Each upgrade should be painless, but should also
|
||||
bring you new features, fewer bugs, and faster compile times.
|
||||
|
||||
### Choo, Choo! Release Channels and Riding the Trains
|
||||
|
||||
Rust development operates on a *train schedule*. That is, all development is
|
||||
done on the `master` branch of the Rust repository. Releases follow a software
|
||||
release train model, which has been used by Cisco IOS and other software
|
||||
projects. There are three *release channels* for Rust:
|
||||
|
||||
* Nightly
|
||||
* Beta
|
||||
* Stable
|
||||
|
||||
Most Rust developers primarily use the stable channel, but those who want to
|
||||
try out experimental new features may use nightly or beta.
|
||||
|
||||
Here’s an example of how the development and release process works: let’s
|
||||
assume that the Rust team is working on the release of Rust 1.5. That release
|
||||
happened in December of 2015, but it will provide us with realistic version
|
||||
numbers. A new feature is added to Rust: a new commit lands on the `master`
|
||||
branch. Each night, a new nightly version of Rust is produced. Every day is a
|
||||
release day, and these releases are created by our release infrastructure
|
||||
automatically. So as time passes, our releases look like this, once a night:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - *
|
||||
```
|
||||
|
||||
Every six weeks, it’s time to prepare a new release! The `beta` branch of the
|
||||
Rust repository branches off from the `master` branch used by nightly. Now,
|
||||
there are two releases:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - *
|
||||
|
|
||||
beta: *
|
||||
```
|
||||
|
||||
Most Rust users do not use beta releases actively, but test against beta in
|
||||
their CI system to help Rust discover possible regressions. In the meantime,
|
||||
there’s still a nightly release every night:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - * - - * - - *
|
||||
|
|
||||
beta: *
|
||||
```
|
||||
|
||||
Let’s say a regression is found. Good thing we had some time to test the beta
|
||||
release before the regression snuck into a stable release! The fix is applied
|
||||
to `master`, so that nightly is fixed, and then the fix is backported to the
|
||||
`beta` branch, and a new release of beta is produced:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - * - - * - - * - - *
|
||||
|
|
||||
beta: * - - - - - - - - *
|
||||
```
|
||||
|
||||
Six weeks after the first beta was created, it’s time for a stable release! The
|
||||
`stable` branch is produced from the `beta` branch:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - * - - * - - * - - * - * - *
|
||||
|
|
||||
beta: * - - - - - - - - *
|
||||
|
|
||||
stable: *
|
||||
```
|
||||
|
||||
Hooray! Rust 1.5 is done! However, we’ve forgotten one thing: because the six
|
||||
weeks have gone by, we also need a new beta of the *next* version of Rust, 1.6.
|
||||
So after `stable` branches off of `beta`, the next version of `beta` branches
|
||||
off of `nightly` again:
|
||||
|
||||
```text
|
||||
nightly: * - - * - - * - - * - - * - - * - * - *
|
||||
| |
|
||||
beta: * - - - - - - - - * *
|
||||
|
|
||||
stable: *
|
||||
```
|
||||
|
||||
This is called the “train model” because every six weeks, a release “leaves the
|
||||
station”, but still has to take a journey through the beta channel before it
|
||||
arrives as a stable release.
|
||||
|
||||
Rust releases every six weeks, like clockwork. If you know the date of one Rust
|
||||
release, you can know the date of the next one: it’s six weeks later. A nice
|
||||
aspect of having releases scheduled every six weeks is that the next train is
|
||||
coming soon. If a feature happens to miss a particular release, there’s no need
|
||||
to worry: another one is happening in a short time! This helps reduce pressure
|
||||
to sneak possibly unpolished features in close to the release deadline.
|
||||
|
||||
Thanks to this process, you can always check out the next build of Rust and
|
||||
verify for yourself that it’s easy to upgrade to: if a beta release doesn’t
|
||||
work as expected, you can report it to the team and get it fixed before the
|
||||
next stable release happens! Breakage in a beta release is relatively rare, but
|
||||
`rustc` is still a piece of software, and bugs do exist.
|
||||
|
||||
### Unstable Features
|
||||
|
||||
There’s one more catch with this release model: unstable features. Rust uses a
|
||||
technique called “feature flags” to determine what features are enabled in a
|
||||
given release. If a new feature is under active development, it lands on
|
||||
`master`, and therefore, in nightly, but behind a *feature flag*. If you, as a
|
||||
user, wish to try out the work-in-progress feature, you can, but you must be
|
||||
using a nightly release of Rust and annotate your source code with the
|
||||
appropriate flag to opt in.
|
||||
|
||||
If you’re using a beta or stable release of Rust, you can’t use any feature
|
||||
flags. This is the key that allows us to get practical use with new features
|
||||
before we declare them stable forever. Those who wish to opt into the bleeding
|
||||
edge can do so, and those who want a rock-solid experience can stick with
|
||||
stable and know that their code won’t break. Stability without stagnation.
|
||||
|
||||
This book only contains information about stable features, as in-progress
|
||||
features are still changing, and surely they’ll be different between when this
|
||||
book was written and when they get enabled in stable builds. You can find
|
||||
documentation for nightly-only features online.
|
||||
|
||||
### Rustup and the Role of Rust Nightly
|
||||
|
||||
Rustup makes it easy to change between different release channels of Rust, on a
|
||||
global or per-project basis. By default, you’ll have stable Rust installed. To
|
||||
install nightly, for example:
|
||||
|
||||
```text
|
||||
$ rustup install nightly
|
||||
```
|
||||
|
||||
You can see all of the *toolchains* (releases of Rust and associated
|
||||
components) you have installed with `rustup` as well. Here’s an example on one
|
||||
of your authors’ computers:
|
||||
|
||||
```powershell
|
||||
> rustup toolchain list
|
||||
stable-x86_64-pc-windows-msvc (default)
|
||||
beta-x86_64-pc-windows-msvc
|
||||
nightly-x86_64-pc-windows-msvc
|
||||
```
|
||||
|
||||
As you can see, the stable toolchain is the default. Most Rust users use stable
|
||||
most of the time. You might want to use stable most of the time, but use
|
||||
nightly on a specific project, because you care about a cutting-edge feature.
|
||||
To do so, you can use `rustup override` in that project’s directory to set the
|
||||
nightly toolchain as the one `rustup` should use when you’re in that directory:
|
||||
|
||||
```text
|
||||
$ cd ~/projects/needs-nightly
|
||||
$ rustup override set nightly
|
||||
```
|
||||
|
||||
Now, every time you call `rustc` or `cargo` inside of
|
||||
*~/projects/needs-nightly*, `rustup` will make sure that you are using nightly
|
||||
Rust, rather than your default of stable Rust. This comes in handy when you
|
||||
have a lot of Rust projects!
|
||||
|
||||
### The RFC Process and Teams
|
||||
|
||||
So how do you learn about these new features? Rust’s development model follows
|
||||
a *Request For Comments (RFC) process*. If you’d like an improvement in Rust,
|
||||
you can write up a proposal, called an RFC.
|
||||
|
||||
Anyone can write RFCs to improve Rust, and the proposals are reviewed and
|
||||
discussed by the Rust team, which is comprised of many topic subteams. There’s
|
||||
a full list of the teams [on Rust’s
|
||||
website](https://www.rust-lang.org/en-US/team.html), which includes teams for
|
||||
each area of the project: language design, compiler implementation,
|
||||
infrastructure, documentation, and more. The appropriate team reads the
|
||||
proposal and the comments, writes some comments of their own, and eventually,
|
||||
there’s consensus to accept or reject the feature.
|
||||
|
||||
If the feature is accepted, an issue is opened on the Rust repository, and
|
||||
someone can implement it. The person who implements it very well may not be the
|
||||
person who proposed the feature in the first place! When the implementation is
|
||||
ready, it lands on the `master` branch behind a feature gate, as we discussed
|
||||
in the “Unstable Features” section.
|
||||
|
||||
After some time, once Rust developers who use nightly releases have been able
|
||||
to try out the new feature, team members will discuss the feature, how it’s
|
||||
worked out on nightly, and decide if it should make it into stable Rust or not.
|
||||
If the decision is to move forward, the feature gate is removed, and the
|
||||
feature is now considered stable! It rides the trains into a new stable release
|
||||
of Rust.
|
||||
173
src/doc/book/2018-edition/src/ch00-00-introduction.md
Normal file
173
src/doc/book/2018-edition/src/ch00-00-introduction.md
Normal file
@ -0,0 +1,173 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to *The Rust Programming Language*, an introductory book about Rust.
|
||||
|
||||
The Rust programming language helps you write faster, more reliable software.
|
||||
High-level ergonomics and low-level control are often at odds in programming
|
||||
language design; Rust challenges that conflict. Through balancing powerful
|
||||
technical capacity and a great developer experience, Rust gives you the option
|
||||
to control low-level details (such as memory usage) without all the hassle
|
||||
traditionally associated with such control.
|
||||
|
||||
## Who Rust Is For
|
||||
|
||||
Rust is ideal for many people for a variety of reasons. Let’s look at a few of
|
||||
the most important groups.
|
||||
|
||||
### Teams of Developers
|
||||
|
||||
Rust is proving to be a productive tool for collaborating among large teams of
|
||||
developers with varying levels of systems programming knowledge. Low-level code
|
||||
is prone to a variety of subtle bugs, which in most other languages can only be
|
||||
caught through extensive testing and careful code review by experienced
|
||||
developers. In Rust, the compiler plays a gatekeeper role by refusing to
|
||||
compile code with these elusive bugs, including concurrency bugs. By working
|
||||
alongside the compiler, the team can spend more time focusing on the program’s
|
||||
logic rather than chasing down bugs.
|
||||
|
||||
Rust also brings contemporary developer tools to the systems programming world:
|
||||
|
||||
* Cargo, the included dependency manager and build tool, makes adding,
|
||||
compiling, and managing dependencies painless and consistent across the Rust
|
||||
ecosystem.
|
||||
* Rustfmt ensures a consistent coding style across developers.
|
||||
* The Rust Language Server powers Integrated Development Environment (IDE)
|
||||
integration for code completion and inline error messages.
|
||||
|
||||
By using these and other tools in the Rust ecosystem, developers can be
|
||||
productive while writing systems-level code.
|
||||
|
||||
### Students
|
||||
|
||||
Rust is for students and those who are interested in learning about systems
|
||||
concepts. Using Rust, many people have learned about topics like operating
|
||||
systems development. The community is very welcoming and happy to answer
|
||||
student questions. Through efforts such as this book, the Rust teams want to
|
||||
make systems concepts more accessible to more people, especially those new to
|
||||
programming.
|
||||
|
||||
### Companies
|
||||
|
||||
Hundreds of companies, large and small, use Rust in production for a variety of
|
||||
tasks. Those tasks include command line tools, web services, DevOps tooling,
|
||||
embedded devices, audio and video analysis and transcoding, cryptocurrencies,
|
||||
bioinformatics, search engines, internet of things applications, machine
|
||||
learning, and even major parts of the Firefox web browser.
|
||||
|
||||
### Open Source Developers
|
||||
|
||||
Rust is for people who want to build the Rust programming language, community,
|
||||
developer tools, and libraries. We’d love to have you contribute to the Rust
|
||||
language.
|
||||
|
||||
### People Who Value Speed and Stability
|
||||
|
||||
Rust is for people who crave speed and stability in a language. By speed, we
|
||||
mean the speed of the programs that you can create with Rust and the speed at
|
||||
which Rust lets you write them. The Rust compiler’s checks ensure stability
|
||||
through feature additions and refactoring as opposed to brittle legacy code in
|
||||
languages without these checks that developers are afraid to modify. By
|
||||
striving for zero-cost abstractions, higher-level features that compile to
|
||||
lower-level code as fast as code written manually, Rust endeavors to make safe
|
||||
code be fast code as well.
|
||||
|
||||
Although we’ve not provided a complete list of everyone the Rust language hopes
|
||||
to support, those we have mentioned are some of the biggest stakeholders.
|
||||
Overall, Rust’s greatest ambition is to eliminate the dichotomy of the
|
||||
trade-offs that programmers have accepted for decades: safety *and*
|
||||
productivity, speed *and* ergonomics. Give Rust a try, and see if its choices
|
||||
work for you.
|
||||
|
||||
## Who This Book Is For
|
||||
|
||||
This book assumes that you’ve written code in another programming language but
|
||||
doesn’t make any assumptions about which one. We’ve tried to make the material
|
||||
broadly accessible to those from a wide variety of programming backgrounds. We
|
||||
don’t spend a lot of time talking about what programming *is* or how to think
|
||||
about it. If you’re entirely new to programming, you would be better served by
|
||||
reading a book that specifically provides an introduction to programming.
|
||||
|
||||
## How to Use This Book
|
||||
|
||||
In general, this book assumes that you’re reading it in sequence from front to
|
||||
back. Later chapters build on concepts in earlier chapters, and earlier
|
||||
chapters might not delve into details on a topic; we typically revisit the
|
||||
topic in a later chapter.
|
||||
|
||||
You’ll find two kinds of chapters in this book: concept chapters and project
|
||||
chapters. In concept chapters, you’ll learn about an aspect of Rust. In project
|
||||
chapters, we’ll build small programs together, applying what you’ve learned so
|
||||
far. Chapters 2, 12, and 20 are project chapters; the rest are concept chapters.
|
||||
|
||||
Additionally, Chapter 2 is a hands-on introduction to the Rust language. We’ll
|
||||
cover concepts at a high level, and later chapters will provide additional
|
||||
detail. If you want to get your hands dirty right away, Chapter 2 is the one
|
||||
for that. At first, you might even want to skip Chapter 3, which covers Rust
|
||||
features similar to other programming language features, and head straight to
|
||||
Chapter 4 to learn about Rust’s ownership system. However, if you’re a
|
||||
particularly meticulous learner who prefers to learn every detail before moving
|
||||
onto the next, you might want to skip Chapter 2 and go straight to Chapter 3,
|
||||
returning to Chapter 2 when you’d like to work on a project applying those
|
||||
details.
|
||||
|
||||
Chapter 5 discusses structs and methods, and Chapter 6 covers enums, `match`
|
||||
expressions, and the `if let` control flow construct. You’ll use structs and
|
||||
enums to make custom types in Rust.
|
||||
|
||||
In Chapter 7, you’ll learn about Rust’s module system and about privacy rules
|
||||
for organizing your code and its public Application Programming Interface
|
||||
(API). Chapter 8 discusses some common collection data structures that the
|
||||
standard library provides, such as vectors, strings, and hash maps. Chapter 9
|
||||
explores Rust’s error handling philosophy and techniques.
|
||||
|
||||
Chapter 10 digs into generics, traits, and lifetimes, which give you the power
|
||||
to define code that applies to multiple types. Chapter 11 is all about testing,
|
||||
which is still necessary even with Rust’s safety guarantees to ensure your
|
||||
program’s logic is correct. In Chapter 12, we’ll build our own implementation
|
||||
of a subset of functionality from the `grep` command line tool that searches
|
||||
for text within files. For this, we’ll use many of the concepts we discussed in
|
||||
the previous chapters.
|
||||
|
||||
Chapter 13 explores closures and iterators: features of Rust that come from
|
||||
functional programming languages. In Chapter 14, we’ll examine Cargo in more
|
||||
depth and talk about best practices for sharing your libraries with others.
|
||||
Chapter 15 discusses smart pointers that the standard library provides and the
|
||||
traits that enable their functionality.
|
||||
|
||||
In Chapter 16, we’ll walk through different models of concurrent programming
|
||||
and talk about how Rust helps you to program in multiple threads fearlessly.
|
||||
Chapter 17 looks at how Rust idioms compare to object oriented programming
|
||||
principles you might be familiar with.
|
||||
|
||||
Chapter 18 is a reference on patterns and pattern matching, which are powerful
|
||||
ways of expressing ideas throughout Rust programs. Chapter 19 contains a
|
||||
smorgasbord of advanced topics of interest, including unsafe Rust and more
|
||||
about lifetimes, traits, types, functions, and closures.
|
||||
|
||||
In Chapter 20, we’ll complete a project in which we’ll implement a low-level
|
||||
multithreaded web server!
|
||||
|
||||
Finally, some appendixes contain useful information about the language in a
|
||||
more reference-like format. Appendix A covers Rust’s keywords. Appendix B
|
||||
covers Rust’s operators and symbols. Appendix C covers derivable traits
|
||||
provided by the standard library. Appendix D covers macros.
|
||||
|
||||
There is no wrong way to read this book: if you want to skip ahead, go for it!
|
||||
You might have to jump back to earlier chapters if you experience any
|
||||
confusion. But do whatever works for you.
|
||||
|
||||
An important part of the process of learning Rust is learning how to read the
|
||||
error messages the compiler displays: these will guide you toward working code.
|
||||
As such, we’ll provide many examples of code that don’t compile along with the
|
||||
error message the compiler will show you in each situation. Know that if you
|
||||
enter and run a random example, it may not compile! Make sure you read the
|
||||
surrounding text to see whether the example you’re trying to run is meant to
|
||||
error. In most situations, we’ll lead you to the correct version of any code
|
||||
that doesn’t compile.
|
||||
|
||||
## Source Code
|
||||
|
||||
The source files from which this book is generated can be found on
|
||||
[GitHub][book].
|
||||
|
||||
[book]: https://github.com/rust-lang/book/tree/master/2018-edition/src
|
||||
12
src/doc/book/2018-edition/src/ch01-00-getting-started.md
Normal file
12
src/doc/book/2018-edition/src/ch01-00-getting-started.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Getting Started
|
||||
|
||||
<!-- If you want to use this paragraph in the Introduction, can you replace it
|
||||
with some other introductory text for the chapter here? Maybe just lay out
|
||||
what's in this chapter so they know it's important not to skip it. -->
|
||||
<!-- Yep, done! /Carol -->
|
||||
|
||||
Let’s get your Rust journey started! In this chapter, we’ll discuss:
|
||||
|
||||
- Installing Rust on Linux, Mac, or Windows
|
||||
- Writing a program that prints “Hello, world!”
|
||||
- Using `cargo`, Rust’s package manager and build system
|
||||
154
src/doc/book/2018-edition/src/ch01-01-installation.md
Normal file
154
src/doc/book/2018-edition/src/ch01-01-installation.md
Normal file
@ -0,0 +1,154 @@
|
||||
## Installation
|
||||
|
||||
The first step to using Rust is to install it. We’ll download Rust through
|
||||
`rustup`, a command-line tool for managing Rust versions and associated tools.
|
||||
For this you’ll need an internet connection.
|
||||
|
||||
The following steps will install the latest stable version of the Rust
|
||||
compiler. The examples and output shown in this book all use stable Rust
|
||||
1.21.0. Rust’s stability guarantees ensure that all of the examples in the book
|
||||
that compile will continue to compile with newer versions of Rust. The output
|
||||
may differ slightly between versions, as error messages and warnings are often
|
||||
improved. In other words, any newer, stable version of Rust you will install
|
||||
with these steps should work as expected with the content of this book.
|
||||
|
||||
<!-- PROD: Start Box -->
|
||||
|
||||
> #### Command Line Notation
|
||||
>
|
||||
> In this chapter and throughout the book we’ll be showing some commands used
|
||||
> in the terminal. Lines that should be entered in a terminal all start with
|
||||
> `$`. You don’t need to type in the `$` character, it is simply there to
|
||||
> indicate the start of each command. Many tutorials use this convention: `$`
|
||||
> for commands run as a regular user, and `#` for commands you should be
|
||||
> running as an administrator. Lines that don’t start with `$` are typically
|
||||
> showing the output of the previous command. Additionally, PowerShell specific
|
||||
> examples will use `>` rather than `$`.
|
||||
|
||||
<!-- PROD: End box -->
|
||||
|
||||
### Installing Rustup on Linux or Mac
|
||||
|
||||
If you’re on Linux or a Mac, open a terminal and enter the following command:
|
||||
|
||||
```text
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
This will download a script and start the installation of the `rustup` tool,
|
||||
which installs the latest stable version of Rust. You may be prompted for your
|
||||
password. If it all goes well, you’ll see this appear:
|
||||
|
||||
```text
|
||||
Rust is installed now. Great!
|
||||
```
|
||||
|
||||
Of course, if you distrust using `curl URL | sh` to install software, you can
|
||||
download, inspect, and run the script however you like.
|
||||
|
||||
The installation script automatically adds Rust to your system PATH after your
|
||||
next login. If you want to start using Rust right away instead of restarting
|
||||
your terminal, run the following command in your shell to add Rust to your
|
||||
system PATH manually:
|
||||
|
||||
<!-- what does this command do? Do you mean instead of logging out and logging
|
||||
in, enter the following? -->
|
||||
<!-- It runs a script that adds Rust to your system PATH manually. I've
|
||||
clarified that yes, this is instead of logging out and back in to your
|
||||
terminal. /Carol -->
|
||||
|
||||
```text
|
||||
$ source $HOME/.cargo/env
|
||||
```
|
||||
|
||||
Alternatively, you can add the following line to your `~/.bash_profile`:
|
||||
|
||||
```text
|
||||
$ export PATH="$HOME/.cargo/bin:$PATH"
|
||||
```
|
||||
|
||||
Finally, you’ll need a linker of some kind. It’s likely you already have one
|
||||
installed, but if you try to compile a Rust program and get errors telling you
|
||||
that a linker could not be executed, you’ll need to install one. You can
|
||||
install a C compiler, as that will usually come with the correct linker. Check
|
||||
your platform’s documentation for how to install a C compiler. Some common Rust
|
||||
packages depend on C code and will need a C compiler too, so it may be worth
|
||||
installing one now regardless.
|
||||
|
||||
### Installing Rustup on Windows
|
||||
|
||||
On Windows, go to [https://www.rust-lang.org/en-US/install.html][install] and
|
||||
follow the instructions for installing Rust. At some point in the installation
|
||||
you’ll receive a message telling you you’ll also need the C++ build tools for
|
||||
Visual Studio 2013 or later. The easiest way to acquire the build tools is to
|
||||
install [Build Tools for Visual Studio 2017][visualstudio], found in the Other
|
||||
Tools and Frameworks section.
|
||||
|
||||
[install]: https://www.rust-lang.org/en-US/install.html
|
||||
[visualstudio]: https://www.visualstudio.com/downloads/
|
||||
|
||||
The rest of this book will use commands that work in both `cmd.exe` and
|
||||
PowerShell. If there are specific differences, we’ll explain which to use.
|
||||
|
||||
### Custom Installations Without Rustup
|
||||
|
||||
If you have reasons for preferring not to use `rustup`, please see [the Rust
|
||||
installation page](https://www.rust-lang.org/install.html) for other options.
|
||||
|
||||
### Updating and Uninstalling
|
||||
|
||||
Once you have Rust installed via `rustup`, updating to the latest version is
|
||||
easy. From your shell, run the update script:
|
||||
|
||||
```text
|
||||
$ rustup update
|
||||
```
|
||||
|
||||
To uninstall Rust and `rustup`, from your shell, run the uninstall script:
|
||||
|
||||
```text
|
||||
$ rustup self uninstall
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
To check whether you have Rust installed correctly, open up a shell and enter:
|
||||
|
||||
```text
|
||||
$ rustc --version
|
||||
```
|
||||
|
||||
You should see the version number, commit hash, and commit date for the latest
|
||||
stable version at the time you install in the following format:
|
||||
|
||||
```text
|
||||
rustc x.y.z (abcabcabc yyyy-mm-dd)
|
||||
```
|
||||
|
||||
If you see this, Rust has been installed successfully! Congrats!
|
||||
|
||||
If you don’t and you’re on Windows, check that Rust is in your `%PATH%` system
|
||||
variable.
|
||||
|
||||
If that’s all correct and Rust still isn’t working, there are a number of
|
||||
places you can get help. The easiest is [the #rust IRC channel on
|
||||
irc.mozilla.org][irc]<!-- ignore -->, which you can access through
|
||||
[Mibbit][mibbit]. Go to that address, and you’ll be chatting with other
|
||||
Rustaceans (a silly nickname we call ourselves) who can help you out. Other
|
||||
great resources include [the Users forum][users] and [Stack
|
||||
Overflow][stackoverflow].
|
||||
|
||||
[irc]: irc://irc.mozilla.org/#rust
|
||||
[mibbit]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust
|
||||
[users]: https://users.rust-lang.org/
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/rust
|
||||
|
||||
### Local Documentation
|
||||
|
||||
The installer also includes a copy of the documentation locally, so you can
|
||||
read it offline. Run `rustup doc` to open the local documentation in your
|
||||
browser.
|
||||
|
||||
Any time there’s a type or function provided by the standard library and you’re
|
||||
not sure what it does or how to use it, use the API (Application Programming
|
||||
Interface) documentation to find out!
|
||||
218
src/doc/book/2018-edition/src/ch01-02-hello-world.md
Normal file
218
src/doc/book/2018-edition/src/ch01-02-hello-world.md
Normal file
@ -0,0 +1,218 @@
|
||||
## Hello, World!
|
||||
|
||||
Now that you have Rust installed, let’s write your first Rust program. It’s
|
||||
traditional when learning a new language to write a little program to print the
|
||||
text “Hello, world!” to the screen, so we’ll do the same here!
|
||||
|
||||
> Note: This book assumes basic familiarity with the command line. Rust itself
|
||||
> makes no specific demands about your editing, tooling, or where your code
|
||||
> lives, so if you prefer an IDE (Integrated Development Environment) to the
|
||||
> command line, feel free to use your favorite IDE. Many IDEs now have some
|
||||
> degree of Rust support; check the IDE’s documentation for details. Enabling
|
||||
> great IDE support has been a recent focus of the Rust team, and progress
|
||||
> has been made rapidly on that front!
|
||||
|
||||
### Creating a Project Directory
|
||||
|
||||
First, make a directory to put your Rust code in. Rust doesn’t care where your
|
||||
code lives, but for the exercises and projects in this book, we’d suggest
|
||||
making a *projects* directory in your home directory and keeping all your
|
||||
projects there.
|
||||
|
||||
Open a terminal and enter the following commands to make a *projects* directory
|
||||
and, inside that, a directory for this “Hello, world!” project:
|
||||
|
||||
Linux and Mac:
|
||||
|
||||
```text
|
||||
$ mkdir ~/projects
|
||||
$ cd ~/projects
|
||||
$ mkdir hello_world
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
Windows CMD:
|
||||
|
||||
```cmd
|
||||
> mkdir "%USERPROFILE%\projects"
|
||||
> cd /d "%USERPROFILE%\projects"
|
||||
> mkdir hello_world
|
||||
> cd hello_world
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
> mkdir $env:USERPROFILE\projects
|
||||
> cd $env:USERPROFILE\projects
|
||||
> mkdir hello_world
|
||||
> cd hello_world
|
||||
```
|
||||
|
||||
### Writing and Running a Rust Program
|
||||
|
||||
Next, make a new source file and call it *main.rs*---Rust files always end with
|
||||
the *.rs* extension. If you’re using more than one word in your filename, use
|
||||
an underscore to separate them. For example, you’d use *hello_world.rs* rather
|
||||
than *helloworld.rs*.
|
||||
|
||||
Now open the *main.rs* file you just created, and enter the code shown in
|
||||
Listing 1-1:
|
||||
|
||||
<span class="filename">Filename: main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 1-1: A program that prints “Hello, world!”</span>
|
||||
|
||||
Save the file, and go back to your terminal window. On Linux or macOS, enter
|
||||
the following commands to compile and run the file:
|
||||
|
||||
```text
|
||||
$ rustc main.rs
|
||||
$ ./main
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
On Windows, use `.\main.exe` instead of `./main`.
|
||||
|
||||
```powershell
|
||||
> rustc main.rs
|
||||
> .\main.exe
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Regardless of your operating system, you should see the string `Hello, world!`
|
||||
print to the terminal. If you don’t see this output, see the “Troubleshooting”
|
||||
section earlier for ways to get help.
|
||||
|
||||
If you did see `Hello, world!` printed, then congratulations! You’ve officially
|
||||
written a Rust program. That makes you a Rust programmer! Welcome!
|
||||
|
||||
<!-- Any quick words of advice for if they didn't? (Disclosure: I tried
|
||||
following this using Bash on windows and couldn't get it working) -->
|
||||
<!-- Added a pointer to the previous troubleshooting section which also applies
|
||||
here /Carol -->
|
||||
|
||||
### Anatomy of a Rust Program
|
||||
|
||||
Now, let’s go over what just happened in your “Hello, world!” program in
|
||||
detail. Here’s the first piece of the puzzle:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
These lines define a *function* in Rust. The `main` function is special: it is
|
||||
always the first code that is run for every executable Rust program. The first
|
||||
line declares a function named `main` that has no parameters and returns
|
||||
nothing. If there were parameters, their names would go inside the parentheses,
|
||||
`(` and `)`.
|
||||
|
||||
Also note that the function body is wrapped in curly brackets, `{` and `}`.
|
||||
Rust requires these around all function bodies. It’s considered good style to
|
||||
place the opening curly bracket on the same line as the function declaration,
|
||||
with one space in between.
|
||||
|
||||
> At the time of writing, an automatic formatter, `rustfmt`, is under
|
||||
> development. If you’d like to stick to a standard style across Rust projects,
|
||||
> `rustfmt` is a tool that will format your code in a particular style. The
|
||||
> plan is to eventually include it with the standard Rust distribution, like
|
||||
> `rustc`, so depending on when you read this book, you may have it already
|
||||
> installed! Check the online documentation for more details.
|
||||
|
||||
Inside the `main` function, we have this code:
|
||||
|
||||
```rust
|
||||
println!("Hello, world!");
|
||||
```
|
||||
|
||||
This line does all of the work in this little program: it prints text to the
|
||||
screen. There are a number of details to notice here. The first is that Rust
|
||||
style is to indent with four spaces, not a tab.
|
||||
|
||||
The second important detail is the `println!` call. This code is calling a Rust
|
||||
*macro*. If it were calling a function instead, it would be entered as
|
||||
`println` (without the `!`). We’ll discuss Rust macros in more detail in
|
||||
Appendix D, but for now you just need to know that when you see a `!` that
|
||||
means that you’re calling a macro instead of a normal function.
|
||||
|
||||
<!-- I might suggest just cutting this next macro section -- for the sake of
|
||||
the intro, we don't really need this info, and I feel like this first exercise
|
||||
should be short and sweet and simple -->
|
||||
<!-- I'm ok with cutting this; it's a fairly common question that some folks
|
||||
have at this point, but I'm ok with those people having to do some research
|
||||
online if they're curious /Carol -->
|
||||
|
||||
Next comes`"Hello, world!"` which is a *string*. We pass this string as an
|
||||
argument to `println!` and the total effect is that the string is printed to
|
||||
the screen. Easy enough!
|
||||
|
||||
We end the line with a semicolon `;`, which indicates that this expression is
|
||||
over, and the next one is ready to begin. Most lines of Rust code end with a
|
||||
`;`.
|
||||
|
||||
### Compiling and Running Are Separate Steps
|
||||
|
||||
You’ve just seen how to run a newly created program, so now let’s break that
|
||||
process down and examine each step.
|
||||
|
||||
Before running a Rust program, you have to compile it using the Rust compiler
|
||||
by entering the `rustc` command and passing it the name of your source file,
|
||||
like this:
|
||||
|
||||
```text
|
||||
$ rustc main.rs
|
||||
```
|
||||
|
||||
If you come from a C or C++ background, you’ll notice that this is similar to
|
||||
`gcc` or `clang`. After compiling successfully, Rust outputs a binary
|
||||
executable.
|
||||
|
||||
On Linux, Mac, and PowerShell on Windows, you can see the executable by
|
||||
entering the `ls` command in your shell as follows:
|
||||
|
||||
```text
|
||||
$ ls
|
||||
main main.rs
|
||||
```
|
||||
|
||||
With CMD on Windows, you’d enter:
|
||||
|
||||
```cmd
|
||||
> dir /B %= the /B option says to only show the file names =%
|
||||
main.exe
|
||||
main.pdb
|
||||
main.rs
|
||||
```
|
||||
|
||||
This shows we have two files: the source code, with the *.rs* extension, and
|
||||
the executable (*main.exe* on Windows, *main* everywhere else). All that’s left
|
||||
to do from here is run the *main* or *main.exe* file, like this:
|
||||
|
||||
```text
|
||||
$ ./main # or .\main.exe on Windows
|
||||
```
|
||||
|
||||
If *main.rs* were your “Hello, world!” program, this would print `Hello,
|
||||
world!` to your terminal.
|
||||
|
||||
If you come from a dynamic language like Ruby, Python, or JavaScript, you may
|
||||
not be used to compiling and running a program being separate steps. Rust is an
|
||||
*ahead-of-time compiled* language, which means that you can compile a program,
|
||||
give the executable to someone else, and they can run it even without having
|
||||
Rust installed. If you give someone a `.rb`, `.py`, or `.js` file, on the other
|
||||
hand, they need to have a Ruby, Python, or JavaScript implementation installed
|
||||
(respectively), but you only need one command to both compile and run your
|
||||
program. Everything is a tradeoff in language design.
|
||||
|
||||
Just compiling with `rustc` is fine for simple programs, but as your project
|
||||
grows, you’ll want to be able to manage all of the options and make it easy to
|
||||
share your code. Next, we’ll introduce you to a tool called Cargo, which will
|
||||
help you write real-world Rust programs.
|
||||
266
src/doc/book/2018-edition/src/ch01-03-hello-cargo.md
Normal file
266
src/doc/book/2018-edition/src/ch01-03-hello-cargo.md
Normal file
@ -0,0 +1,266 @@
|
||||
## Hello, Cargo!
|
||||
|
||||
Cargo is Rust’s build system and package manager. Most Rustaceans will use this
|
||||
tool to manage their Rust projects because Cargo takes care of a lot of tasks
|
||||
for you, such as building your code, downloading the libraries your code
|
||||
depends on, and building those libraries. (We call libraries your code needs
|
||||
*dependencies*.)
|
||||
|
||||
The simplest Rust programs, like the one we’ve written so far, don’t have any
|
||||
dependencies, so if we had built the Hello World project with Cargo, it would
|
||||
only be using the part of Cargo that takes care of building your code. As you
|
||||
write more complex Rust programs, you’ll want to add dependencies, and if you
|
||||
start the project off using Cargo, that will be a lot easier to do.
|
||||
|
||||
As the vast majority of Rust projects use Cargo, the rest of this book will
|
||||
assume that you’re using Cargo too. Cargo comes installed with Rust itself, if
|
||||
you used the official installers as covered in the “Installation” section. If
|
||||
you installed Rust through some other means, you can check if you have Cargo
|
||||
installed by entering the following into your terminal:
|
||||
|
||||
```text
|
||||
$ cargo --version
|
||||
```
|
||||
|
||||
If you see a version number, great! If you see an error like `command not
|
||||
found`, then you should look at the documentation for your method of
|
||||
installation to determine how to install Cargo separately.
|
||||
|
||||
### Creating a Project with Cargo
|
||||
|
||||
Let’s create a new project using Cargo and look at how it differs from our
|
||||
original Hello World project. Navigate back to your *projects* directory (or
|
||||
whichever location you decided to place your code) and then on any operating
|
||||
system run:
|
||||
|
||||
```text
|
||||
$ cargo new hello_cargo --bin
|
||||
$ cd hello_cargo
|
||||
```
|
||||
|
||||
<!-- Below -- so we always have to start a cargo project with the --bin option
|
||||
if we want it to be something we can execute and not just a library, is that
|
||||
right? It might be worth laying that out -->
|
||||
<!-- As of Rust 1.21.0 (the version we're using for the book), yes, you must
|
||||
always specify `--bin`. In a version of Rust in the near future (1.25 or 1.26),
|
||||
binary crates will become the default kind of crate that `cargo new` makes, so
|
||||
you won't have to specify `--bin` (but you can if you want and the behavior
|
||||
will be the same). We'd rather not go into any more detail than we have here
|
||||
because of this change; I think "The `--bin` argument to passed to `cargo new`
|
||||
makes an executable application (often just called a *binary*), as opposed to a
|
||||
library." lays this out enough. /Carol -->
|
||||
|
||||
This creates a new binary executable called `hello_cargo`. The `--bin` argument
|
||||
to passed to `cargo new` makes an executable application (often just called a
|
||||
*binary*), as opposed to a library. We’ve given `hello_cargo` as the name for
|
||||
our project, and Cargo creates its files in a directory of the same name.
|
||||
|
||||
Go into the *hello_cargo* directory and list the files, and you should see that
|
||||
Cargo has generated two files and one directory for us: a *Cargo.toml* and a
|
||||
*src* directory with a *main.rs* file inside. It has also initialized a new git
|
||||
repository, along with a *.gitignore* file.
|
||||
|
||||
> Note: Git is a common version control system. You can change `cargo new` to
|
||||
> use a different version control system, or no version control system, by
|
||||
> using the `--vcs` flag. Run `cargo new --help` to see the available options.
|
||||
|
||||
Open up *Cargo.toml* in your text editor of choice. It should look similar to
|
||||
the code in Listing 1-2:
|
||||
|
||||
<span class="filename">Filename: Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello_cargo"
|
||||
version = "0.1.0"
|
||||
authors = ["Your Name <you@example.com>"]
|
||||
|
||||
[dependencies]
|
||||
```
|
||||
|
||||
<span class="caption">Listing 1-2: Contents of *Cargo.toml* generated by `cargo
|
||||
new`</span>
|
||||
|
||||
This file is in the [*TOML*][toml]<!-- ignore --> (Tom’s Obvious, Minimal
|
||||
Language) format, which is what Cargo uses as its configuration format.
|
||||
|
||||
[toml]: https://github.com/toml-lang/toml
|
||||
|
||||
The first line, `[package]`, is a section heading that indicates that the
|
||||
following statements are configuring a package. As we add more information to
|
||||
this file, we’ll add other sections.
|
||||
|
||||
The next three lines set the configuration information Cargo needs in order to
|
||||
know that it should compile your program: the name, the version, and who wrote
|
||||
it. Cargo gets your name and email information from your environment, so if
|
||||
that’s not correct, go ahead and fix that and save the file.
|
||||
|
||||
The last line, `[dependencies]`, is the start of a section for you to list any
|
||||
of your project’s dependencies. In Rust, packages of code are referred to as
|
||||
*crates*. We won’t need any other crates for this project, but we will in the
|
||||
first project in Chapter 2, so we’ll use this dependencies section then.
|
||||
|
||||
Now open up *src/main.rs* and take a look:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
Cargo has generated a “Hello World!” for you, just like the one we wrote in
|
||||
Listing 1-1! So far, the differences between our previous project and the
|
||||
project generated by Cargo are that with Cargo our code goes in the *src*
|
||||
directory, and we have a *Cargo.toml* configuration file in the top directory.
|
||||
|
||||
Cargo expects your source files to live inside the *src* directory; the
|
||||
top-level project directory is just for READMEs, license information,
|
||||
configuration files, and anything else not related to your code. In this way,
|
||||
using Cargo helps you keep your projects nice and tidy. There’s a place for
|
||||
everything, and everything is in its place.
|
||||
|
||||
If you started a project that doesn’t use Cargo, as we did with our project in
|
||||
the *hello_world* directory, you can convert it to a project that does use
|
||||
Cargo by moving the project code into the *src* directory and creating an
|
||||
appropriate *Cargo.toml*.
|
||||
|
||||
### Building and Running a Cargo Project
|
||||
|
||||
Now let’s look at what’s different about building and running your Hello World
|
||||
program through Cargo! From your project directory, build your project by
|
||||
entering the following commands:
|
||||
|
||||
```text
|
||||
$ cargo build
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs
|
||||
```
|
||||
|
||||
This creates an executable file in *target/debug/hello_cargo* (or
|
||||
*target\\debug\\hello_cargo.exe* on Windows), which you can run with this
|
||||
command:
|
||||
|
||||
```text
|
||||
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Bam! If all goes well, `Hello, world!` should print to the terminal once more.
|
||||
Running `cargo build` for the first time also causes Cargo to create a new file
|
||||
at the top level called *Cargo.lock*, which is used to keep track of the exact
|
||||
versions of dependencies in your project. This project doesn’t have
|
||||
dependencies, so the file is a bit sparse. You won’t ever need to touch this
|
||||
file yourself; Cargo will manage its contents for you.
|
||||
|
||||
We just built a project with `cargo build` and ran it with
|
||||
`./target/debug/hello_cargo`, but we can also use `cargo run` to compile and
|
||||
then run all in one go:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||
Running `target/debug/hello_cargo`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Notice that this time, we didn’t see the output telling us that Cargo was
|
||||
compiling `hello_cargo`. Cargo figured out that the files haven’t changed, so
|
||||
it just ran the binary. If you had modified your source code, Cargo would have
|
||||
rebuilt the project before running it, and you would have seen output like this:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
|
||||
Running `target/debug/hello_cargo`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Finally, there’s `cargo check`. This command will quickly check your code to
|
||||
make sure that it compiles, but not bother producing an executable:
|
||||
|
||||
```text
|
||||
$ cargo check
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
|
||||
```
|
||||
|
||||
Why would you not want an executable? `cargo check` is often much faster than
|
||||
`cargo build`, because it skips the entire step of producing the executable. If
|
||||
you’re checking your work throughout the process of writing the code, using
|
||||
`cargo check` will speed things up! As such, many Rustaceans run `cargo check`
|
||||
periodically as they write their program to make sure that it compiles, and
|
||||
then run `cargo build` once they’re ready to give it a spin themselves.
|
||||
|
||||
So to recap, using Cargo:
|
||||
|
||||
- We can build a project using `cargo build` or `cargo check`
|
||||
- We can build and run the project in one step with `cargo run`
|
||||
- Instead of the result of the build being put in the same directory as our
|
||||
code, Cargo will put it in the *target/debug* directory.
|
||||
|
||||
A final advantage of using Cargo is that the commands are the same no matter
|
||||
what operating system you’re on, so at this point we will no longer be
|
||||
providing specific instructions for Linux and Mac versus Windows.
|
||||
|
||||
### Building for Release
|
||||
|
||||
When your project is finally ready for release, you can use `cargo build
|
||||
--release` to compile your project with optimizations. This will create an
|
||||
executable in *target/release* instead of *target/debug*. These optimizations
|
||||
make your Rust code run faster, but turning them on increases the compilation
|
||||
time of your program. This is why there are two different profiles: one for
|
||||
development when you want to be able to rebuild quickly and often, and one for
|
||||
building the final program you’ll give to a user that won’t be rebuilt
|
||||
repeatedly and that will run as fast as possible. If you’re benchmarking the
|
||||
running time of your code, be sure to run `cargo build --release` and benchmark
|
||||
with the executable in *target/release*.
|
||||
|
||||
### Cargo as Convention
|
||||
|
||||
With simple projects, Cargo doesn’t provide a whole lot of value over just
|
||||
using `rustc`, but it will prove its worth as you continue. With complex
|
||||
projects composed of multiple crates, it’s much easier to let Cargo coordinate
|
||||
the build.
|
||||
|
||||
Even though the `hello_cargo` project is simple, it now uses much of the real
|
||||
tooling you’ll use for the rest of your Rust career. In fact, to work on any
|
||||
existing projects you can use the following commands to check out the code
|
||||
using Git, change into the project directory, and build:
|
||||
|
||||
```text
|
||||
$ git clone someurl.com/someproject
|
||||
$ cd someproject
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
If you want to look at Cargo in more detail, check out [its documentation].
|
||||
|
||||
[its documentation]: https://doc.rust-lang.org/cargo/
|
||||
|
||||
<!--Below -- I`m not sure this is the place for this conversation, it seems too
|
||||
deep into the weeds for the "getting started" chapter. I know we discussed
|
||||
Nightly Rust as an appendix previously, but honestly I think this is more
|
||||
suited somewhere online, perhaps in the extended docs. I like the idea of
|
||||
finishing the chapter here, on this practical note, and I think at this point
|
||||
readers will want to get stuck in anyway and may skip this and never come back
|
||||
because it's buried at the end of a chapter that's not really related to it. If
|
||||
it's online somewhere separate they can come to it when they're ready. What do
|
||||
you think?-->
|
||||
<!-- Ok, I can see that. /Carol -->
|
||||
|
||||
## Summary
|
||||
|
||||
You’re already off to a great start on your Rust journey! In this chapter,
|
||||
you’ve:
|
||||
|
||||
* Installed the latest stable version of Rust
|
||||
* Written a “Hello, world!” program using both `rustc` directly and using
|
||||
the conventions of `cargo`
|
||||
|
||||
This is a great time to build a more substantial program, to get used to
|
||||
reading and writing Rust code. In the next chapter, we’ll build a guessing game
|
||||
program. If you’d rather start by learning about how common programming
|
||||
concepts work in Rust, see Chapter 3.
|
||||
1011
src/doc/book/2018-edition/src/ch02-00-guessing-game-tutorial.md
Normal file
1011
src/doc/book/2018-edition/src/ch02-00-guessing-game-tutorial.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
# Common Programming Concepts
|
||||
|
||||
This chapter covers concepts that appear in almost every programming language
|
||||
and how they work in Rust. Many programming languages have much in common at
|
||||
their core. None of the concepts presented in this chapter are unique to Rust,
|
||||
but we’ll discuss them in the context of Rust and explain the conventions
|
||||
around using these concepts.
|
||||
|
||||
Specifically, you’ll learn about variables, basic types, functions, comments,
|
||||
and control flow. These foundations will be in every Rust program, and learning
|
||||
them early will give you a strong core to start from.
|
||||
|
||||
> ### Keywords
|
||||
>
|
||||
> The Rust language has a set of *keywords* that are reserved for use by
|
||||
> the language only, much as in other languages. Keep in mind that you cannot
|
||||
> use these words as names of variables or functions. Most of the keywords have
|
||||
> special meanings, and you’ll be using them to do various tasks in your Rust
|
||||
> programs; a few have no current functionality associated with them but have
|
||||
> been reserved for functionality that might be added to Rust in the future. You
|
||||
> can find a list of the keywords in Appendix A.
|
||||
@ -0,0 +1,228 @@
|
||||
## Variables and Mutability
|
||||
|
||||
As mentioned in Chapter 2, by default variables are immutable. This is one of
|
||||
many nudges Rust gives you to write your code in a way that takes advantage of
|
||||
the safety and easy concurrency that Rust offers. However, you still have the
|
||||
option to make your variables mutable. Let’s explore how and why Rust
|
||||
encourages you to favor immutability and why sometimes you might want to opt
|
||||
out.
|
||||
|
||||
When a variable is immutable, once a value is bound to a name, you can’t change
|
||||
that value. To illustrate this, let’s generate a new project called *variables*
|
||||
in your *projects* directory by using `cargo new --bin variables`.
|
||||
|
||||
Then, in your new *variables* directory, open *src/main.rs* and replace its
|
||||
code with the following code that won’t compile just yet:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
x = 6;
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
Save and run the program using `cargo run`. You should receive an error
|
||||
message, as shown in this output:
|
||||
|
||||
```text
|
||||
error[E0384]: cannot assign twice to immutable variable `x`
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
2 | let x = 5;
|
||||
| - first assignment to `x`
|
||||
3 | println!("The value of x is: {}", x);
|
||||
4 | x = 6;
|
||||
| ^^^^^ cannot assign twice to immutable variable
|
||||
```
|
||||
|
||||
This example shows how the compiler helps you find errors in your programs.
|
||||
Even though compiler errors can be frustrating, they only mean your program
|
||||
isn’t safely doing what you want it to do yet; they do *not* mean that you’re
|
||||
not a good programmer! Experienced Rustaceans still get compiler errors.
|
||||
|
||||
The error indicates that the cause of the error is that you `cannot assign twice
|
||||
to immutable variable x`, because you tried to assign a second value to the
|
||||
immutable `x` variable.
|
||||
|
||||
It’s important that we get compile-time errors when we attempt to change a
|
||||
value that we previously designated as immutable because this very situation
|
||||
can lead to bugs. If one part of our code operates on the assumption that a
|
||||
value will never change and another part of our code changes that value, it’s
|
||||
possible that the first part of the code won’t do what it was designed to do.
|
||||
The cause of this kind of bug can be difficult to track down after the fact,
|
||||
especially when the second piece of code changes the value only *sometimes*.
|
||||
|
||||
In Rust, the compiler guarantees that when you state that a value won’t change,
|
||||
it really won’t change. That means that when you’re reading and writing code,
|
||||
you don’t have to keep track of how and where a value might change. Your code
|
||||
is thus easier to reason through.
|
||||
|
||||
But mutability can be very useful. Variables are immutable only by default; as
|
||||
you did in Chapter 2, you can make them mutable by adding `mut` in front of the
|
||||
variable name. In addition to allowing this value to change, `mut` conveys
|
||||
intent to future readers of the code by indicating that other parts of the code
|
||||
will be changing this variable value.
|
||||
|
||||
For example, let’s change *src/main.rs* to the following:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
x = 6;
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
When we run the program now, we get this:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 5
|
||||
The value of x is: 6
|
||||
```
|
||||
|
||||
We’re allowed to change the value that `x` binds to from `5` to `6` when `mut`
|
||||
is used. In some cases, you’ll want to make a variable mutable because it makes
|
||||
the code more convenient to write than if it had only immutable variables.
|
||||
|
||||
There are multiple trade-offs to consider in addition to the prevention of
|
||||
bugs. For example, in cases where you’re using large data structures, mutating
|
||||
an instance in place may be faster than copying and returning newly allocated
|
||||
instances. With smaller data structures, creating new instances and writing in
|
||||
a more functional programming style may be easier to think through, so lower
|
||||
performance might be a worthwhile penalty for gaining that clarity.
|
||||
|
||||
### Differences Between Variables and Constants
|
||||
|
||||
Being unable to change the value of a variable might have reminded you of
|
||||
another programming concept that most other languages have: *constants*. Like
|
||||
immutable variables, constants are values that are bound to a name and are not
|
||||
allowed to change, but there are a few differences between constants and
|
||||
variables.
|
||||
|
||||
First, you aren’t allowed to use `mut` with constants. Constants aren’t just
|
||||
immutable by default—they’re always immutable.
|
||||
|
||||
You declare constants using the `const` keyword instead of the `let` keyword,
|
||||
and the type of the value *must* be annotated. We’re about to cover types and
|
||||
type annotations in the next section, “Data Types,” so don’t worry about the
|
||||
details right now. Just know that you must always annotate the type.
|
||||
|
||||
Constants can be declared in any scope, including the global scope, which makes
|
||||
them useful for values that many parts of code need to know about.
|
||||
|
||||
The last difference is that constants may be set only to a constant expression,
|
||||
not the result of a function call or any other value that could only be
|
||||
computed at runtime.
|
||||
|
||||
Here’s an example of a constant declaration where the constant’s name is
|
||||
`MAX_POINTS` and its value is set to 100,000. (Rust’s constant naming
|
||||
convention is to use all uppercase with underscores between words):
|
||||
|
||||
```rust
|
||||
const MAX_POINTS: u32 = 100_000;
|
||||
```
|
||||
|
||||
Constants are valid for the entire time a program runs, within the scope they
|
||||
were declared in, making them a useful choice for values in your application
|
||||
domain that multiple parts of the program might need to know about, such as the
|
||||
maximum number of points any player of a game is allowed to earn or the speed
|
||||
of light.
|
||||
|
||||
Naming hardcoded values used throughout your program as constants is useful in
|
||||
conveying the meaning of that value to future maintainers of the code. It also
|
||||
helps to have only one place in your code you would need to change if the
|
||||
hardcoded value needed to be updated in the future.
|
||||
|
||||
### Shadowing
|
||||
|
||||
As you saw in the “Comparing the Guess to the Secret Number” section in Chapter
|
||||
2, you can declare a new variable with the same name as a previous variable,
|
||||
and the new variable shadows the previous variable. Rustaceans say that the
|
||||
first variable is *shadowed* by the second, which means that the second
|
||||
variable’s value is what appears when the variable is used. We can shadow a
|
||||
variable by using the same variable’s name and repeating the use of the `let`
|
||||
keyword as follows:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
||||
let x = x + 1;
|
||||
|
||||
let x = x * 2;
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
This program first binds `x` to a value of `5`. Then it shadows `x` by
|
||||
repeating `let x =`, taking the original value and adding `1` so the value of
|
||||
`x` is then `6`. The third `let` statement also shadows `x`, multiplying the
|
||||
previous value by `2` to give `x` a final value of `12`. When we run this
|
||||
program, it will output the following:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 12
|
||||
```
|
||||
|
||||
Shadowing is different than marking a variable as `mut`, because we’ll get a
|
||||
compile-time error if we accidentally try to reassign to this variable without
|
||||
using the `let` keyword. By using `let`, we can perform a few transformations
|
||||
on a value but have the variable be immutable after those transformations have
|
||||
been completed.
|
||||
|
||||
The other difference between `mut` and shadowing is that because we’re
|
||||
effectively creating a new variable when we use the `let` keyword again, we can
|
||||
change the type of the value but reuse the same name. For example, say our
|
||||
program asks a user to show how many spaces they want between some text by
|
||||
inputting space characters, but we really want to store that input as a number:
|
||||
|
||||
```rust
|
||||
let spaces = " ";
|
||||
let spaces = spaces.len();
|
||||
```
|
||||
|
||||
This construct is allowed because the first `spaces` variable is a string type
|
||||
and the second `spaces` variable, which is a brand-new variable that happens to
|
||||
have the same name as the first one, is a number type. Shadowing thus spares us
|
||||
from having to come up with different names, such as `spaces_str` and
|
||||
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
|
||||
try to use `mut` for this, as shown here, we’ll get a compile-time error:
|
||||
|
||||
```rust,ignore
|
||||
let mut spaces = " ";
|
||||
spaces = spaces.len();
|
||||
```
|
||||
|
||||
The error says we’re not allowed to mutate a variable’s type:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:3:14
|
||||
|
|
||||
3 | spaces = spaces.len();
|
||||
| ^^^^^^^^^^^^ expected &str, found usize
|
||||
|
|
||||
= note: expected type `&str`
|
||||
found type `usize`
|
||||
```
|
||||
|
||||
Now that we’ve explored how variables work, let’s look at more data types they
|
||||
can have.
|
||||
364
src/doc/book/2018-edition/src/ch03-02-data-types.md
Normal file
364
src/doc/book/2018-edition/src/ch03-02-data-types.md
Normal file
@ -0,0 +1,364 @@
|
||||
## Data Types
|
||||
|
||||
Every value in Rust is of a certain *data type*, which tells Rust what kind of
|
||||
data is being specified so it knows how to work with that data. We’ll look at
|
||||
two data type subsets: scalar and compound.
|
||||
|
||||
Keep in mind that Rust is a *statically typed* language, which means that it
|
||||
must know the types of all variables at compile time. The compiler can usually
|
||||
infer what type we want to use based on the value and how we use it. In cases
|
||||
when many types are possible, such as when we converted a `String` to a numeric
|
||||
type using `parse` in the “Comparing the Guess to the Secret Number” section in
|
||||
Chapter 2, we must add a type annotation, like this:
|
||||
|
||||
```rust
|
||||
let guess: u32 = "42".parse().expect("Not a number!");
|
||||
```
|
||||
|
||||
If we don’t add the type annotation here, Rust will display the following
|
||||
error, which means the compiler needs more information from us to know which
|
||||
type we want to use:
|
||||
|
||||
```text
|
||||
error[E0282]: type annotations needed
|
||||
--> src/main.rs:2:9
|
||||
|
|
||||
2 | let guess = "42".parse().expect("Not a number!");
|
||||
| ^^^^^
|
||||
| |
|
||||
| cannot infer type for `_`
|
||||
| consider giving `guess` a type
|
||||
```
|
||||
|
||||
You’ll see different type annotations for other data types.
|
||||
|
||||
### Scalar Types
|
||||
|
||||
A *scalar* type represents a single value. Rust has four primary scalar types:
|
||||
integers, floating-point numbers, Booleans, and characters. You may recognize
|
||||
these from other programming languages. Let’s jump into how they work in Rust.
|
||||
|
||||
#### Integer Types
|
||||
|
||||
An *integer* is a number without a fractional component. We used one integer
|
||||
type in Chapter 2, the `u32` type. This type declaration indicates that the
|
||||
value it’s associated with should be an unsigned integer (signed integer types
|
||||
start with `i`, instead of `u`) that takes up 32 bits of space. Table 3-1 shows
|
||||
the built-in integer types in Rust. Each variant in the Signed and Unsigned
|
||||
columns (for example, `i16`) can be used to declare the type of an integer
|
||||
value.
|
||||
|
||||
<span class="caption">Table 3-1: Integer Types in Rust</span>
|
||||
|
||||
| Length | Signed | Unsigned |
|
||||
|--------|---------|----------|
|
||||
| 8-bit | `i8` | `u8` |
|
||||
| 16-bit | `i16` | `u16` |
|
||||
| 32-bit | `i32` | `u32` |
|
||||
| 64-bit | `i64` | `u64` |
|
||||
| arch | `isize` | `usize` |
|
||||
|
||||
Each variant can be either signed or unsigned and has an explicit size.
|
||||
*Signed* and *unsigned* refer to whether it’s possible for the number to be
|
||||
negative or positive—in other words, whether the number needs to have a sign
|
||||
with it (signed) or whether it will only ever be positive and can therefore be
|
||||
represented without a sign (unsigned). It’s like writing numbers on paper: when
|
||||
the sign matters, a number is shown with a plus sign or a minus sign; however,
|
||||
when it’s safe to assume the number is positive, it’s shown with no sign.
|
||||
Signed numbers are stored using two’s complement representation (if you’re
|
||||
unsure what this is, you can search for it online; an explanation is outside
|
||||
the scope of this book).
|
||||
|
||||
Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
|
||||
1</sup> - 1 inclusive, where *n* is the number of bits that variant uses. So an
|
||||
`i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup> - 1, which equals
|
||||
-128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
|
||||
so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
|
||||
|
||||
Additionally, the `isize` and `usize` types depend on the kind of computer your
|
||||
program is running on: 64 bits if you’re on a 64-bit architecture and 32 bits
|
||||
if you’re on a 32-bit architecture.
|
||||
|
||||
You can write integer literals in any of the forms shown in Table 3-2. Note
|
||||
that all number literals except the byte literal allow a type suffix, such as
|
||||
`57u8`, and `_` as a visual separator, such as `1_000`.
|
||||
|
||||
<span class="caption">Table 3-2: Integer Literals in Rust</span>
|
||||
|
||||
| Number literals | Example |
|
||||
|------------------|---------------|
|
||||
| Decimal | `98_222` |
|
||||
| Hex | `0xff` |
|
||||
| Octal | `0o77` |
|
||||
| Binary | `0b1111_0000` |
|
||||
| Byte (`u8` only) | `b'A'` |
|
||||
|
||||
So how do you know which type of integer to use? If you’re unsure, Rust’s
|
||||
defaults are generally good choices, and integer types default to `i32`: this
|
||||
type is generally the fastest, even on 64-bit systems. The primary situation in
|
||||
which you’d use `isize` or `usize` is when indexing some sort of collection.
|
||||
|
||||
#### Floating-Point Types
|
||||
|
||||
Rust also has two primitive types for *floating-point numbers*, which are
|
||||
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
||||
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
||||
because on modern CPUs it’s roughly the same speed as `f32` but is capable of
|
||||
more precision.
|
||||
|
||||
Here’s an example that shows floating-point numbers in action:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 2.0; // f64
|
||||
|
||||
let y: f32 = 3.0; // f32
|
||||
}
|
||||
```
|
||||
|
||||
Floating-point numbers are represented according to the IEEE-754 standard. The
|
||||
`f32` type is a single-precision float, and `f64` has double precision.
|
||||
|
||||
#### Numeric Operations
|
||||
|
||||
Rust supports the basic mathematical operations you’d expect for all of the
|
||||
number types: addition, subtraction, multiplication, division, and remainder.
|
||||
The following code shows how you’d use each one in a `let` statement:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// addition
|
||||
let sum = 5 + 10;
|
||||
|
||||
// subtraction
|
||||
let difference = 95.5 - 4.3;
|
||||
|
||||
// multiplication
|
||||
let product = 4 * 30;
|
||||
|
||||
// division
|
||||
let quotient = 56.7 / 32.2;
|
||||
|
||||
// remainder
|
||||
let remainder = 43 % 5;
|
||||
}
|
||||
```
|
||||
|
||||
Each expression in these statements uses a mathematical operator and evaluates
|
||||
to a single value, which is then bound to a variable. Appendix B contains a
|
||||
list of all operators that Rust provides.
|
||||
|
||||
#### The Boolean Type
|
||||
|
||||
As in most other programming languages, a Boolean type in Rust has two possible
|
||||
values: `true` and `false`. The Boolean type in Rust is specified using `bool`.
|
||||
For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let t = true;
|
||||
|
||||
let f: bool = false; // with explicit type annotation
|
||||
}
|
||||
```
|
||||
|
||||
The main way to consume Boolean values is through conditionals, such as an `if`
|
||||
expression. We’ll cover how `if` expressions work in Rust in the “Control Flow”
|
||||
section.
|
||||
|
||||
#### The Character Type
|
||||
|
||||
So far we’ve worked only with numbers, but Rust supports letters too. Rust’s
|
||||
`char` type is the language’s most primitive alphabetic type, and the following
|
||||
code shows one way to use it. (Note that the `char` type is specified with
|
||||
single quotes, as opposed to strings, which use double quotes.)
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let c = 'z';
|
||||
let z = 'ℤ';
|
||||
let heart_eyed_cat = '😻';
|
||||
}
|
||||
```
|
||||
|
||||
Rust’s `char` type represents a Unicode Scalar Value, which means it can
|
||||
represent a lot more than just ASCII. Accented letters; Chinese, Japanese, and
|
||||
Korean characters; emoji; and zero-width spaces are all valid `char` values in
|
||||
Rust. Unicode Scalar Values range from `U+0000` to `U+D7FF` and `U+E000` to
|
||||
`U+10FFFF` inclusive. However, a “character” isn’t really a concept in Unicode,
|
||||
so your human intuition for what a “character” is may not match up with what a
|
||||
`char` is in Rust. We’ll discuss this topic in detail in “Strings” in Chapter 8.
|
||||
|
||||
### Compound Types
|
||||
|
||||
*Compound types* can group multiple values into one type. Rust has two
|
||||
primitive compound types: tuples and arrays.
|
||||
|
||||
#### The Tuple Type
|
||||
|
||||
A tuple is a general way of grouping together some number of other values with
|
||||
a variety of types into one compound type.
|
||||
|
||||
We create a tuple by writing a comma-separated list of values inside
|
||||
parentheses. Each position in the tuple has a type, and the types of the
|
||||
different values in the tuple don’t have to be the same. We’ve added optional
|
||||
type annotations in this example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
||||
}
|
||||
```
|
||||
|
||||
The variable `tup` binds to the entire tuple, because a tuple is considered a
|
||||
single compound element. To get the individual values out of a tuple, we can
|
||||
use pattern matching to destructure a tuple value, like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let tup = (500, 6.4, 1);
|
||||
|
||||
let (x, y, z) = tup;
|
||||
|
||||
println!("The value of y is: {}", y);
|
||||
}
|
||||
```
|
||||
|
||||
This program first creates a tuple and binds it to the variable `tup`. It then
|
||||
uses a pattern with `let` to take `tup` and turn it into three separate
|
||||
variables, `x`, `y`, and `z`. This is called *destructuring*, because it breaks
|
||||
the single tuple into three parts. Finally, the program prints the value of
|
||||
`y`, which is `6.4`.
|
||||
|
||||
In addition to destructuring through pattern matching, we can access a tuple
|
||||
element directly by using a period (`.`) followed by the index of the value we
|
||||
want to access. For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x: (i32, f64, u8) = (500, 6.4, 1);
|
||||
|
||||
let five_hundred = x.0;
|
||||
|
||||
let six_point_four = x.1;
|
||||
|
||||
let one = x.2;
|
||||
}
|
||||
```
|
||||
|
||||
This program creates a tuple, `x`, and then makes new variables for each
|
||||
element by using their index. As with most programming languages, the first
|
||||
index in a tuple is 0.
|
||||
|
||||
#### The Array Type
|
||||
|
||||
Another way to have a collection of multiple values is with an *array*. Unlike
|
||||
a tuple, every element of an array must have the same type. Arrays in Rust are
|
||||
different from arrays in some other languages because arrays in Rust have a
|
||||
fixed length: once declared, they cannot grow or shrink in size.
|
||||
|
||||
In Rust, the values going into an array are written as a comma-separated list
|
||||
inside square brackets:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
}
|
||||
```
|
||||
|
||||
Arrays are useful when you want your data allocated on the stack rather than
|
||||
the heap (we will discuss the stack and the heap more in Chapter 4), or when
|
||||
you want to ensure you always have a fixed number of elements. An array isn’t
|
||||
as flexible as the vector type, though. A vector is a similar collection type
|
||||
provided by the standard library that *is* allowed to grow or shrink in size.
|
||||
If you’re unsure whether to use an array or a vector, you should probably use a
|
||||
vector. Chapter 8 discusses vectors in more detail.
|
||||
|
||||
An example of when you might want to use an array rather than a vector is in a
|
||||
program that needs to know the names of the months of the year. It’s very
|
||||
unlikely that such a program will need to add or remove months, so you can use
|
||||
an array because you know it will always contain 12 items:
|
||||
|
||||
```rust
|
||||
let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
```
|
||||
|
||||
##### Accessing Array Elements
|
||||
|
||||
An array is a single chunk of memory allocated on the stack. You can access
|
||||
elements of an array using indexing, like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
||||
let first = a[0];
|
||||
let second = a[1];
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the variable named `first` will get the value `1`, because
|
||||
that is the value at index `[0]` in the array. The variable named `second` will
|
||||
get the value `2` from index `[1]` in the array.
|
||||
|
||||
##### Invalid Array Element Access
|
||||
|
||||
What happens if you try to access an element of an array that is past the end
|
||||
of the array? Say you change the example to the following code, which will
|
||||
compile but exit with an error when it runs:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
let index = 10;
|
||||
|
||||
let element = a[index];
|
||||
|
||||
println!("The value of element is: {}", element);
|
||||
}
|
||||
```
|
||||
|
||||
Running this code using `cargo run` produces the following result:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling arrays v0.1.0 (file:///projects/arrays)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/arrays`
|
||||
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
||||
10', src/main.rs:6
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
```
|
||||
|
||||
The compilation didn’t produce any errors, but the program resulted in a
|
||||
*runtime* error and didn’t exit successfully. When you attempt to access an
|
||||
element using indexing, Rust will check that the index you’ve specified is less
|
||||
than the array length. If the index is greater than the length, Rust will
|
||||
*panic*, which is the term Rust uses when a program exits with an error.
|
||||
|
||||
This is the first example of Rust’s safety principles in action. In many
|
||||
low-level languages, this kind of check is not done, and when you provide an
|
||||
incorrect index, invalid memory can be accessed. Rust protects you against this
|
||||
kind of error by immediately exiting instead of allowing the memory access and
|
||||
continuing. Chapter 9 discusses more of Rust’s error handling.
|
||||
346
src/doc/book/2018-edition/src/ch03-03-how-functions-work.md
Normal file
346
src/doc/book/2018-edition/src/ch03-03-how-functions-work.md
Normal file
@ -0,0 +1,346 @@
|
||||
## Functions
|
||||
|
||||
Functions are pervasive in Rust code. You’ve already seen one of the most
|
||||
important functions in the language: the `main` function, which is the entry
|
||||
point of many programs. You’ve also seen the `fn` keyword, which allows you to
|
||||
declare new functions.
|
||||
|
||||
Rust code uses *snake case* as the conventional style for function and variable
|
||||
names. In snake case, all letters are lowercase and underscores separate words.
|
||||
Here’s a program that contains an example function definition:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
another_function();
|
||||
}
|
||||
|
||||
fn another_function() {
|
||||
println!("Another function.");
|
||||
}
|
||||
```
|
||||
|
||||
Function definitions in Rust start with `fn` and have a set of parentheses
|
||||
after the function name. The curly brackets tell the compiler where the
|
||||
function body begins and ends.
|
||||
|
||||
We can call any function we’ve defined by entering its name followed by a set
|
||||
of parentheses. Because `another_function` is defined in the program, it can be
|
||||
called from inside the `main` function. Note that we defined `another_function`
|
||||
*after* the `main` function in the source code; we could have defined it before
|
||||
as well. Rust doesn’t care where you define your functions, only that they’re
|
||||
defined somewhere.
|
||||
|
||||
Let’s start a new binary project named *functions* to explore functions
|
||||
further. Place the `another_function` example in *src/main.rs* and run it. You
|
||||
should see the following output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
|
||||
Running `target/debug/functions`
|
||||
Hello, world!
|
||||
Another function.
|
||||
```
|
||||
|
||||
The lines execute in the order in which they appear in the `main` function.
|
||||
First, the “Hello, world!” message prints, and then `another_function` is
|
||||
called and its message is printed.
|
||||
|
||||
### Function Parameters
|
||||
|
||||
Functions can also be defined to have *parameters*, which are special variables
|
||||
that are part of a function’s signature. When a function has parameters, you
|
||||
can provide it with concrete values for those parameters. Technically, the
|
||||
concrete values are called *arguments*, but in casual conversation, people tend
|
||||
to use the words *parameter* and *argument* interchangeably for either the
|
||||
variables in a function’s definition or the concrete values passed in when you
|
||||
call a function.
|
||||
|
||||
The following rewritten version of `another_function` shows what parameters
|
||||
look like in Rust:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
another_function(5);
|
||||
}
|
||||
|
||||
fn another_function(x: i32) {
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
Try running this program; you should get the following output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
The declaration of `another_function` has one parameter named `x`. The type of
|
||||
`x` is specified as `i32`. When `5` is passed to `another_function`, the
|
||||
`println!` macro puts `5` where the pair of curly brackets were in the format
|
||||
string.
|
||||
|
||||
In function signatures, you *must* declare the type of each parameter. This is
|
||||
a deliberate decision in Rust’s design: requiring type annotations in function
|
||||
definitions means the compiler almost never needs you to use them elsewhere in
|
||||
the code to figure out what you mean.
|
||||
|
||||
When you want a function to have multiple parameters, separate the parameter
|
||||
declarations with commas, like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
another_function(5, 6);
|
||||
}
|
||||
|
||||
fn another_function(x: i32, y: i32) {
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of y is: {}", y);
|
||||
}
|
||||
```
|
||||
|
||||
This example creates a function with two parameters, both of which are `i32`
|
||||
types. The function then prints the values in both of its parameters. Note that
|
||||
function parameters don’t all need to be the same type, they just happen to be
|
||||
in this example.
|
||||
|
||||
Let’s try running this code. Replace the program currently in your *functions*
|
||||
project’s *src/main.rs* file with the preceding example and run it using `cargo
|
||||
run`:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
The value of y is: 6
|
||||
```
|
||||
|
||||
Because we called the function with `5` as the value for `x` and `6` is passed
|
||||
as the value for `y`, the two strings are printed with these values.
|
||||
|
||||
### Function Bodies
|
||||
|
||||
Function bodies are made up of a series of statements optionally ending in an
|
||||
expression. So far, we’ve only covered functions without an ending expression,
|
||||
but you have seen an expression as part of statements. Because Rust is an
|
||||
expression-based language, this is an important distinction to understand.
|
||||
Other languages don’t have the same distinctions, so let’s look at what
|
||||
statements and expressions are and how their differences affect the bodies of
|
||||
functions.
|
||||
|
||||
### Statements and Expressions
|
||||
|
||||
We’ve actually already used statements and expressions. *Statements* are
|
||||
instructions that perform some action and do not return a value. *Expressions*
|
||||
evaluate to a resulting value. Let’s look at some examples.
|
||||
|
||||
Creating a variable and assigning a value to it with the `let` keyword is a
|
||||
statement. In Listing 3-1, `let y = 6;` is a statement:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let y = 6;
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-1: A `main` function declaration containing one statement</span>
|
||||
|
||||
Function definitions are also statements; the entire preceding example is a
|
||||
statement in itself.
|
||||
|
||||
Statements do not return values. Therefore, you can’t assign a `let` statement
|
||||
to another variable, as the following code tries to do; you’ll get an error:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let x = (let y = 6);
|
||||
}
|
||||
```
|
||||
|
||||
When you run this program, the error you’ll get looks like this:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
error: expected expression, found statement (`let`)
|
||||
--> src/main.rs:2:14
|
||||
|
|
||||
2 | let x = (let y = 6);
|
||||
| ^^^
|
||||
|
|
||||
= note: variable declaration using `let` is a statement
|
||||
```
|
||||
|
||||
The `let y = 6` statement does not return a value, so there isn’t anything for
|
||||
`x` to bind to. This is different from what happens in other languages, such as
|
||||
C and Ruby, where the assignment returns the value of the assignment. In those
|
||||
languages, you can write `x = y = 6` and have both `x` and `y` have the value
|
||||
`6`; that is not the case in Rust.
|
||||
|
||||
Expressions evaluate to something and make up most of the rest of the code that
|
||||
you’ll write in Rust. Consider a simple math operation, such as `5 + 6`, which
|
||||
is an expression that evaluates to the value `11`. Expressions can be part of
|
||||
statements: in Listing 3-1, the `6` in the statement `let y = 6;` is an
|
||||
expression that evaluates to the value `6`. Calling a function is an
|
||||
expression. Calling a macro is an expression. The block that we use to create
|
||||
new scopes, `{}`, is an expression, for example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
||||
let y = {
|
||||
let x = 3;
|
||||
x + 1
|
||||
};
|
||||
|
||||
println!("The value of y is: {}", y);
|
||||
}
|
||||
```
|
||||
|
||||
This expression:
|
||||
|
||||
```rust,ignore
|
||||
{
|
||||
let x = 3;
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
||||
as part of the `let` statement. Note the `x + 1` line without a semicolon at
|
||||
the end, which is unlike most of the lines you’ve seen so far. Expressions do
|
||||
not include ending semicolons. If you add a semicolon to the end of an
|
||||
expression, you turn it into a statement, which will then not return a value.
|
||||
Keep this in mind as you explore function return values and expressions next.
|
||||
|
||||
### Functions with Return Values
|
||||
|
||||
Functions can return values to the code that calls them. We don’t name return
|
||||
values, but we do declare their type after an arrow (`->`). In Rust, the return
|
||||
value of the function is synonymous with the value of the final expression in
|
||||
the block of the body of a function. You can return early from a function by
|
||||
using the `return` keyword and specifying a value, but most functions return
|
||||
the last expression implicitly. Here’s an example of a function that returns a
|
||||
value:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn five() -> i32 {
|
||||
5
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = five();
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
```
|
||||
|
||||
There are no function calls, macros, or even `let` statements in the `five`
|
||||
function—just the number `5` by itself. That’s a perfectly valid function in
|
||||
Rust. Note that the function’s return type is specified, too, as `-> i32`. Try
|
||||
running this code; the output should look like this:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
The `5` in `five` is the function’s return value, which is why the return type
|
||||
is `i32`. Let’s examine this in more detail. There are two important bits:
|
||||
first, the line `let x = five();` shows that we’re using the return value of a
|
||||
function to initialize a variable. Because the function `five` returns a `5`,
|
||||
that line is the same as the following:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
```
|
||||
|
||||
Second, the `five` function has no parameters and defines the type of the
|
||||
return value, but the body of the function is a lonely `5` with no semicolon
|
||||
because it’s an expression whose value we want to return.
|
||||
|
||||
Let’s look at another example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
|
||||
fn plus_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}
|
||||
```
|
||||
|
||||
Running this code will print `The value of x is: 6`. But if we place a
|
||||
semicolon at the end of the line containing `x + 1`, changing it from an
|
||||
expression to a statement, we’ll get an error.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
}
|
||||
|
||||
fn plus_one(x: i32) -> i32 {
|
||||
x + 1;
|
||||
}
|
||||
```
|
||||
|
||||
Running this code produces an error, as follows:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:7:28
|
||||
|
|
||||
7 | fn plus_one(x: i32) -> i32 {
|
||||
| ____________________________^
|
||||
8 | | x + 1;
|
||||
| | - help: consider removing this semicolon
|
||||
9 | | }
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
```
|
||||
|
||||
The main error message, “mismatched types,” reveals the core issue with this
|
||||
code. The definition of the function `plus_one` says that it will return an
|
||||
`i32`, but statements don’t evaluate to a value, which is expressed by `()`,
|
||||
the empty tuple. Therefore, nothing is returned, which contradicts the function
|
||||
definition and results in an error. In this output, Rust provides a message to
|
||||
possibly help rectify this issue: it suggests removing the semicolon, which
|
||||
would fix the error.
|
||||
47
src/doc/book/2018-edition/src/ch03-04-comments.md
Normal file
47
src/doc/book/2018-edition/src/ch03-04-comments.md
Normal file
@ -0,0 +1,47 @@
|
||||
## Comments
|
||||
|
||||
All programmers strive to make their code easy to understand, but sometimes
|
||||
extra explanation is warranted. In these cases, programmers leave notes, or
|
||||
*comments*, in their source code that the compiler will ignore but people
|
||||
reading the source code may find useful.
|
||||
|
||||
Here’s a simple comment:
|
||||
|
||||
```rust
|
||||
// Hello, world.
|
||||
```
|
||||
|
||||
In Rust, comments must start with two slashes and continue until the end of the
|
||||
line. For comments that extend beyond a single line, you’ll need to include
|
||||
`//` on each line, like this:
|
||||
|
||||
```rust
|
||||
// So we’re doing something complicated here, long enough that we need
|
||||
// multiple lines of comments to do it! Whew! Hopefully, this comment will
|
||||
// explain what’s going on.
|
||||
```
|
||||
|
||||
Comments can also be placed at the end of lines containing code:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let lucky_number = 7; // I’m feeling lucky today.
|
||||
}
|
||||
```
|
||||
|
||||
But you’ll more often see them used in this format, with the comment on a
|
||||
separate line above the code it’s annotating:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// I’m feeling lucky today.
|
||||
let lucky_number = 7;
|
||||
}
|
||||
```
|
||||
|
||||
Rust also has another kind of comment, documentation comments, which we’ll
|
||||
discuss in Chapter 14.
|
||||
462
src/doc/book/2018-edition/src/ch03-05-control-flow.md
Normal file
462
src/doc/book/2018-edition/src/ch03-05-control-flow.md
Normal file
@ -0,0 +1,462 @@
|
||||
## Control Flow
|
||||
|
||||
Deciding whether or not to run some code depending on if a condition is true
|
||||
and deciding to run some code repeatedly while a condition is true are basic
|
||||
building blocks in most programming languages. The most common constructs that
|
||||
let you control the flow of execution of Rust code are `if` expressions and
|
||||
loops.
|
||||
|
||||
### `if` Expressions
|
||||
|
||||
An `if` expression allows you to branch your code depending on conditions. You
|
||||
provide a condition and then state, “If this condition is met, run this block
|
||||
of code. If the condition is not met, do not run this block of code.”
|
||||
|
||||
Create a new project called *branches* in your *projects* directory to explore
|
||||
the `if` expression. In the *src/main.rs* file, input the following:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
if number < 5 {
|
||||
println!("condition was true");
|
||||
} else {
|
||||
println!("condition was false");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
|
||||
|
||||
All `if` expressions start with the keyword `if`, which is followed by a
|
||||
condition. In this case, the condition checks whether or not the variable
|
||||
`number` has a value less than 5. The block of code we want to execute if the
|
||||
condition is true is placed immediately after the condition inside curly
|
||||
brackets. Blocks of code associated with the conditions in `if` expressions are
|
||||
sometimes called *arms*, just like the arms in `match` expressions that we
|
||||
discussed in the “Comparing the Guess to the Secret Number” section of
|
||||
Chapter 2.
|
||||
|
||||
Optionally, we can also include an `else` expression, which we chose
|
||||
to do here, to give the program an alternative block of code to execute should
|
||||
the condition evaluate to false. If you don’t provide an `else` expression and
|
||||
the condition is false, the program will just skip the `if` block and move on
|
||||
to the next bit of code.
|
||||
|
||||
Try running this code; you should see the following output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was true
|
||||
```
|
||||
|
||||
Let’s try changing the value of `number` to a value that makes the condition
|
||||
`false` to see what happens:
|
||||
|
||||
```rust,ignore
|
||||
let number = 7;
|
||||
```
|
||||
|
||||
Run the program again, and look at the output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was false
|
||||
```
|
||||
|
||||
It’s also worth noting that the condition in this code *must* be a `bool`. If
|
||||
the condition isn’t a `bool`, we’ll get an error. For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
if number {
|
||||
println!("number was three");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `if` condition evaluates to a value of `3` this time, and Rust throws an
|
||||
error:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:4:8
|
||||
|
|
||||
4 | if number {
|
||||
| ^^^^^^ expected bool, found integral variable
|
||||
|
|
||||
= note: expected type `bool`
|
||||
found type `{integer}`
|
||||
```
|
||||
|
||||
The error indicates that Rust expected a `bool` but got an integer. Unlike
|
||||
languages such as Ruby and JavaScript, Rust will not automatically try to
|
||||
convert non-Boolean types to a Boolean. You must be explicit and always provide
|
||||
`if` with a Boolean as its condition. If we want the `if` code block to run
|
||||
only when a number is not equal to `0`, for example, we can change the `if`
|
||||
expression to the following:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
if number != 0 {
|
||||
println!("number was something other than zero");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Running this code will print `number was something other than zero`.
|
||||
|
||||
#### Handling Multiple Conditions with `else if`
|
||||
|
||||
You can have multiple conditions by combining `if` and `else` in an `else if`
|
||||
expression. For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let number = 6;
|
||||
|
||||
if number % 4 == 0 {
|
||||
println!("number is divisible by 4");
|
||||
} else if number % 3 == 0 {
|
||||
println!("number is divisible by 3");
|
||||
} else if number % 2 == 0 {
|
||||
println!("number is divisible by 2");
|
||||
} else {
|
||||
println!("number is not divisible by 4, 3, or 2");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This program has four possible paths it can take. After running it, you should
|
||||
see the following output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
number is divisible by 3
|
||||
```
|
||||
|
||||
When this program executes, it checks each `if` expression in turn and executes
|
||||
the first body for which the condition holds true. Note that even though 6 is
|
||||
divisible by 2, we don’t see the output `number is divisible by 2`, nor do we
|
||||
see the `number is not divisible by 4, 3, or 2` text from the `else` block.
|
||||
That’s because Rust only executes the block for the first true condition, and
|
||||
once it finds one, it doesn’t even check the rest.
|
||||
|
||||
Using too many `else if` expressions can clutter your code, so if you have more
|
||||
than one, you might want to refactor your code. Chapter 6 describes a powerful
|
||||
Rust branching construct called `match` for these cases.
|
||||
|
||||
#### Using `if` in a `let` Statement
|
||||
|
||||
Because `if` is an expression, we can use it on the right side of a `let`
|
||||
statement, as in Listing 3-2:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let condition = true;
|
||||
let number = if condition {
|
||||
5
|
||||
} else {
|
||||
6
|
||||
};
|
||||
|
||||
println!("The value of number is: {}", number);
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-2: Assigning the result of an `if` expression
|
||||
to a variable</span>
|
||||
|
||||
The `number` variable will be bound to a value based on the outcome of the `if`
|
||||
expression. Run this code to see what happens:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/branches`
|
||||
The value of number is: 5
|
||||
```
|
||||
|
||||
Remember that blocks of code evaluate to the last expression in them, and
|
||||
numbers by themselves are also expressions. In this case, the value of the
|
||||
whole `if` expression depends on which block of code executes. This means the
|
||||
values that have the potential to be results from each arm of the `if` must be
|
||||
the same type; in Listing 3-2, the results of both the `if` arm and the `else`
|
||||
arm were `i32` integers. If the types are mismatched, as in the following
|
||||
example, we’ll get an error:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let condition = true;
|
||||
|
||||
let number = if condition {
|
||||
5
|
||||
} else {
|
||||
"six"
|
||||
};
|
||||
|
||||
println!("The value of number is: {}", number);
|
||||
}
|
||||
```
|
||||
|
||||
When we try to run this code, we’ll get an error. The `if` and `else` arms have
|
||||
value types that are incompatible, and Rust indicates exactly where to find the
|
||||
problem in the program:
|
||||
|
||||
```text
|
||||
error[E0308]: if and else have incompatible types
|
||||
--> src/main.rs:4:18
|
||||
|
|
||||
4 | let number = if condition {
|
||||
| __________________^
|
||||
5 | | 5
|
||||
6 | | } else {
|
||||
7 | | "six"
|
||||
8 | | };
|
||||
| |_____^ expected integral variable, found &str
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found type `&str`
|
||||
```
|
||||
|
||||
The expression in the `if` block evaluates to an integer, and the expression in
|
||||
the `else` block evaluates to a string. This won’t work because variables must
|
||||
have a single type. Rust needs to know at compile time what type the `number`
|
||||
variable is, definitively, so it can verify at compile time that its type is
|
||||
valid everywhere we use `number`. Rust wouldn’t be able to do that if the type
|
||||
of `number` was only determined at runtime; the compiler would be more complex
|
||||
and would make fewer guarantees about the code if it had to keep track of
|
||||
multiple hypothetical types for any variable.
|
||||
|
||||
### Repetition with Loops
|
||||
|
||||
It’s often useful to execute a block of code more than once. For this task,
|
||||
Rust provides several *loops*. A loop runs through the code inside the loop
|
||||
body to the end and then starts immediately back at the beginning. To
|
||||
experiment with loops, let’s make a new project called *loops*.
|
||||
|
||||
Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one.
|
||||
|
||||
#### Repeating Code with `loop`
|
||||
|
||||
The `loop` keyword tells Rust to execute a block of code over and over again
|
||||
forever or until you explicitly tell it to stop.
|
||||
|
||||
As an example, change the *src/main.rs* file in your *loops* directory to look
|
||||
like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
loop {
|
||||
println!("again!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When we run this program, we’ll see `again!` printed over and over continuously
|
||||
until we stop the program manually. Most terminals support a keyboard shortcut,
|
||||
<span class="keystroke">ctrl-c</span>, to halt a program that is stuck in a
|
||||
continual loop. Give it a try:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
|
||||
Running `target/debug/loops`
|
||||
again!
|
||||
again!
|
||||
again!
|
||||
again!
|
||||
^Cagain!
|
||||
```
|
||||
|
||||
The symbol `^C` represents where you pressed <span class="keystroke">ctrl-c
|
||||
</span>. You may or may not see the word `again!` printed after the `^C`,
|
||||
depending on where the code was in the loop when it received the halt signal.
|
||||
|
||||
Fortunately, Rust provides another, more reliable way to break out of a loop.
|
||||
You can place the `break` keyword within the loop to tell the program when to
|
||||
stop executing the loop. Recall that we did this in the guessing game in the
|
||||
“Quitting After a Correct Guess” section of Chapter 2 to exit the
|
||||
program when the user won the game by guessing the correct number.
|
||||
|
||||
#### Conditional Loops with `while`
|
||||
|
||||
It’s often useful for a program to evaluate a condition within a loop. While
|
||||
the condition is true, the loop runs. When the condition ceases to be true, the
|
||||
program calls `break`, stopping the loop. This loop type could be implemented
|
||||
using a combination of `loop`, `if`, `else`, and `break`; you could try that
|
||||
now in a program, if you’d like.
|
||||
|
||||
However, this pattern is so common that Rust has a built-in language construct
|
||||
for it, called a `while` loop. Listing 3-3 uses `while`: the program loops
|
||||
three times, counting down each time, and then, after the loop, it prints
|
||||
another message and exits.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut number = 3;
|
||||
|
||||
while number != 0 {
|
||||
println!("{}!", number);
|
||||
|
||||
number = number - 1;
|
||||
}
|
||||
|
||||
println!("LIFTOFF!!!");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-3: Using a `while` loop to run code while a
|
||||
condition holds true</span>
|
||||
|
||||
This construct eliminates a lot of nesting that would be necessary if you used
|
||||
`loop`, `if`, `else`, and `break`, and it’s clearer. While a condition holds
|
||||
true, the code runs; otherwise, it exits the loop.
|
||||
|
||||
#### Looping Through a Collection with `for`
|
||||
|
||||
You could use the `while` construct to loop over the elements of a collection,
|
||||
such as an array. For example, let’s look at Listing 3-4:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
let mut index = 0;
|
||||
|
||||
while index < 5 {
|
||||
println!("the value is: {}", a[index]);
|
||||
|
||||
index = index + 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-4: Looping through each element of a collection
|
||||
using a `while` loop</span>
|
||||
|
||||
Here, the code counts up through the elements in the array. It starts at index
|
||||
`0`, and then loops until it reaches the final index in the array (that is,
|
||||
when `index < 5` is no longer true). Running this code will print every element
|
||||
in the array:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
|
||||
Running `target/debug/loops`
|
||||
the value is: 10
|
||||
the value is: 20
|
||||
the value is: 30
|
||||
the value is: 40
|
||||
the value is: 50
|
||||
```
|
||||
|
||||
All five array values appear in the terminal, as expected. Even though `index`
|
||||
will reach a value of `5` at some point, the loop stops executing before trying
|
||||
to fetch a sixth value from the array.
|
||||
|
||||
But this approach is error prone; we could cause the program to panic if the
|
||||
index length is incorrect. It’s also slow, because the compiler adds runtime
|
||||
code to perform the conditional check on every element on every iteration
|
||||
through the loop.
|
||||
|
||||
As a more concise alternative, you can use a `for` loop and execute some code
|
||||
for each item in a collection. A `for` loop looks like this code in Listing 3-5:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
||||
for element in a.iter() {
|
||||
println!("the value is: {}", element);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-5: Looping through each element of a collection
|
||||
using a `for` loop</span>
|
||||
|
||||
When we run this code, we’ll see the same output as in Listing 3-4. More
|
||||
importantly, we’ve now increased the safety of the code and eliminated the
|
||||
chance of bugs that might result from going beyond the end of the array or not
|
||||
going far enough and missing some items.
|
||||
|
||||
For example, in the code in Listing 3-4, if you removed an item from the `a`
|
||||
array but forgot to update the condition to `while index < 4`, the code would
|
||||
panic. Using the `for` loop, you wouldn’t need to remember to change any other
|
||||
code if you changed the number of values in the array.
|
||||
|
||||
The safety and conciseness of `for` loops make them the most commonly used loop
|
||||
construct in Rust. Even in situations in which you want to run some code a
|
||||
certain number of times, as in the countdown example that used a `while` loop
|
||||
in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that
|
||||
would be to use a `Range`, which is a type provided by the standard library
|
||||
that generates all numbers in sequence starting from one number and ending
|
||||
before another number.
|
||||
|
||||
Here’s what the countdown would look like using a `for` loop and another method
|
||||
we’ve not yet talked about, `rev`, to reverse the range:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
for number in (1..4).rev() {
|
||||
println!("{}!", number);
|
||||
}
|
||||
println!("LIFTOFF!!!");
|
||||
}
|
||||
```
|
||||
|
||||
This code is a bit nicer, isn’t it?
|
||||
|
||||
## Summary
|
||||
|
||||
You made it! That was a sizable chapter: you learned about variables, scalar
|
||||
and compound data types, functions, comments, `if` expressions, and loops! If
|
||||
you want to practice with the concepts discussed in this chapter, try building
|
||||
programs to do the following:
|
||||
|
||||
* Convert temperatures between Fahrenheit and Celsius.
|
||||
* Generate the nth Fibonacci number.
|
||||
* Print the lyrics to the Christmas carol “The Twelve Days of Christmas,”
|
||||
taking advantage of the repetition in the song.
|
||||
|
||||
When you’re ready to move on, we’ll talk about a concept in Rust that *doesn’t*
|
||||
commonly exist in other programming languages: ownership.
|
||||
@ -0,0 +1,7 @@
|
||||
# Understanding Ownership
|
||||
|
||||
Ownership is Rust’s most unique feature, and it enables Rust to make memory
|
||||
safety guarantees without needing a garbage collector. Therefore, it’s
|
||||
important to understand how ownership works in Rust. In this chapter, we’ll
|
||||
talk about ownership as well as several related features: borrowing, slices,
|
||||
and how Rust lays data out in memory.
|
||||
552
src/doc/book/2018-edition/src/ch04-01-what-is-ownership.md
Normal file
552
src/doc/book/2018-edition/src/ch04-01-what-is-ownership.md
Normal file
@ -0,0 +1,552 @@
|
||||
## What Is Ownership?
|
||||
|
||||
Rust’s central feature is *ownership*. Although the feature is straightforward
|
||||
to explain, it has deep implications for the rest of the language.
|
||||
|
||||
All programs have to manage the way they use a computer’s memory while running.
|
||||
Some languages have garbage collection that constantly looks for no longer used
|
||||
memory as the program runs; in other languages, the programmer must explicitly
|
||||
allocate and free the memory. Rust uses a third approach: memory is managed
|
||||
through a system of ownership with a set of rules that the compiler checks at
|
||||
compile time. None of the ownership features slow down your program while it’s
|
||||
running.
|
||||
|
||||
Because ownership is a new concept for many programmers, it does take some time
|
||||
to get used to. The good news is that the more experienced you become with Rust
|
||||
and the rules of the ownership system, the more you’ll be able to naturally
|
||||
develop code that is safe and efficient. Keep at it!
|
||||
|
||||
When you understand ownership, you’ll have a solid foundation for understanding
|
||||
the features that make Rust unique. In this chapter, you’ll learn ownership by
|
||||
working through some examples that focus on a very common data structure:
|
||||
strings.
|
||||
|
||||
> ### The Stack and the Heap
|
||||
>
|
||||
> In many programming languages, you don’t have to think about the stack and
|
||||
> the heap very often. But in a systems programming language like Rust, whether
|
||||
> a value is on the stack or the heap has more of an effect on how the language
|
||||
> behaves and why you have to make certain decisions. Parts of ownership will
|
||||
> be described in relation to the stack and the heap later in this chapter, so
|
||||
> here is a brief explanation in preparation.
|
||||
>
|
||||
> Both the stack and the heap are parts of memory that is available to your code
|
||||
> to use at runtime, but they are structured in different ways. The stack stores
|
||||
> values in the order it gets them and removes the values in the opposite order.
|
||||
> This is referred to as *last in, first out*. Think of a stack of plates: when
|
||||
> you add more plates, you put them on top of the pile, and when you need a
|
||||
> plate, you take one off the top. Adding or removing plates from the middle or
|
||||
> bottom wouldn’t work as well! Adding data is called *pushing onto the stack*,
|
||||
> and removing data is called *popping off the stack*.
|
||||
>
|
||||
> The stack is fast because of the way it accesses the data: it never has to
|
||||
> search for a place to put new data or a place to get data from because that
|
||||
> place is always the top. Another property that makes the stack fast is that
|
||||
> all data on the stack must take up a known, fixed size.
|
||||
>
|
||||
> Data with a size unknown at compile time or a size that might change can be
|
||||
> stored on the heap instead. The heap is less organized: when you put data on
|
||||
> the heap, you ask for some amount of space. The operating system finds an
|
||||
> empty spot somewhere in the heap that is big enough, marks it as being in
|
||||
> use, and returns a *pointer*, which is the address of that location. This
|
||||
> process is called *allocating on the heap*, sometimes abbreviated as just
|
||||
> “allocating.” Pushing values onto the stack is not considered allocating.
|
||||
> Because the pointer is a known, fixed size, you can store the pointer on the
|
||||
> stack, but when you want the actual data, you have to follow the pointer.
|
||||
>
|
||||
> Think of being seated at a restaurant. When you enter, you state the number of
|
||||
> people in your group, and the staff finds an empty table that fits everyone
|
||||
> and leads you there. If someone in your group comes late, they can ask where
|
||||
> you’ve been seated to find you.
|
||||
>
|
||||
> Accessing data in the heap is slower than accessing data on the stack because
|
||||
> you have to follow a pointer to get there. Contemporary processors are faster
|
||||
> if they jump around less in memory. Continuing the analogy, consider a server
|
||||
> at a restaurant taking orders from many tables. It’s most efficient to get
|
||||
> all the orders at one table before moving on to the next table. Taking an
|
||||
> order from table A, then an order from table B, then one from A again, and
|
||||
> then one from B again would be a much slower process. By the same token, a
|
||||
> processor can do its job better if it works on data that’s close to other
|
||||
> data (as it is on the stack) rather than farther away (as it can be on the
|
||||
> heap). Allocating a large amount of space on the heap can also take time.
|
||||
>
|
||||
> When your code calls a function, the values passed into the function
|
||||
> (including, potentially, pointers to data on the heap) and the function’s
|
||||
> local variables get pushed onto the stack. When the function is over, those
|
||||
> values get popped off the stack.
|
||||
>
|
||||
> Keeping track of what parts of code are using what data on the heap,
|
||||
> minimizing the amount of duplicate data on the heap, and cleaning up unused
|
||||
> data on the heap so you don’t run out of space are all problems that ownership
|
||||
> addresses. Once you understand ownership, you won’t need to think about the
|
||||
> stack and the heap very often, but knowing that managing heap data is why
|
||||
> ownership exists can help explain why it works the way it does.
|
||||
|
||||
### Ownership Rules
|
||||
|
||||
First, let’s take a look at the ownership rules. Keep these rules in mind as we
|
||||
work through the examples that illustrate them:
|
||||
|
||||
> 1. Each value in Rust has a variable that’s called its *owner*.
|
||||
> 2. There can only be one owner at a time.
|
||||
> 3. When the owner goes out of scope, the value will be dropped.
|
||||
|
||||
### Variable Scope
|
||||
|
||||
We’ve walked through an example of a Rust program already in Chapter 2. Now
|
||||
that we’re past basic syntax, we won’t include all the `fn main() {` code in
|
||||
examples, so if you’re following along, you’ll have to put the following
|
||||
examples inside a `main` function manually. As a result, our examples will be a
|
||||
bit more concise, letting us focus on the actual details rather than
|
||||
boilerplate code.
|
||||
|
||||
As a first example of ownership, we’ll look at the *scope* of some variables. A
|
||||
scope is the range within a program for which an item is valid. Let’s say we
|
||||
have a variable that looks like this:
|
||||
|
||||
```rust
|
||||
let s = "hello";
|
||||
```
|
||||
|
||||
The variable `s` refers to a string literal, where the value of the string is
|
||||
hardcoded into the text of our program. The variable is valid from the point at
|
||||
which it’s declared until the end of the current *scope*. Listing 4-1 has
|
||||
comments annotating where the variable `s` is valid:
|
||||
|
||||
```rust
|
||||
{ // s is not valid here, it’s not yet declared
|
||||
let s = "hello"; // s is valid from this point forward
|
||||
|
||||
// do stuff with s
|
||||
} // this scope is now over, and s is no longer valid
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-1: A variable and the scope in which it is
|
||||
valid</span>
|
||||
|
||||
In other words, there are two important points in time here:
|
||||
|
||||
* When `s` comes *into scope*, it is valid.
|
||||
* It remains valid until it goes *out of scope*.
|
||||
|
||||
At this point, the relationship between scopes and when variables are valid is
|
||||
similar to that in other programming languages. Now we’ll build on top of this
|
||||
understanding by introducing the `String` type.
|
||||
|
||||
### The `String` Type
|
||||
|
||||
To illustrate the rules of ownership, we need a data type that is more complex
|
||||
than the ones we covered in the “Data Types” section of Chapter 3. The types
|
||||
covered previously are all stored on the stack and popped off the stack when
|
||||
their scope is over, but we want to look at data that is stored on the heap and
|
||||
explore how Rust knows when to clean up that data.
|
||||
|
||||
We’ll use `String` as the example here and concentrate on the parts of `String`
|
||||
that relate to ownership. These aspects also apply to other complex data types
|
||||
provided by the standard library and that you create. We’ll discuss `String` in
|
||||
more depth in Chapter 8.
|
||||
|
||||
We’ve already seen string literals, where a string value is hardcoded into our
|
||||
program. String literals are convenient, but they aren’t suitable for every
|
||||
situation in which we may want to use text. One reason is that they’re
|
||||
immutable. Another is that not every string value can be known when we write
|
||||
our code: for example, what if we want to take user input and store it? For
|
||||
these situations, Rust has a second string type, `String`. This type is
|
||||
allocated on the heap and as such is able to store an amount of text that is
|
||||
unknown to us at compile time. You can create a `String` from a string literal
|
||||
using the `from` function, like so:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello");
|
||||
```
|
||||
|
||||
The double colon (`::`) is an operator that allows us to namespace this
|
||||
particular `from` function under the `String` type rather than using some sort
|
||||
of name like `string_from`. We’ll discuss this syntax more in the “Method
|
||||
Syntax” section of Chapter 5 and when we talk about namespacing with modules in
|
||||
“Module Definitions” in Chapter 7.
|
||||
|
||||
This kind of string *can* be mutated:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("hello");
|
||||
|
||||
s.push_str(", world!"); // push_str() appends a literal to a String
|
||||
|
||||
println!("{}", s); // This will print `hello, world!`
|
||||
```
|
||||
|
||||
So, what’s the difference here? Why can `String` be mutated but literals
|
||||
cannot? The difference is how these two types deal with memory.
|
||||
|
||||
### Memory and Allocation
|
||||
|
||||
In the case of a string literal, we know the contents at compile time, so the
|
||||
text is hardcoded directly into the final executable. This is why string
|
||||
literals are fast and efficient. But these properties only come from the string
|
||||
literal’s immutability. Unfortunately, we can’t put a blob of memory into the
|
||||
binary for each piece of text whose size is unknown at compile time and whose
|
||||
size might change while running the program.
|
||||
|
||||
With the `String` type, in order to support a mutable, growable piece of text,
|
||||
we need to allocate an amount of memory on the heap, unknown at compile time,
|
||||
to hold the contents. This means:
|
||||
|
||||
* The memory must be requested from the operating system at runtime.
|
||||
* We need a way of returning this memory to the operating system when we’re
|
||||
done with our `String`.
|
||||
|
||||
That first part is done by us: when we call `String::from`, its implementation
|
||||
requests the memory it needs. This is pretty much universal in programming
|
||||
languages.
|
||||
|
||||
However, the second part is different. In languages with a *garbage collector
|
||||
(GC)*, the GC keeps track and cleans up memory that isn’t being used anymore,
|
||||
and we don’t need to think about it. Without a GC, it’s our responsibility to
|
||||
identify when memory is no longer being used and call code to explicitly return
|
||||
it, just as we did to request it. Doing this correctly has historically been a
|
||||
difficult programming problem. If we forget, we’ll waste memory. If we do it
|
||||
too early, we’ll have an invalid variable. If we do it twice, that’s a bug too.
|
||||
We need to pair exactly one `allocate` with exactly one `free`.
|
||||
|
||||
Rust takes a different path: the memory is automatically returned once the
|
||||
variable that owns it goes out of scope. Here’s a version of our scope example
|
||||
from Listing 4-1 using a `String` instead of a string literal:
|
||||
|
||||
```rust
|
||||
{
|
||||
let s = String::from("hello"); // s is valid from this point forward
|
||||
|
||||
// do stuff with s
|
||||
} // this scope is now over, and s is no
|
||||
// longer valid
|
||||
```
|
||||
|
||||
There is a natural point at which we can return the memory our `String` needs
|
||||
to the operating system: when `s` goes out of scope. When a variable goes out
|
||||
of scope, Rust calls a special function for us. This function is called `drop`,
|
||||
and it’s where the author of `String` can put the code to return the memory.
|
||||
Rust calls `drop` automatically at the closing `}`.
|
||||
|
||||
> Note: In C++, this pattern of deallocating resources at the end of an item’s
|
||||
> lifetime is sometimes called *Resource Acquisition Is Initialization (RAII)*.
|
||||
> The `drop` function in Rust will be familiar to you if you’ve used RAII
|
||||
> patterns.
|
||||
|
||||
This pattern has a profound impact on the way Rust code is written. It may seem
|
||||
simple right now, but the behavior of code can be unexpected in more
|
||||
complicated situations when we want to have multiple variables use the data
|
||||
we’ve allocated on the heap. Let’s explore some of those situations now.
|
||||
|
||||
#### Ways Variables and Data Interact: Move
|
||||
|
||||
Multiple variables can interact with the same data in different ways in Rust.
|
||||
Let’s look at an example using an integer in Listing 4-2:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
let y = x;
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-2: Assigning the integer value of variable `x`
|
||||
to `y`</span>
|
||||
|
||||
We can probably guess what this is doing: “bind the value `5` to `x`; then make
|
||||
a copy of the value in `x` and bind it to `y`.” We now have two variables, `x`
|
||||
and `y`, and both equal `5`. This is indeed what is happening, because integers
|
||||
are simple values with a known, fixed size, and these two `5` values are pushed
|
||||
onto the stack.
|
||||
|
||||
Now let’s look at the `String` version:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1;
|
||||
```
|
||||
|
||||
This looks very similar to the previous code, so we might assume that the way
|
||||
it works would be the same: that is, the second line would make a copy of the
|
||||
value in `s1` and bind it to `s2`. But this isn’t quite what happens.
|
||||
|
||||
Take a look at Figure 4-1 to see what is happening to `String` under the
|
||||
covers. A `String` is made up of three parts, shown on the left: a pointer to
|
||||
the memory that holds the contents of the string, a length, and a capacity.
|
||||
This group of data is stored on the stack. On the right is the memory on the
|
||||
heap that holds the contents.
|
||||
|
||||
<img alt="String in memory" src="img/trpl04-01.svg" class="center" style="width: 50%;" />
|
||||
|
||||
<span class="caption">Figure 4-1: Representation in memory of a `String`
|
||||
holding the value `"hello"` bound to `s1`</span>
|
||||
|
||||
The length is how much memory, in bytes, the contents of the `String` is
|
||||
currently using. The capacity is the total amount of memory, in bytes, that the
|
||||
`String` has received from the operating system. The difference between length
|
||||
and capacity matters, but not in this context, so for now, it’s fine to ignore
|
||||
the capacity.
|
||||
|
||||
When we assign `s1` to `s2`, the `String` data is copied, meaning we copy the
|
||||
pointer, the length, and the capacity that are on the stack. We do not copy the
|
||||
data on the heap that the pointer refers to. In other words, the data
|
||||
representation in memory looks like Figure 4-2.
|
||||
|
||||
<img alt="s1 and s2 pointing to the same value" src="img/trpl04-02.svg" class="center" style="width: 50%;" />
|
||||
|
||||
<span class="caption">Figure 4-2: Representation in memory of the variable `s2`
|
||||
that has a copy of the pointer, length, and capacity of `s1`</span>
|
||||
|
||||
The representation does *not* look like Figure 4-3, which is what memory would
|
||||
look like if Rust instead copied the heap data as well. If Rust did this, the
|
||||
operation `s2 = s1` could be very expensive in terms of runtime performance if
|
||||
the data on the heap were large.
|
||||
|
||||
<img alt="s1 and s2 to two places" src="img/trpl04-03.svg" class="center" style="width: 50%;" />
|
||||
|
||||
<span class="caption">Figure 4-3: Another possibility for what `s2 = s1` might
|
||||
do if Rust copied the heap data as well</span>
|
||||
|
||||
Earlier, we said that when a variable goes out of scope, Rust automatically
|
||||
calls the `drop` function and cleans up the heap memory for that variable. But
|
||||
Figure 4-2 shows both data pointers pointing to the same location. This is a
|
||||
problem: when `s2` and `s1` go out of scope, they will both try to free the
|
||||
same memory. This is known as a *double free* error and is one of the memory
|
||||
safety bugs we mentioned previously. Freeing memory twice can lead to memory
|
||||
corruption, which can potentially lead to security vulnerabilities.
|
||||
|
||||
To ensure memory safety, there’s one more detail to what happens in this
|
||||
situation in Rust. Instead of trying to copy the allocated memory, Rust
|
||||
considers `s1` to no longer be valid and, therefore, Rust doesn’t need to free
|
||||
anything when `s1` goes out of scope. Check out what happens when you try to
|
||||
use `s1` after `s2` is created; it won’t work:
|
||||
|
||||
```rust,ignore
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1;
|
||||
|
||||
println!("{}, world!", s1);
|
||||
```
|
||||
|
||||
You’ll get an error like this because Rust prevents you from using the
|
||||
invalidated reference:
|
||||
|
||||
```text
|
||||
error[E0382]: use of moved value: `s1`
|
||||
--> src/main.rs:5:28
|
||||
|
|
||||
3 | let s2 = s1;
|
||||
| -- value moved here
|
||||
4 |
|
||||
5 | println!("{}, world!", s1);
|
||||
| ^^ value used here after move
|
||||
|
|
||||
= note: move occurs because `s1` has type `std::string::String`, which does
|
||||
not implement the `Copy` trait
|
||||
```
|
||||
|
||||
If you’ve heard the terms *shallow copy* and *deep copy* while working with
|
||||
other languages, the concept of copying the pointer, length, and capacity
|
||||
without copying the data probably sounds like making a shallow copy. But
|
||||
because Rust also invalidates the first variable, instead of being called a
|
||||
shallow copy, it’s known as a *move*. Here we would read this by saying that
|
||||
`s1` was *moved* into `s2`. So what actually happens is shown in Figure 4-4.
|
||||
|
||||
<img alt="s1 moved to s2" src="img/trpl04-04.svg" class="center" style="width: 50%;" />
|
||||
|
||||
<span class="caption">Figure 4-4: Representation in memory after `s1` has been
|
||||
invalidated</span>
|
||||
|
||||
That solves our problem! With only `s2` valid, when it goes out of scope, it
|
||||
alone will free the memory, and we’re done.
|
||||
|
||||
In addition, there’s a design choice that’s implied by this: Rust will never
|
||||
automatically create “deep” copies of your data. Therefore, any *automatic*
|
||||
copying can be assumed to be inexpensive in terms of runtime performance.
|
||||
|
||||
#### Ways Variables and Data Interact: Clone
|
||||
|
||||
If we *do* want to deeply copy the heap data of the `String`, not just the
|
||||
stack data, we can use a common method called `clone`. We’ll discuss method
|
||||
syntax in Chapter 5, but because methods are a common feature in many
|
||||
programming languages, you’ve probably seen them before.
|
||||
|
||||
Here’s an example of the `clone` method in action:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1.clone();
|
||||
|
||||
println!("s1 = {}, s2 = {}", s1, s2);
|
||||
```
|
||||
|
||||
This works just fine and explicitly produces the behavior shown in Figure 4-3,
|
||||
where the heap data *does* get copied.
|
||||
|
||||
When you see a call to `clone`, you know that some arbitrary code is being
|
||||
executed and that code may be expensive. It’s a visual indicator that something
|
||||
different is going on.
|
||||
|
||||
#### Stack-Only Data: Copy
|
||||
|
||||
There’s another wrinkle we haven’t talked about yet. This code using integers,
|
||||
part of which was shown earlier in Listing 4-2, works and is valid:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
let y = x;
|
||||
|
||||
println!("x = {}, y = {}", x, y);
|
||||
```
|
||||
|
||||
But this code seems to contradict what we just learned: we don’t have a call to
|
||||
`clone`, but `x` is still valid and wasn’t moved into `y`.
|
||||
|
||||
The reason is that types such as integers that have a known size at compile
|
||||
time are stored entirely on the stack, so copies of the actual values are quick
|
||||
to make. That means there’s no reason we would want to prevent `x` from being
|
||||
valid after we create the variable `y`. In other words, there’s no difference
|
||||
between deep and shallow copying here, so calling `clone` wouldn’t do anything
|
||||
different from the usual shallow copying and we can leave it out.
|
||||
|
||||
Rust has a special annotation called the `Copy` trait that we can place on
|
||||
types like integers that are stored on the stack (we’ll talk more about traits
|
||||
in Chapter 10). If a type has the `Copy` trait, an older variable is still
|
||||
usable after assignment. Rust won’t let us annotate a type with the `Copy`
|
||||
trait if the type, or any of its parts, has implemented the `Drop` trait. If
|
||||
the type needs something special to happen when the value goes out of scope and
|
||||
we add the `Copy` annotation to that type, we’ll get a compile time error. To
|
||||
learn about how to add the `Copy` annotation to your type, see “Derivable
|
||||
Traits” in Appendix C.
|
||||
|
||||
So what types are `Copy`? You can check the documentation for the given type to
|
||||
be sure, but as a general rule, any group of simple scalar values can be
|
||||
`Copy`, and nothing that requires allocation or is some form of resource is
|
||||
`Copy`. Here are some of the types that are `Copy`:
|
||||
|
||||
* All the integer types, such as `u32`.
|
||||
* The Boolean type, `bool`, with values `true` and `false`.
|
||||
* All the floating point types, such as `f64`.
|
||||
* The character type, `char`.
|
||||
* Tuples, but only if they contain types that are also `Copy`. For example,
|
||||
`(i32, i32)` is `Copy`, but `(i32, String)` is not.
|
||||
|
||||
### Ownership and Functions
|
||||
|
||||
The semantics for passing a value to a function are similar to those for
|
||||
assigning a value to a variable. Passing a variable to a function will move or
|
||||
copy, just as assignment does. Listing 4-3 has an example with some annotations
|
||||
showing where variables go into and out of scope:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s = String::from("hello"); // s comes into scope
|
||||
|
||||
takes_ownership(s); // s's value moves into the function...
|
||||
// ... and so is no longer valid here
|
||||
|
||||
let x = 5; // x comes into scope
|
||||
|
||||
makes_copy(x); // x would move into the function,
|
||||
// but i32 is Copy, so it’s okay to still
|
||||
// use x afterward
|
||||
|
||||
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
|
||||
// special happens.
|
||||
|
||||
fn takes_ownership(some_string: String) { // some_string comes into scope
|
||||
println!("{}", some_string);
|
||||
} // Here, some_string goes out of scope and `drop` is called. The backing
|
||||
// memory is freed.
|
||||
|
||||
fn makes_copy(some_integer: i32) { // some_integer comes into scope
|
||||
println!("{}", some_integer);
|
||||
} // Here, some_integer goes out of scope. Nothing special happens.
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-3: Functions with ownership and scope
|
||||
annotated</span>
|
||||
|
||||
If we tried to use `s` after the call to `takes_ownership`, Rust would throw a
|
||||
compile time error. These static checks protect us from mistakes. Try adding
|
||||
code to `main` that uses `s` and `x` to see where you can use them and where
|
||||
the ownership rules prevent you from doing so.
|
||||
|
||||
### Return Values and Scope
|
||||
|
||||
Returning values can also transfer ownership. Listing 4-4 is an example with
|
||||
similar annotations to those in Listing 4-3:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = gives_ownership(); // gives_ownership moves its return
|
||||
// value into s1
|
||||
|
||||
let s2 = String::from("hello"); // s2 comes into scope
|
||||
|
||||
let s3 = takes_and_gives_back(s2); // s2 is moved into
|
||||
// takes_and_gives_back, which also
|
||||
// moves its return value into s3
|
||||
} // Here, s3 goes out of scope and is dropped. s2 goes out of scope but was
|
||||
// moved, so nothing happens. s1 goes out of scope and is dropped.
|
||||
|
||||
fn gives_ownership() -> String { // gives_ownership will move its
|
||||
// return value into the function
|
||||
// that calls it
|
||||
|
||||
let some_string = String::from("hello"); // some_string comes into scope
|
||||
|
||||
some_string // some_string is returned and
|
||||
// moves out to the calling
|
||||
// function.
|
||||
}
|
||||
|
||||
// takes_and_gives_back will take a String and return one.
|
||||
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
|
||||
// scope
|
||||
|
||||
a_string // a_string is returned and moves out to the calling function
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-4: Transferring ownership of return
|
||||
values</span>
|
||||
|
||||
The ownership of a variable follows the same pattern every time: assigning a
|
||||
value to another variable moves it. When a variable that includes data on the
|
||||
heap goes out of scope, the value will be cleaned up by `drop` unless the data
|
||||
has been moved to be owned by another variable.
|
||||
|
||||
Taking ownership and then returning ownership with every function is a bit
|
||||
tedious. What if we want to let a function use a value but not take ownership?
|
||||
It’s quite annoying that anything we pass in also needs to be passed back if we
|
||||
want to use it again, in addition to any data resulting from the body of the
|
||||
function that we might want to return as well.
|
||||
|
||||
It’s possible to return multiple values using a tuple, as shown in Listing 4-5:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let (s2, len) = calculate_length(s1);
|
||||
|
||||
println!("The length of '{}' is {}.", s2, len);
|
||||
}
|
||||
|
||||
fn calculate_length(s: String) -> (String, usize) {
|
||||
let length = s.len(); // len() returns the length of a String
|
||||
|
||||
(s, length)
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-5: Returning ownership of parameters</span>
|
||||
|
||||
But this is too much ceremony and a lot of work for a concept that should be
|
||||
common. Luckily for us, Rust has a feature for this concept, called
|
||||
*references*.
|
||||
@ -0,0 +1,322 @@
|
||||
## References and Borrowing
|
||||
|
||||
The issue with the tuple code in Listing 4-5 is that we have to return the
|
||||
`String` to the calling function so we can still use the `String` after the
|
||||
call to `calculate_length`, because the `String` was moved into
|
||||
`calculate_length`.
|
||||
|
||||
Here is how you would define and use a `calculate_length` function that has a
|
||||
reference to an object as a parameter instead of taking ownership of the
|
||||
value:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let len = calculate_length(&s1);
|
||||
|
||||
println!("The length of '{}' is {}.", s1, len);
|
||||
}
|
||||
|
||||
fn calculate_length(s: &String) -> usize {
|
||||
s.len()
|
||||
}
|
||||
```
|
||||
|
||||
First, notice that all the tuple code in the variable declaration and the
|
||||
function return value is gone. Second, note that we pass `&s1` into
|
||||
`calculate_length` and, in its definition, we take `&String` rather than
|
||||
`String`.
|
||||
|
||||
These ampersands are *references*, and they allow you to refer to some value
|
||||
without taking ownership of it. Figure 4-5 shows a diagram.
|
||||
|
||||
<img alt="&String s pointing at String s1" src="img/trpl04-05.svg" class="center" />
|
||||
|
||||
<span class="caption">Figure 4-5: A diagram of `&String s` pointing at `String
|
||||
s1`</span>
|
||||
|
||||
> Note: The opposite of referencing by using `&` is *dereferencing*, which is
|
||||
> accomplished with the dereference operator, `*`. We’ll see some uses of the
|
||||
> dereference operator in Chapter 8 and discuss details of dereferencing in
|
||||
> Chapter 15.
|
||||
|
||||
Let’s take a closer look at the function call here:
|
||||
|
||||
```rust
|
||||
# fn calculate_length(s: &String) -> usize {
|
||||
# s.len()
|
||||
# }
|
||||
let s1 = String::from("hello");
|
||||
|
||||
let len = calculate_length(&s1);
|
||||
```
|
||||
|
||||
The `&s1` syntax lets us create a reference that *refers* to the value of `s1`
|
||||
but does not own it. Because it does not own it, the value it points to will
|
||||
not be dropped when the reference goes out of scope.
|
||||
|
||||
Likewise, the signature of the function uses `&` to indicate that the type of
|
||||
the parameter `s` is a reference. Let’s add some explanatory annotations:
|
||||
|
||||
```rust
|
||||
fn calculate_length(s: &String) -> usize { // s is a reference to a String
|
||||
s.len()
|
||||
} // Here, s goes out of scope. But because it does not have ownership of what
|
||||
// it refers to, nothing happens.
|
||||
```
|
||||
|
||||
The scope in which the variable `s` is valid is the same as any function
|
||||
parameter’s scope, but we don’t drop what the reference points to when it goes
|
||||
out of scope because we don’t have ownership. When functions have references as
|
||||
parameters instead of the actual values, we won’t need to return the values in
|
||||
order to give back ownership, because we never had ownership.
|
||||
|
||||
We call having references as function parameters *borrowing*. As in real life,
|
||||
if a person owns something, you can borrow it from them. When you’re done, you
|
||||
have to give it back.
|
||||
|
||||
So what happens if we try to modify something we’re borrowing? Try the code in
|
||||
Listing 4-6. Spoiler alert: it doesn’t work!
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
|
||||
change(&s);
|
||||
}
|
||||
|
||||
fn change(some_string: &String) {
|
||||
some_string.push_str(", world");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-6: Attempting to modify a borrowed value</span>
|
||||
|
||||
Here’s the error:
|
||||
|
||||
```text
|
||||
error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable
|
||||
--> error.rs:8:5
|
||||
|
|
||||
7 | fn change(some_string: &String) {
|
||||
| ------- use `&mut String` here to make mutable
|
||||
8 | some_string.push_str(", world");
|
||||
| ^^^^^^^^^^^ cannot borrow as mutable
|
||||
```
|
||||
|
||||
Just as variables are immutable by default, so are references. We’re not
|
||||
allowed to modify something we have a reference to.
|
||||
|
||||
### Mutable References
|
||||
|
||||
We can fix the error in the code from Listing 4-6 with just a small tweak:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
change(&mut s);
|
||||
}
|
||||
|
||||
fn change(some_string: &mut String) {
|
||||
some_string.push_str(", world");
|
||||
}
|
||||
```
|
||||
|
||||
First, we had to change `s` to be `mut`. Then we had to create a mutable
|
||||
reference with `&mut s` and accept a mutable reference with `some_string: &mut
|
||||
String`.
|
||||
|
||||
But mutable references have one big restriction: you can only have one mutable
|
||||
reference to a particular piece of data in a particular scope. This code will
|
||||
fail:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
let mut s = String::from("hello");
|
||||
|
||||
let r1 = &mut s;
|
||||
let r2 = &mut s;
|
||||
```
|
||||
|
||||
Here’s the error:
|
||||
|
||||
```text
|
||||
error[E0499]: cannot borrow `s` as mutable more than once at a time
|
||||
--> borrow_twice.rs:5:19
|
||||
|
|
||||
4 | let r1 = &mut s;
|
||||
| - first mutable borrow occurs here
|
||||
5 | let r2 = &mut s;
|
||||
| ^ second mutable borrow occurs here
|
||||
6 | }
|
||||
| - first borrow ends here
|
||||
```
|
||||
|
||||
This restriction allows for mutation but in a very controlled fashion. It’s
|
||||
something that new Rustaceans struggle with, because most languages let you
|
||||
mutate whenever you’d like.
|
||||
|
||||
The benefit of having this restriction is that Rust can prevent data races at
|
||||
compile time. A *data race* is similar to a race condition and happens when
|
||||
these three behaviors occur:
|
||||
|
||||
* Two or more pointers access the same data at the same time.
|
||||
* At least one of the pointers is being used to write to the data.
|
||||
* There’s no mechanism being used to synchronize access to the data.
|
||||
|
||||
Data races cause undefined behavior and can be difficult to diagnose and fix
|
||||
when you’re trying to track them down at runtime; Rust prevents this problem
|
||||
from happening because it won’t even compile code with data races!
|
||||
|
||||
As always, we can use curly brackets to create a new scope, allowing for
|
||||
multiple mutable references, just not *simultaneous* ones:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("hello");
|
||||
|
||||
{
|
||||
let r1 = &mut s;
|
||||
|
||||
} // r1 goes out of scope here, so we can make a new reference with no problems.
|
||||
|
||||
let r2 = &mut s;
|
||||
```
|
||||
|
||||
A similar rule exists for combining mutable and immutable references. This code
|
||||
results in an error:
|
||||
|
||||
```rust,ignore
|
||||
let mut s = String::from("hello");
|
||||
|
||||
let r1 = &s; // no problem
|
||||
let r2 = &s; // no problem
|
||||
let r3 = &mut s; // BIG PROBLEM
|
||||
```
|
||||
|
||||
Here’s the error:
|
||||
|
||||
```text
|
||||
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as
|
||||
immutable
|
||||
--> borrow_thrice.rs:6:19
|
||||
|
|
||||
4 | let r1 = &s; // no problem
|
||||
| - immutable borrow occurs here
|
||||
5 | let r2 = &s; // no problem
|
||||
6 | let r3 = &mut s; // BIG PROBLEM
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
| - immutable borrow ends here
|
||||
```
|
||||
|
||||
Whew! We *also* cannot have a mutable reference while we have an immutable one.
|
||||
Users of an immutable reference don’t expect the values to suddenly change out
|
||||
from under them! However, multiple immutable references are okay because no one
|
||||
who is just reading the data has the ability to affect anyone else’s reading of
|
||||
the data.
|
||||
|
||||
Even though these errors may be frustrating at times, remember that it’s the
|
||||
Rust compiler pointing out a potential bug early (at compile time rather than
|
||||
at runtime) and showing you exactly where the problem is. Then you don’t have
|
||||
to track down why your data isn’t what you thought it was.
|
||||
|
||||
### Dangling References
|
||||
|
||||
In languages with pointers, it’s easy to erroneously create a *dangling
|
||||
pointer*, a pointer that references a location in memory that may have been
|
||||
given to someone else, by freeing some memory while preserving a pointer to
|
||||
that memory. In Rust, by contrast, the compiler guarantees that references will
|
||||
never be dangling references: if you have a reference to some data, the
|
||||
compiler will ensure that the data will not go out of scope before the
|
||||
reference to the data does.
|
||||
|
||||
Let’s try to create a dangling reference, which Rust will prevent with a
|
||||
compile-time error:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let reference_to_nothing = dangle();
|
||||
}
|
||||
|
||||
fn dangle() -> &String {
|
||||
let s = String::from("hello");
|
||||
|
||||
&s
|
||||
}
|
||||
```
|
||||
|
||||
Here’s the error:
|
||||
|
||||
```text
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> dangle.rs:5:16
|
||||
|
|
||||
5 | fn dangle() -> &String {
|
||||
| ^ expected lifetime parameter
|
||||
|
|
||||
= help: this function's return type contains a borrowed value, but there is
|
||||
no value for it to be borrowed from
|
||||
= help: consider giving it a 'static lifetime
|
||||
```
|
||||
|
||||
This error message refers to a feature we haven’t covered yet: *lifetimes*.
|
||||
We’ll discuss lifetimes in detail in Chapter 10. But, if you disregard the
|
||||
parts about lifetimes, the message does contain the key to why this code is a
|
||||
problem:
|
||||
|
||||
```text
|
||||
this function's return type contains a borrowed value, but there is no value
|
||||
for it to be borrowed from.
|
||||
```
|
||||
|
||||
Let’s take a closer look at exactly what’s happening at each stage of our
|
||||
`dangle` code:
|
||||
|
||||
```rust,ignore
|
||||
fn dangle() -> &String { // dangle returns a reference to a String
|
||||
|
||||
let s = String::from("hello"); // s is a new String
|
||||
|
||||
&s // we return a reference to the String, s
|
||||
} // Here, s goes out of scope, and is dropped. Its memory goes away.
|
||||
// Danger!
|
||||
```
|
||||
|
||||
Because `s` is created inside `dangle`, when the code of `dangle` is finished,
|
||||
`s` will be deallocated. But we tried to return a reference to it. That means
|
||||
this reference would be pointing to an invalid `String` That’s no good! Rust
|
||||
won’t let us do this.
|
||||
|
||||
The solution here is to return the `String` directly:
|
||||
|
||||
```rust
|
||||
fn no_dangle() -> String {
|
||||
let s = String::from("hello");
|
||||
|
||||
s
|
||||
}
|
||||
```
|
||||
|
||||
This works without any problems. Ownership is moved out, and nothing is
|
||||
deallocated.
|
||||
|
||||
### The Rules of References
|
||||
|
||||
Let’s recap what we’ve discussed about references:
|
||||
|
||||
* At any given time, you can have *either* (but not both of) one mutable
|
||||
reference or any number of immutable references.
|
||||
* References must always be valid.
|
||||
|
||||
Next, we’ll look at a different kind of reference: slices.
|
||||
386
src/doc/book/2018-edition/src/ch04-03-slices.md
Normal file
386
src/doc/book/2018-edition/src/ch04-03-slices.md
Normal file
@ -0,0 +1,386 @@
|
||||
## The Slice Type
|
||||
|
||||
Another data type that does not have ownership is the *slice*. Slices let you
|
||||
reference a contiguous sequence of elements in a collection rather than the
|
||||
whole collection.
|
||||
|
||||
Here’s a small programming problem: write a function that takes a string and
|
||||
returns the first word it finds in that string. If the function doesn’t find a
|
||||
space in the string, the whole string must be one word, so the entire string
|
||||
should be returned.
|
||||
|
||||
Let’s think about the signature of this function:
|
||||
|
||||
```rust,ignore
|
||||
fn first_word(s: &String) -> ?
|
||||
```
|
||||
|
||||
This function, `first_word`, has a `&String` as a parameter. We don’t want
|
||||
ownership, so this is fine. But what should we return? We don’t really have a
|
||||
way to talk about *part* of a string. However, we could return the index of the
|
||||
end of the word. Let’s try that, as shown in Listing 4-7:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn first_word(s: &String) -> usize {
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &item) in bytes.iter().enumerate() {
|
||||
if item == b' ' {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
s.len()
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-7: The `first_word` function that returns a
|
||||
byte index value into the `String` parameter</span>
|
||||
|
||||
Because we need to go through the `String` element by element and check whether
|
||||
a value is a space, we’ll convert our `String` to an array of bytes using the
|
||||
`as_bytes` method:
|
||||
|
||||
```rust,ignore
|
||||
let bytes = s.as_bytes();
|
||||
```
|
||||
|
||||
Next, we create an iterator over the array of bytes using the `iter` method:
|
||||
|
||||
```rust,ignore
|
||||
for (i, &item) in bytes.iter().enumerate() {
|
||||
```
|
||||
|
||||
We’ll discuss iterators in more detail in Chapter 13. For now, know that `iter`
|
||||
is a method that returns each element in a collection and that `enumerate`
|
||||
wraps the result of `iter` and returns each element as part of a tuple instead.
|
||||
The first element of the tuple returned from `enumerate` is the index, and the
|
||||
second element is a reference to the element. This is a bit more convenient
|
||||
than calculating the index ourselves.
|
||||
|
||||
Because the `enumerate` method returns a tuple, we can use patterns to
|
||||
destructure that tuple, just like everywhere else in Rust. So in the `for`
|
||||
loop, we specify a pattern that has `i` for the index in the tuple and `&item`
|
||||
for the single byte in the tuple. Because we get a reference to the element
|
||||
from `.iter().enumerate()`, we use `&` in the pattern.
|
||||
|
||||
Inside the `for` loop, we search for the byte that represents the space by
|
||||
using the byte literal syntax. If we find a space, we return the position.
|
||||
Otherwise, we return the length of the string by using `s.len()`:
|
||||
|
||||
```rust,ignore
|
||||
if item == b' ' {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
s.len()
|
||||
```
|
||||
|
||||
We now have a way to find out the index of the end of the first word in the
|
||||
string, but there’s a problem. We’re returning a `usize` on its own, but it’s
|
||||
only a meaningful number in the context of the `&String`. In other words,
|
||||
because it’s a separate value from the `String`, there’s no guarantee that it
|
||||
will still be valid in the future. Consider the program in Listing 4-8 that
|
||||
uses the `first_word` function from Listing 4-7:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# fn first_word(s: &String) -> usize {
|
||||
# let bytes = s.as_bytes();
|
||||
#
|
||||
# for (i, &item) in bytes.iter().enumerate() {
|
||||
# if item == b' ' {
|
||||
# return i;
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# s.len()
|
||||
# }
|
||||
#
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
||||
let word = first_word(&s); // word will get the value 5
|
||||
|
||||
s.clear(); // This empties the String, making it equal to ""
|
||||
|
||||
// word still has the value 5 here, but there's no more string that
|
||||
// we could meaningfully use the value 5 with. word is now totally invalid!
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 4-8: Storing the result from calling the
|
||||
`first_word` function and then changing the `String` contents</span>
|
||||
|
||||
This program compiles without any errors and would also do so if we used `word`
|
||||
after calling `s.clear()`. Because `word` isn’t connected to the state of `s`
|
||||
at all, `word` still contains the value `5`. We could use that value `5` with
|
||||
the variable `s` to try to extract the first word out, but this would be a bug
|
||||
because the contents of `s` have changed since we saved `5` in `word`.
|
||||
|
||||
Having to worry about the index in `word` getting out of sync with the data in
|
||||
`s` is tedious and error prone! Managing these indices is even more brittle if
|
||||
we write a `second_word` function. Its signature would have to look like this:
|
||||
|
||||
```rust,ignore
|
||||
fn second_word(s: &String) -> (usize, usize) {
|
||||
```
|
||||
|
||||
Now we’re tracking a starting *and* an ending index, and we have even more
|
||||
values that were calculated from data in a particular state but aren’t tied to
|
||||
that state at all. We now have three unrelated variables floating around that
|
||||
need to be kept in sync.
|
||||
|
||||
Luckily, Rust has a solution to this problem: string slices.
|
||||
|
||||
### String Slices
|
||||
|
||||
A *string slice* is a reference to part of a `String`, and it looks like this:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello world");
|
||||
|
||||
let hello = &s[0..5];
|
||||
let world = &s[6..11];
|
||||
```
|
||||
|
||||
This is similar to taking a reference to the whole `String` but with the extra
|
||||
`[0..5]` bit. Rather than a reference to the entire `String`, it’s a reference
|
||||
to a portion of the `String`. The `start..end` syntax is a range that begins at
|
||||
`start` and continues up to, but not including, `end`.
|
||||
|
||||
We can create slices using a range within brackets by specifying
|
||||
`[starting_index..ending_index]`, where `starting_index` is the first position
|
||||
in the slice and `ending_index` is one more than the last position in the
|
||||
slice. Internally, the slice data structure stores the starting position and
|
||||
the length of the slice, which corresponds to `ending_index` minus
|
||||
`starting_index`. So in the case of `let world = &s[6..11];`, `world` would be
|
||||
a slice that contains a pointer to the 6th byte of `s` and a length value of 5.
|
||||
|
||||
Figure 4-6 shows this in a diagram.
|
||||
|
||||
<img alt="world containing a pointer to the 6th byte of String s and a length 5" src="img/trpl04-06.svg" class="center" style="width: 50%;" />
|
||||
|
||||
<span class="caption">Figure 4-6: String slice referring to part of a
|
||||
`String`</span>
|
||||
|
||||
With Rust’s `..` range syntax, if you want to start at the first index (zero),
|
||||
you can drop the value before the two periods. In other words, these are equal:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello");
|
||||
|
||||
let slice = &s[0..2];
|
||||
let slice = &s[..2];
|
||||
```
|
||||
|
||||
By the same token, if your slice includes the last byte of the `String`, you
|
||||
can drop the trailing number. That means these are equal:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello");
|
||||
|
||||
let len = s.len();
|
||||
|
||||
let slice = &s[3..len];
|
||||
let slice = &s[3..];
|
||||
```
|
||||
|
||||
You can also drop both values to take a slice of the entire string. So these
|
||||
are equal:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello");
|
||||
|
||||
let len = s.len();
|
||||
|
||||
let slice = &s[0..len];
|
||||
let slice = &s[..];
|
||||
```
|
||||
|
||||
> Note: String slice range indices must occur at valid UTF-8 character
|
||||
> boundaries. If you attempt to create a string slice in the middle of a
|
||||
> multibyte character, your program will exit with an error. For the purposes
|
||||
> of introducing string slices, we are assuming ASCII only in this section; a
|
||||
> more thorough discussion of UTF-8 handling is in the “Strings” section of
|
||||
> Chapter 8.
|
||||
|
||||
With all this information in mind, let’s rewrite `first_word` to return a
|
||||
slice. The type that signifies “string slice” is written as `&str`:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn first_word(s: &String) -> &str {
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &item) in bytes.iter().enumerate() {
|
||||
if item == b' ' {
|
||||
return &s[0..i];
|
||||
}
|
||||
}
|
||||
|
||||
&s[..]
|
||||
}
|
||||
```
|
||||
|
||||
We get the index for the end of the word in the same way as we did in Listing
|
||||
4-7, by looking for the first occurrence of a space. When we find a space, we
|
||||
return a string slice using the start of the string and the index of the space
|
||||
as the starting and ending indices.
|
||||
|
||||
Now when we call `first_word`, we get back a single value that is tied to the
|
||||
underlying data. The value is made up of a reference to the starting point of
|
||||
the slice and the number of elements in the slice.
|
||||
|
||||
Returning a slice would also work for a `second_word` function:
|
||||
|
||||
```rust,ignore
|
||||
fn second_word(s: &String) -> &str {
|
||||
```
|
||||
|
||||
We now have a straightforward API that’s much harder to mess up, because the
|
||||
compiler will ensure the references into the `String` remain valid. Remember
|
||||
the bug in the program in Listing 4-8, when we got the index to the end of the
|
||||
first word but then cleared the string so our index was invalid? That code was
|
||||
logically incorrect but didn’t show any immediate errors. The problems would
|
||||
show up later if we kept trying to use the first word index with an emptied
|
||||
string. Slices make this bug impossible and let us know we have a problem with
|
||||
our code much sooner. Using the slice version of `first_word` will throw a
|
||||
compile time error:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
||||
let word = first_word(&s);
|
||||
|
||||
s.clear(); // Error!
|
||||
}
|
||||
```
|
||||
|
||||
Here’s the compiler error:
|
||||
|
||||
```text
|
||||
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
|
||||
--> src/main.rs:6:5
|
||||
|
|
||||
4 | let word = first_word(&s);
|
||||
| - immutable borrow occurs here
|
||||
5 |
|
||||
6 | s.clear(); // Error!
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
| - immutable borrow ends here
|
||||
```
|
||||
|
||||
Recall from the borrowing rules that if we have an immutable reference to
|
||||
something, we cannot also take a mutable reference. Because `clear` needs to
|
||||
truncate the `String`, it tries to take a mutable reference, which fails. Not
|
||||
only has Rust made our API easier to use, but it has also eliminated an entire
|
||||
class of errors at compile time!
|
||||
|
||||
#### String Literals Are Slices
|
||||
|
||||
Recall that we talked about string literals being stored inside the binary. Now
|
||||
that we know about slices, we can properly understand string literals:
|
||||
|
||||
```rust
|
||||
let s = "Hello, world!";
|
||||
```
|
||||
|
||||
The type of `s` here is `&str`: it’s a slice pointing to that specific point of
|
||||
the binary. This is also why string literals are immutable; `&str` is an
|
||||
immutable reference.
|
||||
|
||||
#### String Slices as Parameters
|
||||
|
||||
Knowing that you can take slices of literals and `String`s leads us to one more
|
||||
improvement on `first_word`, and that’s its signature:
|
||||
|
||||
```rust,ignore
|
||||
fn first_word(s: &String) -> &str {
|
||||
```
|
||||
|
||||
A more experienced Rustacean would write the following line instead because it
|
||||
allows us to use the same function on both `String`s and `&str`s:
|
||||
|
||||
```rust,ignore
|
||||
fn first_word(s: &str) -> &str {
|
||||
```
|
||||
|
||||
If we have a string slice, we can pass that directly. If we have a `String`, we
|
||||
can pass a slice of the entire `String`. Defining a function to take a string
|
||||
slice instead of a reference to a `String` makes our API more general and useful
|
||||
without losing any functionality:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# fn first_word(s: &str) -> &str {
|
||||
# let bytes = s.as_bytes();
|
||||
#
|
||||
# for (i, &item) in bytes.iter().enumerate() {
|
||||
# if item == b' ' {
|
||||
# return &s[0..i];
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# &s[..]
|
||||
# }
|
||||
fn main() {
|
||||
let my_string = String::from("hello world");
|
||||
|
||||
// first_word works on slices of `String`s
|
||||
let word = first_word(&my_string[..]);
|
||||
|
||||
let my_string_literal = "hello world";
|
||||
|
||||
// first_word works on slices of string literals
|
||||
let word = first_word(&my_string_literal[..]);
|
||||
|
||||
// Because string literals *are* string slices already,
|
||||
// this works too, without the slice syntax!
|
||||
let word = first_word(my_string_literal);
|
||||
}
|
||||
```
|
||||
|
||||
### Other Slices
|
||||
|
||||
String slices, as you might imagine, are specific to strings. But there’s a
|
||||
more general slice type, too. Consider this array:
|
||||
|
||||
```rust
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
```
|
||||
|
||||
Just as we might want to refer to a part of a string, we might want to refer
|
||||
to part of an array. We’d do so like this:
|
||||
|
||||
```rust
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
||||
let slice = &a[1..3];
|
||||
```
|
||||
|
||||
This slice has the type `&[i32]`. It works the same way as string slices do, by
|
||||
storing a reference to the first element and a length. You’ll use this kind of
|
||||
slice for all sorts of other collections. We’ll discuss these collections in
|
||||
detail when we talk about vectors in Chapter 8.
|
||||
|
||||
## Summary
|
||||
|
||||
The concepts of ownership, borrowing, and slices ensure memory safety in Rust
|
||||
programs at compile time. The Rust language gives you control over your memory
|
||||
usage in the same way as other systems programming languages, but having the
|
||||
owner of data automatically clean up that data when the owner goes out of scope
|
||||
means you don’t have to write and debug extra code to get this control.
|
||||
|
||||
Ownership affects how lots of other parts of Rust work, so we’ll talk about
|
||||
these concepts further throughout the rest of the book. Let’s move on to
|
||||
Chapter 5 and look at grouping pieces of data together in a `struct`.
|
||||
11
src/doc/book/2018-edition/src/ch05-00-structs.md
Normal file
11
src/doc/book/2018-edition/src/ch05-00-structs.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Using Structs to Structure Related Data
|
||||
|
||||
A *struct*, or *structure*, is a custom data type that lets you name and
|
||||
package together multiple related values that make up a meaningful group. If
|
||||
you’re familiar with an object-oriented language, a *struct* is like an
|
||||
object’s data attributes. In this chapter, we’ll compare and contrast tuples
|
||||
with structs, demonstrate how to use structs, and discuss how to define methods
|
||||
and associated functions to specify behavior associated with a struct’s data.
|
||||
Structs and enums (discussed in Chapter 6) are the building blocks for creating
|
||||
new types in your program’s domain to take full advantage of Rust’s compile
|
||||
time type checking.
|
||||
310
src/doc/book/2018-edition/src/ch05-01-defining-structs.md
Normal file
310
src/doc/book/2018-edition/src/ch05-01-defining-structs.md
Normal file
@ -0,0 +1,310 @@
|
||||
## Defining and Instantiating Structs
|
||||
|
||||
Structs are similar to tuples, which were discussed in Chapter 3. Like tuples,
|
||||
the pieces of a struct can be different types. Unlike with tuples, you’ll name
|
||||
each piece of data so it’s clear what the values mean. As a result of these
|
||||
names, structs are more flexible than tuples: you don’t have to rely on the
|
||||
order of the data to specify or access the values of an instance.
|
||||
|
||||
To define a struct, we enter the keyword `struct` and name the entire struct. A
|
||||
struct’s name should describe the significance of the pieces of data being
|
||||
grouped together. Then, inside curly brackets, we define the names and types of
|
||||
the pieces of data, which we call *fields*. For example, Listing 5-1 shows a
|
||||
struct that stores information about a user account:
|
||||
|
||||
```rust
|
||||
struct User {
|
||||
username: String,
|
||||
email: String,
|
||||
sign_in_count: u64,
|
||||
active: bool,
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-1: A `User` struct definition</span>
|
||||
|
||||
To use a struct after we’ve defined it, we create an *instance* of that struct
|
||||
by specifying concrete values for each of the fields. We create an instance by
|
||||
stating the name of the struct and then add curly brackets containing `key:
|
||||
value` pairs, where the keys are the names of the fields and the values are the
|
||||
data we want to store in those fields. We don’t have to specify the fields in
|
||||
the same order in which we declared them in the struct. In other words, the
|
||||
struct definition is like a general template for the type, and instances fill
|
||||
in that template with particular data to create values of the type. For
|
||||
example, we can declare a particular user as shown in Listing 5-2:
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
let user1 = User {
|
||||
email: String::from("someone@example.com"),
|
||||
username: String::from("someusername123"),
|
||||
active: true,
|
||||
sign_in_count: 1,
|
||||
};
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-2: Creating an instance of the `User`
|
||||
struct</span>
|
||||
|
||||
To get a specific value from a struct, we can use dot notation. If we wanted
|
||||
just this user’s email address, we could use `user1.email` wherever we wanted
|
||||
to use this value. If the instance is mutable, we can change a value by using
|
||||
the dot notation and assigning into a particular field. Listing 5-3 shows how
|
||||
to change the value in the `email` field of a mutable `User` instance:
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
let mut user1 = User {
|
||||
email: String::from("someone@example.com"),
|
||||
username: String::from("someusername123"),
|
||||
active: true,
|
||||
sign_in_count: 1,
|
||||
};
|
||||
|
||||
user1.email = String::from("anotheremail@example.com");
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-3: Changing the value in the `email` field of a
|
||||
`User` instance</span>
|
||||
|
||||
Note that the entire instance must be mutable; Rust doesn’t allow us to mark
|
||||
only certain fields as mutable.
|
||||
|
||||
As with any expression, we can construct a new instance of the struct as the
|
||||
last expression in the function body to implicitly return that new instance.
|
||||
Listing 5-4 shows a `build_user` function that returns a `User` instance with
|
||||
the given email and username. The `active` field gets the value of `true`, and
|
||||
the `sign_in_count` gets a value of `1`.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
fn build_user(email: String, username: String) -> User {
|
||||
User {
|
||||
email: email,
|
||||
username: username,
|
||||
active: true,
|
||||
sign_in_count: 1,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-4: A `build_user` function that takes an email
|
||||
and username and returns a `User` instance</span>
|
||||
|
||||
It makes sense to name the function parameters with the same name as the struct
|
||||
fields, but having to repeat the `email` and `username` field names and
|
||||
variables is a bit tedious. If the struct had more fields, repeating each name
|
||||
would get even more annoying. Luckily, there’s a convenient shorthand!
|
||||
|
||||
### Using the Field Init Shorthand when Variables and Fields Have the Same Name
|
||||
|
||||
Because the parameter names and the struct field names are exactly the same in
|
||||
Listing 5-4, we can use the *field init shorthand* syntax to rewrite
|
||||
`build_user` so that it behaves exactly the same but doesn’t have the
|
||||
repetition of `email` and `username` as shown in Listing 5-5.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
fn build_user(email: String, username: String) -> User {
|
||||
User {
|
||||
email,
|
||||
username,
|
||||
active: true,
|
||||
sign_in_count: 1,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-5: A `build_user` function that uses field init
|
||||
shorthand because the `email` and `username` parameters have the same name as
|
||||
struct fields</span>
|
||||
|
||||
Here, we’re creating a new instance of the `User` struct, which has a field
|
||||
named `email`. We want to set the `email` field’s value to the value in the
|
||||
`email` parameter of the `build_user` function. Because the `email` field and
|
||||
the `email` parameter have the same name, we only need to write `email` rather
|
||||
than `email: email`.
|
||||
|
||||
### Creating Instances From Other Instances With Struct Update Syntax
|
||||
|
||||
It’s often useful to create a new instance of a struct that uses most of an old
|
||||
instance’s values but changes some. You’ll do this using *struct update syntax*.
|
||||
|
||||
First, Listing 5-6 shows how we create a new `User` instance in `user2` without
|
||||
the update syntax. We set new values for `email` and `username` but otherwise
|
||||
use the same values from `user1` that we created in Listing 5-2:
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
# let user1 = User {
|
||||
# email: String::from("someone@example.com"),
|
||||
# username: String::from("someusername123"),
|
||||
# active: true,
|
||||
# sign_in_count: 1,
|
||||
# };
|
||||
#
|
||||
let user2 = User {
|
||||
email: String::from("another@example.com"),
|
||||
username: String::from("anotherusername567"),
|
||||
active: user1.active,
|
||||
sign_in_count: user1.sign_in_count,
|
||||
};
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-6: Creating a new `User` instance using some of
|
||||
the values from `user1`</span>
|
||||
|
||||
Using struct update syntax, we can achieve the same effect with less code, as
|
||||
shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
|
||||
explicitly set should have the same value as the fields in the given instance.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
# username: String,
|
||||
# email: String,
|
||||
# sign_in_count: u64,
|
||||
# active: bool,
|
||||
# }
|
||||
#
|
||||
# let user1 = User {
|
||||
# email: String::from("someone@example.com"),
|
||||
# username: String::from("someusername123"),
|
||||
# active: true,
|
||||
# sign_in_count: 1,
|
||||
# };
|
||||
#
|
||||
let user2 = User {
|
||||
email: String::from("another@example.com"),
|
||||
username: String::from("anotherusername567"),
|
||||
..user1
|
||||
};
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-7: Using struct update syntax to set new
|
||||
`email` and `username` values for a `User` instance but use the rest of the
|
||||
values from the fields of the instance in the `user1` variable</span>
|
||||
|
||||
The code in Listing 5-7 also creates an instance in `user2` that has a
|
||||
different value for `email` and `username` but has the same values for the
|
||||
`active` and `sign_in_count` fields from `user1`.
|
||||
|
||||
### Tuple Structs without Named Fields to Create Different Types
|
||||
|
||||
You can also define structs that look similar to tuples, called *tuple
|
||||
structs*. Tuple structs have the added meaning the struct name provides but
|
||||
don’t have names associated with their fields; rather, they just have the types
|
||||
of the fields. Tuple structs are useful when you want to give the whole tuple a
|
||||
name and make the tuple be a different type than other tuples, and naming each
|
||||
field as in a regular struct would be verbose or redundant.
|
||||
|
||||
To define a tuple struct start with the `struct` keyword and the struct name
|
||||
followed by the types in the tuple. For example, here are definitions and
|
||||
usages of two tuple structs named `Color` and `Point`:
|
||||
|
||||
```rust
|
||||
struct Color(i32, i32, i32);
|
||||
struct Point(i32, i32, i32);
|
||||
|
||||
let black = Color(0, 0, 0);
|
||||
let origin = Point(0, 0, 0);
|
||||
```
|
||||
|
||||
Note that the `black` and `origin` values are different types, because they’re
|
||||
instances of different tuple structs. Each struct you define is its own type,
|
||||
even though the fields within the struct have the same types. For example, a
|
||||
function that takes a parameter of type `Color` cannot take a `Point` as an
|
||||
argument, even though both types are made up of three `i32` values. Otherwise,
|
||||
tuple struct instances behave like tuples: you can destructure them into their
|
||||
individual pieces, you can use a `.` followed by the index to access an
|
||||
individual value, and so on.
|
||||
|
||||
### Unit-Like Structs Without Any Fields
|
||||
|
||||
You can also define structs that don’t have any fields! These are called
|
||||
*unit-like structs* because they behave similarly to `()`, the unit type.
|
||||
Unit-like structs can be useful in situations in which you need to implement a
|
||||
trait on some type but don’t have any data that you want to store in the type
|
||||
itself. We’ll discuss traits in Chapter 10.
|
||||
|
||||
> ### Ownership of Struct Data
|
||||
>
|
||||
> In the `User` struct definition in Listing 5-1, we used the owned `String`
|
||||
> type rather than the `&str` string slice type. This is a deliberate choice
|
||||
> because we want instances of this struct to own all of its data and for that
|
||||
> data to be valid for as long as the entire struct is valid.
|
||||
>
|
||||
> It’s possible for structs to store references to data owned by something else,
|
||||
> but to do so requires the use of *lifetimes*, a Rust feature that we’ll
|
||||
> discuss in Chapter 10. Lifetimes ensure that the data referenced by a struct
|
||||
> is valid for as long as the struct is. Let’s say you try to store a reference
|
||||
> in a struct without specifying lifetimes, like this, which won’t work:
|
||||
>
|
||||
> <span class="filename">Filename: src/main.rs</span>
|
||||
>
|
||||
> ```rust,ignore
|
||||
> struct User {
|
||||
> username: &str,
|
||||
> email: &str,
|
||||
> sign_in_count: u64,
|
||||
> active: bool,
|
||||
> }
|
||||
>
|
||||
> fn main() {
|
||||
> let user1 = User {
|
||||
> email: "someone@example.com",
|
||||
> username: "someusername123",
|
||||
> active: true,
|
||||
> sign_in_count: 1,
|
||||
> };
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> The compiler will complain that it needs lifetime specifiers:
|
||||
>
|
||||
> ```text
|
||||
> error[E0106]: missing lifetime specifier
|
||||
> -->
|
||||
> |
|
||||
> 2 | username: &str,
|
||||
> | ^ expected lifetime parameter
|
||||
>
|
||||
> error[E0106]: missing lifetime specifier
|
||||
> -->
|
||||
> |
|
||||
> 3 | email: &str,
|
||||
> | ^ expected lifetime parameter
|
||||
> ```
|
||||
>
|
||||
> In Chapter 10, we’ll discuss how to fix these errors so you can store
|
||||
> references in structs, but for now, we’ll fix errors like these using owned
|
||||
> types like `String` instead of references like `&str`.
|
||||
264
src/doc/book/2018-edition/src/ch05-02-example-structs.md
Normal file
264
src/doc/book/2018-edition/src/ch05-02-example-structs.md
Normal file
@ -0,0 +1,264 @@
|
||||
## An Example Program Using Structs
|
||||
|
||||
To understand when we might want to use structs, let’s write a program that
|
||||
calculates the area of a rectangle. We’ll start with single variables, and then
|
||||
refactor the program until we’re using structs instead.
|
||||
|
||||
Let’s make a new binary project with Cargo called *rectangles* that will take
|
||||
the width and height of a rectangle specified in pixels and calculate the area
|
||||
of the rectangle. Listing 5-8 shows a short program with one way of doing
|
||||
exactly that in our project’s *src/main.rs*:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let width1 = 30;
|
||||
let height1 = 50;
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(width1, height1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
width * height
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-8: Calculating the area of a rectangle
|
||||
specified by separate width and height variables</span>
|
||||
|
||||
Now, run this program using `cargo run`:
|
||||
|
||||
```text
|
||||
The area of the rectangle is 1500 square pixels.
|
||||
```
|
||||
|
||||
Even though Listing 5-8 works and figures out the area of the rectangle by
|
||||
calling the `area` function with each dimension, we can do better. The width
|
||||
and the height are related to each other because together they describe one
|
||||
rectangle.
|
||||
|
||||
The issue with this code is evident in the signature of `area`:
|
||||
|
||||
```rust,ignore
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
```
|
||||
|
||||
The `area` function is supposed to calculate the area of one rectangle, but the
|
||||
function we wrote has two parameters. The parameters are related, but that’s
|
||||
not expressed anywhere in our program. It would be more readable and more
|
||||
manageable to group width and height together. We’ve already discussed one way
|
||||
we might do that in “The Tuple Type” section of Chapter 3: by using tuples.
|
||||
|
||||
### Refactoring with Tuples
|
||||
|
||||
Listing 5-9 shows another version of our program that uses tuples:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let rect1 = (30, 50);
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(rect1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(dimensions: (u32, u32)) -> u32 {
|
||||
dimensions.0 * dimensions.1
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-9: Specifying the width and height of the
|
||||
rectangle with a tuple</span>
|
||||
|
||||
In one way, this program is better. Tuples let us add a bit of structure, and
|
||||
we’re now passing just one argument. But in another way, this version is less
|
||||
clear: tuples don’t name their elements, so our calculation has become more
|
||||
confusing because we have to index into the parts of the tuple.
|
||||
|
||||
It doesn’t matter if we mix up width and height for the area calculation, but
|
||||
if we want to draw the rectangle on the screen, it would matter! We would have
|
||||
to keep in mind that `width` is the tuple index `0` and `height` is the tuple
|
||||
index `1`. If someone else worked on this code, they would have to figure this
|
||||
out and keep it in mind as well. It would be easy to forget or mix up these
|
||||
values and cause errors, because we haven’t conveyed the meaning of our data in
|
||||
our code.
|
||||
|
||||
### Refactoring with Structs: Adding More Meaning
|
||||
|
||||
We use structs to add meaning by labeling the data. We can transform the tuple
|
||||
we’re using into a data type with a name for the whole as well as names for the
|
||||
parts, as shown in Listing 5-10:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(&rect1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-10: Defining a `Rectangle` struct</span>
|
||||
|
||||
Here we’ve defined a struct and named it `Rectangle`. Inside the curly
|
||||
brackets, we defined the fields as `width` and `height`, both of which have
|
||||
type `u32`. Then in `main`, we created a particular instance of `Rectangle`
|
||||
that has a width of 30 and a height of 50.
|
||||
|
||||
Our `area` function is now defined with one parameter, which we’ve named
|
||||
`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
|
||||
instance. As mentioned in Chapter 4, we want to borrow the struct rather than
|
||||
take ownership of it. This way, `main` retains its ownership and can continue
|
||||
using `rect1`, which is the reason we use the `&` in the function signature and
|
||||
where we call the function.
|
||||
|
||||
The `area` function accesses the `width` and `height` fields of the `Rectangle`
|
||||
instance. Our function signature for `area` now says exactly what we mean:
|
||||
calculate the area of `Rectangle`, using its `width` and `height` fields. This
|
||||
conveys that the width and height are related to each other, and it gives
|
||||
descriptive names to the values rather than using the tuple index values of `0`
|
||||
and `1`. This is a win for clarity.
|
||||
|
||||
### Adding Useful Functionality with Derived Traits
|
||||
|
||||
It’d be nice to be able to print an instance of `Rectangle` while we’re
|
||||
debugging our program and see the values for all its fields. Listing 5-11 tries
|
||||
using the `println!` macro as we have used in previous chapters. This won’t
|
||||
work, however:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {}", rect1);
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-11: Attempting to print a `Rectangle`
|
||||
instance</span>
|
||||
|
||||
When we run this code, we get an error with this core message:
|
||||
|
||||
```text
|
||||
error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied
|
||||
```
|
||||
|
||||
The `println!` macro can do many kinds of formatting, and by default, curly
|
||||
brackets tell `println!` to use formatting known as `Display`: output intended
|
||||
for direct end user consumption. The primitive types we’ve seen so far
|
||||
implement `Display` by default, because there’s only one way you’d want to show
|
||||
a `1` or any other primitive type to a user. But with structs, the way
|
||||
`println!` should format the output is less clear because there are more
|
||||
display possibilities: Do you want commas or not? Do you want to print the
|
||||
curly brackets? Should all the fields be shown? Due to this ambiguity, Rust
|
||||
doesn’t try to guess what we want, and structs don’t have a provided
|
||||
implementation of `Display`.
|
||||
|
||||
If we continue reading the errors, we’ll find this helpful note:
|
||||
|
||||
```text
|
||||
`Rectangle` cannot be formatted with the default formatter; try using
|
||||
`:?` instead if you are using a format string
|
||||
```
|
||||
|
||||
Let’s try it! The `println!` macro call will now look like `println!("rect1 is
|
||||
{:?}", rect1);`. Putting the specifier `:?` inside the curly brackets tells
|
||||
`println!` we want to use an output format called `Debug`. `Debug` is a trait
|
||||
that enables us to print our struct in a way that is useful for developers so
|
||||
we can see its value while we’re debugging our code.
|
||||
|
||||
Run the code with this change. Drat! We still get an error:
|
||||
|
||||
```text
|
||||
error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
|
||||
```
|
||||
|
||||
But again, the compiler gives us a helpful note:
|
||||
|
||||
```text
|
||||
`Rectangle` cannot be formatted using `:?`; if it is defined in your
|
||||
crate, add `#[derive(Debug)]` or manually implement it
|
||||
```
|
||||
|
||||
Rust *does* include functionality to print out debugging information, but we
|
||||
have to explicitly opt in to make that functionality available for our struct.
|
||||
To do that, we add the annotation `#[derive(Debug)]` just before the struct
|
||||
definition, as shown in Listing 5-12:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {:?}", rect1);
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-12: Adding the annotation to derive the `Debug`
|
||||
trait and printing the `Rectangle` instance using debug formatting</span>
|
||||
|
||||
Now when we run the program, we won’t get any errors, and we’ll see the
|
||||
following output:
|
||||
|
||||
```text
|
||||
rect1 is Rectangle { width: 30, height: 50 }
|
||||
```
|
||||
|
||||
Nice! It’s not the prettiest output, but it shows the values of all the fields
|
||||
for this instance, which would definitely help during debugging. When we have
|
||||
larger structs, it’s useful to have output that’s a bit easier to read; in
|
||||
those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string.
|
||||
When we use the `{:#?}` style in the example, the output will look like this:
|
||||
|
||||
```text
|
||||
rect1 is Rectangle {
|
||||
width: 30,
|
||||
height: 50
|
||||
}
|
||||
```
|
||||
|
||||
Rust has provided a number of traits for us to use with the `derive` annotation
|
||||
that can add useful behavior to our custom types. Those traits and their
|
||||
behaviors are listed in Appendix C, “Derivable Traits.” We’ll cover how to
|
||||
implement these traits with custom behavior as well as how to create your own
|
||||
traits in Chapter 10.
|
||||
|
||||
Our `area` function is very specific: it only computes the area of rectangles.
|
||||
It would be helpful to tie this behavior more closely to our `Rectangle`
|
||||
struct, because it won’t work with any other type. Let’s look at how we can
|
||||
continue to refactor this code by turning the `area` function into an `area`
|
||||
*method* defined on our `Rectangle` type.
|
||||
276
src/doc/book/2018-edition/src/ch05-03-method-syntax.md
Normal file
276
src/doc/book/2018-edition/src/ch05-03-method-syntax.md
Normal file
@ -0,0 +1,276 @@
|
||||
## Method Syntax
|
||||
|
||||
*Methods* are similar to functions: they’re declared with the `fn` keyword and
|
||||
their name, they can have parameters and a return value, and they contain some
|
||||
code that is run when they’re called from somewhere else. However, methods are
|
||||
different from functions in that they’re defined within the context of a struct
|
||||
(or an enum or a trait object, which we cover in Chapters 6 and 17,
|
||||
respectively), and their first parameter is always `self`, which represents the
|
||||
instance of the struct the method is being called on.
|
||||
|
||||
### Defining Methods
|
||||
|
||||
Let’s change the `area` function that has a `Rectangle` instance as a parameter
|
||||
and instead make an `area` method defined on the `Rectangle` struct, as shown
|
||||
in Listing 5-13:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
rect1.area()
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-13: Defining an `area` method on the
|
||||
`Rectangle` struct</span>
|
||||
|
||||
To define the function within the context of `Rectangle`, we start an `impl`
|
||||
(implementation) block. Then we move the `area` function within the `impl`
|
||||
curly brackets and change the first (and in this case, only) parameter to be
|
||||
`self` in the signature and everywhere within the body. In `main`, where we
|
||||
called the `area` function and passed `rect1` as an argument, we can instead
|
||||
use *method syntax* to call the `area` method on our `Rectangle` instance.
|
||||
The method syntax goes after an instance: we add a dot followed by the method
|
||||
name, parentheses, and any arguments.
|
||||
|
||||
In the signature for `area`, we use `&self` instead of `rectangle: &Rectangle`
|
||||
because Rust knows the type of `self` is `Rectangle` due to this method’s being
|
||||
inside the `impl Rectangle` context. Note that we still need to use the `&`
|
||||
before `self`, just as we did in `&Rectangle`. Methods can take ownership of
|
||||
`self`, borrow `self` immutably as we’ve done here, or borrow `self` mutably,
|
||||
just as they can any other parameter.
|
||||
|
||||
We’ve chosen `&self` here for the same reason we used `&Rectangle` in the
|
||||
function version: we don’t want to take ownership, and we just want to read the
|
||||
data in the struct, not write to it. If we wanted to change the instance that
|
||||
we’ve called the method on as part of what the method does, we’d use `&mut
|
||||
self` as the first parameter. Having a method that takes ownership of the
|
||||
instance by using just `self` as the first parameter is rare; this technique is
|
||||
usually used when the method transforms `self` into something else and you want
|
||||
to prevent the caller from using the original instance after the transformation.
|
||||
|
||||
The main benefit of using methods instead of functions, in addition to using
|
||||
method syntax and not having to repeat the type of `self` in every method’s
|
||||
signature, is for organization. We’ve put all the things we can do with an
|
||||
instance of a type in one `impl` block rather than making future users of our
|
||||
code search for capabilities of `Rectangle` in various places in the library we
|
||||
provide.
|
||||
|
||||
> ### Where’s the `->` Operator?
|
||||
>
|
||||
> In C and C++, two different operators are used for calling methods: you use
|
||||
> `.` if you’re calling a method on the object directly and `->` if you’re
|
||||
> calling the method on a pointer to the object and need to dereference the
|
||||
> pointer first. In other words, if `object` is a pointer,
|
||||
> `object->something()` is similar to `(*object).something()`.
|
||||
>
|
||||
> Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a
|
||||
> feature called *automatic referencing and dereferencing*. Calling methods is
|
||||
> one of the few places in Rust that has this behavior.
|
||||
>
|
||||
> Here’s how it works: when you call a method with `object.something()`, Rust
|
||||
> automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of
|
||||
> the method. In other words, the following are the same:
|
||||
>
|
||||
> ```rust
|
||||
> # #[derive(Debug,Copy,Clone)]
|
||||
> # struct Point {
|
||||
> # x: f64,
|
||||
> # y: f64,
|
||||
> # }
|
||||
> #
|
||||
> # impl Point {
|
||||
> # fn distance(&self, other: &Point) -> f64 {
|
||||
> # let x_squared = f64::powi(other.x - self.x, 2);
|
||||
> # let y_squared = f64::powi(other.y - self.y, 2);
|
||||
> #
|
||||
> # f64::sqrt(x_squared + y_squared)
|
||||
> # }
|
||||
> # }
|
||||
> # let p1 = Point { x: 0.0, y: 0.0 };
|
||||
> # let p2 = Point { x: 5.0, y: 6.5 };
|
||||
> p1.distance(&p2);
|
||||
> (&p1).distance(&p2);
|
||||
> ```
|
||||
>
|
||||
> The first one looks much cleaner. This automatic referencing behavior works
|
||||
> because methods have a clear receiver—the type of `self`. Given the receiver
|
||||
> and name of a method, Rust can figure out definitively whether the method is
|
||||
> reading (`&self`), mutating (`&mut self`), or consuming (`self`). The fact
|
||||
> that Rust makes borrowing implicit for method receivers is a big part of
|
||||
> making ownership ergonomic in practice.
|
||||
|
||||
### Methods with More Parameters
|
||||
|
||||
Let’s practice using methods by implementing a second method on the `Rectangle`
|
||||
struct. This time, we want an instance of `Rectangle` to take another instance
|
||||
of `Rectangle` and return `true` if the second `Rectangle` can fit completely
|
||||
within `self`; otherwise it should return `false`. That is, we want to be able
|
||||
to write the program shown in Listing 5-14, once we’ve defined the `can_hold`
|
||||
method:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
let rect2 = Rectangle { width: 10, height: 40 };
|
||||
let rect3 = Rectangle { width: 60, height: 45 };
|
||||
|
||||
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
|
||||
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-14: Using the as-yet-unwritten `can_hold`
|
||||
method</span>
|
||||
|
||||
And the expected output would look like the following, because both dimensions
|
||||
of `rect2` are smaller than the dimensions of `rect1` but `rect3` is wider than
|
||||
`rect1`:
|
||||
|
||||
```text
|
||||
Can rect1 hold rect2? true
|
||||
Can rect1 hold rect3? false
|
||||
```
|
||||
|
||||
We know we want to define a method, so it will be within the `impl Rectangle`
|
||||
block. The method name will be `can_hold`, and it will take an immutable borrow
|
||||
of another `Rectangle` as a parameter. We can tell what the type of the
|
||||
parameter will be by looking at the code that calls the method:
|
||||
`rect1.can_hold(&rect2)` passes in `&rect2`, which is an immutable borrow to
|
||||
`rect2`, an instance of `Rectangle`. This makes sense because we only need to
|
||||
read `rect2` (rather than write, which would mean we’d need a mutable borrow),
|
||||
and we want `main` to retain ownership of `rect2` so we can use it again after
|
||||
calling the `can_hold` method. The return value of `can_hold` will be a
|
||||
Boolean, and the implementation will check whether the width and height of
|
||||
`self` are both greater than the width and height of the other `Rectangle`,
|
||||
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||
Listing 5-13, shown in Listing 5-15:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-15: Implementing the `can_hold` method on
|
||||
`Rectangle` that takes another `Rectangle` instance as a parameter</span>
|
||||
|
||||
When we run this code with the `main` function in Listing 5-14, we’ll get our
|
||||
desired output. Methods can take multiple parameters that we add to the
|
||||
signature after the `self` parameter, and those parameters work just like
|
||||
parameters in functions.
|
||||
|
||||
### Associated Functions
|
||||
|
||||
Another useful feature of `impl` blocks is that we’re allowed to define
|
||||
functions within `impl` blocks that *don’t* take `self` as a parameter. These
|
||||
are called *associated functions* because they’re associated with the struct.
|
||||
They’re still functions, not methods, because they don’t have an instance of
|
||||
the struct to work with. You’ve already used the `String::from` associated
|
||||
function.
|
||||
|
||||
Associated functions are often used for constructors that will return a new
|
||||
instance of the struct. For example, we could provide an associated function
|
||||
that would have one dimension parameter and use that as both width and height,
|
||||
thus making it easier to create a square `Rectangle` rather than having to
|
||||
specify the same value twice:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn square(size: u32) -> Rectangle {
|
||||
Rectangle { width: size, height: size }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To call this associated function, we use the `::` syntax with the struct name;
|
||||
`let sq = Rectangle::square(3);` is an example. This function is namespaced by
|
||||
the struct: the `::` syntax is used for both associated functions and
|
||||
namespaces created by modules. We’ll discuss modules in Chapter 7.
|
||||
|
||||
### Multiple `impl` Blocks
|
||||
|
||||
Each struct is allowed to have multiple `impl` blocks. For example, Listing
|
||||
5-15 is equivalent to the code shown in Listing 5-16, which has each method
|
||||
in its own `impl` block:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-16: Rewriting Listing 5-15 using multiple `impl`
|
||||
blocks</span>
|
||||
|
||||
There’s no reason to separate these methods into multiple `impl` blocks here,
|
||||
but this is valid syntax. We’ll see a case in which multiple `impl` blocks are
|
||||
useful in Chapter 10 where we discuss generic types and traits.
|
||||
|
||||
## Summary
|
||||
|
||||
Structs let you create custom types that are meaningful for your domain. By
|
||||
using structs, you can keep associated pieces of data connected to each other
|
||||
and name each piece to make your code clear. Methods let you specify the
|
||||
behavior that instances of your structs have, and associated functions let you
|
||||
namespace functionality that is particular to your struct without having an
|
||||
instance available.
|
||||
|
||||
But structs aren’t the only way you can create custom types: let’s turn to
|
||||
Rust’s enum feature to add another tool to your toolbox.
|
||||
15
src/doc/book/2018-edition/src/ch06-00-enums.md
Normal file
15
src/doc/book/2018-edition/src/ch06-00-enums.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Enums and Pattern Matching
|
||||
|
||||
In this chapter we’ll look at *enumerations*, also referred to as *enums*.
|
||||
Enums allow you to define a type by enumerating its possible values. First,
|
||||
we’ll define and use an enum to show how an enum can encode meaning along with
|
||||
data. Next, we’ll explore a particularly useful enum, called `Option`, which
|
||||
expresses that a value can be either something or nothing. Then we’ll look at
|
||||
how pattern matching in the `match` expression makes it easy to run different
|
||||
code for different values of an enum. Finally, we’ll cover how the `if let`
|
||||
construct is another convenient and concise idiom available to you to handle
|
||||
enums in your code.
|
||||
|
||||
Enums are a feature in many languages, but their capabilities differ in each
|
||||
language. Rust’s enums are most similar to *algebraic data types* in functional
|
||||
languages, such as F#, OCaml, and Haskell.
|
||||
399
src/doc/book/2018-edition/src/ch06-01-defining-an-enum.md
Normal file
399
src/doc/book/2018-edition/src/ch06-01-defining-an-enum.md
Normal file
@ -0,0 +1,399 @@
|
||||
## Defining an Enum
|
||||
|
||||
Let’s look at a situation we might want to express in code and see why enums
|
||||
are useful and more appropriate than structs in this case. Say we need to work
|
||||
with IP addresses. Currently, two major standards are used for IP addresses:
|
||||
version four and version six. These are the only possibilities for an IP
|
||||
address that our program will come across: we can *enumerate* all possible
|
||||
values, which is where enumeration gets its name.
|
||||
|
||||
Any IP address can be either a version four or a version six address, but not
|
||||
both at the same time. That property of IP addresses makes the enum data
|
||||
structure appropriate, because enum values can only be one of the variants.
|
||||
Both version four and version six addresses are still fundamentally IP
|
||||
addresses, so they should be treated as the same type when the code is handling
|
||||
situations that apply to any kind of IP address.
|
||||
|
||||
We can express this concept in code by defining an `IpAddrKind` enumeration and
|
||||
listing the possible kinds an IP address can be, `V4` and `V6`. These are known
|
||||
as the *variants* of the enum:
|
||||
|
||||
```rust
|
||||
enum IpAddrKind {
|
||||
V4,
|
||||
V6,
|
||||
}
|
||||
```
|
||||
|
||||
`IpAddrKind` is now a custom data type that we can use elsewhere in our code.
|
||||
|
||||
### Enum Values
|
||||
|
||||
We can create instances of each of the two variants of `IpAddrKind` like this:
|
||||
|
||||
```rust
|
||||
# enum IpAddrKind {
|
||||
# V4,
|
||||
# V6,
|
||||
# }
|
||||
#
|
||||
let four = IpAddrKind::V4;
|
||||
let six = IpAddrKind::V6;
|
||||
```
|
||||
|
||||
Note that the variants of the enum are namespaced under its identifier, and we
|
||||
use a double colon to separate the two. The reason this is useful is that now
|
||||
both values `IpAddrKind::V4` and `IpAddrKind::V6` are of the same type:
|
||||
`IpAddrKind`. We can then, for instance, define a function that takes any
|
||||
`IpAddrKind`:
|
||||
|
||||
```rust
|
||||
# enum IpAddrKind {
|
||||
# V4,
|
||||
# V6,
|
||||
# }
|
||||
#
|
||||
fn route(ip_type: IpAddrKind) { }
|
||||
```
|
||||
|
||||
And we can call this function with either variant:
|
||||
|
||||
```rust
|
||||
# enum IpAddrKind {
|
||||
# V4,
|
||||
# V6,
|
||||
# }
|
||||
#
|
||||
# fn route(ip_type: IpAddrKind) { }
|
||||
#
|
||||
route(IpAddrKind::V4);
|
||||
route(IpAddrKind::V6);
|
||||
```
|
||||
|
||||
Using enums has even more advantages. Thinking more about our IP address type,
|
||||
at the moment we don’t have a way to store the actual IP address *data*; we
|
||||
only know what *kind* it is. Given that you just learned about structs in
|
||||
Chapter 5, you might tackle this problem as shown in Listing 6-1:
|
||||
|
||||
```rust
|
||||
enum IpAddrKind {
|
||||
V4,
|
||||
V6,
|
||||
}
|
||||
|
||||
struct IpAddr {
|
||||
kind: IpAddrKind,
|
||||
address: String,
|
||||
}
|
||||
|
||||
let home = IpAddr {
|
||||
kind: IpAddrKind::V4,
|
||||
address: String::from("127.0.0.1"),
|
||||
};
|
||||
|
||||
let loopback = IpAddr {
|
||||
kind: IpAddrKind::V6,
|
||||
address: String::from("::1"),
|
||||
};
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-1: Storing the data and `IpAddrKind` variant of
|
||||
an IP address using a `struct`</span>
|
||||
|
||||
Here, we’ve defined a struct `IpAddr` that has two fields: a `kind` field that
|
||||
is of type `IpAddrKind` (the enum we defined previously) and an `address` field
|
||||
of type `String`. We have two instances of this struct. The first, `home`, has
|
||||
the value `IpAddrKind::V4` as its `kind` with associated address data of
|
||||
`127.0.0.1`. The second instance, `loopback`, has the other variant of
|
||||
`IpAddrKind` as its `kind` value, `V6`, and has address `::1` associated with
|
||||
it. We’ve used a struct to bundle the `kind` and `address` values together, so
|
||||
now the variant is associated with the value.
|
||||
|
||||
We can represent the same concept in a more concise way using just an enum,
|
||||
rather than an enum inside a struct, by putting data directly into each enum
|
||||
variant. This new definition of the `IpAddr` enum says that both `V4` and `V6`
|
||||
variants will have associated `String` values:
|
||||
|
||||
```rust
|
||||
enum IpAddr {
|
||||
V4(String),
|
||||
V6(String),
|
||||
}
|
||||
|
||||
let home = IpAddr::V4(String::from("127.0.0.1"));
|
||||
|
||||
let loopback = IpAddr::V6(String::from("::1"));
|
||||
```
|
||||
|
||||
We attach data to each variant of the enum directly, so there is no need for an
|
||||
extra struct.
|
||||
|
||||
There’s another advantage to using an enum rather than a struct: each variant
|
||||
can have different types and amounts of associated data. Version four type IP
|
||||
addresses will always have four numeric components that will have values
|
||||
between 0 and 255. If we wanted to store `V4` addresses as four `u8` values but
|
||||
still express `V6` addresses as one `String` value, we wouldn’t be able to with
|
||||
a struct. Enums handle this case with ease:
|
||||
|
||||
```rust
|
||||
enum IpAddr {
|
||||
V4(u8, u8, u8, u8),
|
||||
V6(String),
|
||||
}
|
||||
|
||||
let home = IpAddr::V4(127, 0, 0, 1);
|
||||
|
||||
let loopback = IpAddr::V6(String::from("::1"));
|
||||
```
|
||||
|
||||
We’ve shown several different ways to define data structures to store version
|
||||
four and version six IP addresses. However, as it turns out, wanting to store
|
||||
IP addresses and encode which kind they are is so common that [the standard
|
||||
library has a definition we can use!][IpAddr]<!-- ignore --> Let’s look at how
|
||||
the standard library defines `IpAddr`: it has the exact enum and variants that
|
||||
we’ve defined and used, but it embeds the address data inside the variants in
|
||||
the form of two different structs, which are defined differently for each
|
||||
variant:
|
||||
|
||||
[IpAddr]: ../../std/net/enum.IpAddr.html
|
||||
|
||||
```rust
|
||||
struct Ipv4Addr {
|
||||
// --snip--
|
||||
}
|
||||
|
||||
struct Ipv6Addr {
|
||||
// --snip--
|
||||
}
|
||||
|
||||
enum IpAddr {
|
||||
V4(Ipv4Addr),
|
||||
V6(Ipv6Addr),
|
||||
}
|
||||
```
|
||||
|
||||
This code illustrates that you can put any kind of data inside an enum variant:
|
||||
strings, numeric types, or structs, for example. You can even include another
|
||||
enum! Also, standard library types are often not much more complicated than
|
||||
what you might come up with.
|
||||
|
||||
Note that even though the standard library contains a definition for `IpAddr`,
|
||||
we can still create and use our own definition without conflict because we
|
||||
haven’t brought the standard library’s definition into our scope. We’ll talk
|
||||
more about bringing types into scope in Chapter 7.
|
||||
|
||||
Let’s look at another example of an enum in Listing 6-2: this one has a wide
|
||||
variety of types embedded in its variants:
|
||||
|
||||
```rust
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(i32, i32, i32),
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-2: A `Message` enum whose variants each store
|
||||
different amounts and types of values</span>
|
||||
|
||||
This enum has four variants with different types:
|
||||
|
||||
* `Quit` has no data associated with it at all.
|
||||
* `Move` includes an anonymous struct inside it.
|
||||
* `Write` includes a single `String`.
|
||||
* `ChangeColor` includes three `i32` values.
|
||||
|
||||
Defining an enum with variants like the ones in Listing 6-2 is similar to
|
||||
defining different kinds of struct definitions, except the enum doesn’t use the
|
||||
`struct` keyword and all the variants are grouped together under the `Message`
|
||||
type. The following structs could hold the same data that the preceding enum
|
||||
variants hold:
|
||||
|
||||
```rust
|
||||
struct QuitMessage; // unit struct
|
||||
struct MoveMessage {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
struct WriteMessage(String); // tuple struct
|
||||
struct ChangeColorMessage(i32, i32, i32); // tuple struct
|
||||
```
|
||||
|
||||
But if we used the different structs, which each have their own type, we
|
||||
couldn’t as easily define a function to take any of these kinds of messages as
|
||||
we could with the `Message` enum defined in Listing 6-2, which is a single type.
|
||||
|
||||
There is one more similarity between enums and structs: just as we’re able to
|
||||
define methods on structs using `impl`, we’re also able to define methods on
|
||||
enums. Here’s a method named `call` that we could define on our `Message` enum:
|
||||
|
||||
```rust
|
||||
# enum Message {
|
||||
# Quit,
|
||||
# Move { x: i32, y: i32 },
|
||||
# Write(String),
|
||||
# ChangeColor(i32, i32, i32),
|
||||
# }
|
||||
#
|
||||
impl Message {
|
||||
fn call(&self) {
|
||||
// method body would be defined here
|
||||
}
|
||||
}
|
||||
|
||||
let m = Message::Write(String::from("hello"));
|
||||
m.call();
|
||||
```
|
||||
|
||||
The body of the method would use `self` to get the value that we called the
|
||||
method on. In this example, we’ve created a variable `m` that has the value
|
||||
`Message::Write(String::from("hello"))`, and that is what `self` will be in the
|
||||
body of the `call` method when `m.call()` runs.
|
||||
|
||||
Let’s look at another enum in the standard library that is very common and
|
||||
useful: `Option`.
|
||||
|
||||
### The `Option` Enum and Its Advantages Over Null Values
|
||||
|
||||
In the previous section, we looked at how the `IpAddr` enum let us use Rust’s
|
||||
type system to encode more information than just the data into our program.
|
||||
This section explores a case study of `Option`, which is another enum defined
|
||||
by the standard library. The `Option` type is used in many places because it
|
||||
encodes the very common scenario in which a value could be something or it
|
||||
could be nothing. Expressing this concept in terms of the type system means the
|
||||
compiler can check whether you’ve handled all the cases you should be handling;
|
||||
this functionality can prevent bugs that are extremely common in other
|
||||
programming languages.
|
||||
|
||||
Programming language design is often thought of in terms of which features you
|
||||
include, but the features you exclude are important too. Rust doesn’t have the
|
||||
null feature that many other languages have. *Null* is a value that means there
|
||||
is no value there. In languages with null, variables can always be in one of
|
||||
two states: null or not-null.
|
||||
|
||||
In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony
|
||||
Hoare, the inventor of null, has this to say:
|
||||
|
||||
> I call it my billion-dollar mistake. At that time, I was designing the first
|
||||
> comprehensive type system for references in an object-oriented language. My
|
||||
> goal was to ensure that all use of references should be absolutely safe, with
|
||||
> checking performed automatically by the compiler. But I couldn’t resist the
|
||||
> temptation to put in a null reference, simply because it was so easy to
|
||||
> implement. This has led to innumerable errors, vulnerabilities, and system
|
||||
> crashes, which have probably caused a billion dollars of pain and damage in
|
||||
> the last forty years.
|
||||
|
||||
The problem with null values is that if you try to use a null value as a
|
||||
not-null value, you’ll get an error of some kind. Because this null or not-null
|
||||
property is pervasive, it’s extremely easy to make this kind of error.
|
||||
|
||||
However, the concept that null is trying to express is still a useful one: a
|
||||
null is a value that is currently invalid or absent for some reason.
|
||||
|
||||
The problem isn’t really with the concept but with the particular
|
||||
implementation. As such, Rust does not have nulls, but it does have an enum
|
||||
that can encode the concept of a value being present or absent. This enum is
|
||||
`Option<T>`, and it is [defined by the standard library][option]<!-- ignore -->
|
||||
as follows:
|
||||
|
||||
[option]: ../../std/option/enum.Option.html
|
||||
|
||||
```rust
|
||||
enum Option<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
```
|
||||
|
||||
The `Option<T>` enum is so useful that it’s even included in the prelude; you
|
||||
don’t need to bring it into scope explicitly. In addition, so are its variants:
|
||||
you can use `Some` and `None` directly without the `Option::` prefix. The
|
||||
`Option<T>` enum is still just a regular enum, and `Some(T)` and `None` are
|
||||
still variants of type `Option<T>`.
|
||||
|
||||
The `<T>` syntax is a feature of Rust we haven’t talked about yet. It’s a
|
||||
generic type parameter, and we’ll cover generics in more detail in Chapter 10.
|
||||
For now, all you need to know is that `<T>` means the `Some` variant of the
|
||||
`Option` enum can hold one piece of data of any type. Here are some examples of
|
||||
using `Option` values to hold number types and string types:
|
||||
|
||||
```rust
|
||||
let some_number = Some(5);
|
||||
let some_string = Some("a string");
|
||||
|
||||
let absent_number: Option<i32> = None;
|
||||
```
|
||||
|
||||
If we use `None` rather than `Some`, we need to tell Rust what type of
|
||||
`Option<T>` we have, because the compiler can’t infer the type that the `Some`
|
||||
variant will hold by looking only at a `None` value.
|
||||
|
||||
When we have a `Some` value, we know that a value is present and the value is
|
||||
held within the `Some`. When we have a `None` value, in some sense, it means
|
||||
the same thing as null: we don’t have a valid value. So why is having
|
||||
`Option<T>` any better than having null?
|
||||
|
||||
In short, because `Option<T>` and `T` (where `T` can be any type) are different
|
||||
types, the compiler won’t let us use an `Option<T>` value as if it were
|
||||
definitely a valid value. For example, this code won’t compile because it’s
|
||||
trying to add an `i8` to an `Option<i8>`:
|
||||
|
||||
```rust,ignore
|
||||
let x: i8 = 5;
|
||||
let y: Option<i8> = Some(5);
|
||||
|
||||
let sum = x + y;
|
||||
```
|
||||
|
||||
If we run this code, we get an error message like this:
|
||||
|
||||
```text
|
||||
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is
|
||||
not satisfied
|
||||
-->
|
||||
|
|
||||
5 | let sum = x + y;
|
||||
| ^ no implementation for `i8 + std::option::Option<i8>`
|
||||
|
|
||||
```
|
||||
|
||||
Intense! In effect, this error message means that Rust doesn’t understand how
|
||||
to add an `i8` and an `Option<i8>`, because they’re different types. When we
|
||||
have a value of a type like `i8` in Rust, the compiler will ensure that we
|
||||
always have a valid value. We can proceed confidently without having to check
|
||||
for null before using that value. Only when we have an `Option<i8>` (or
|
||||
whatever type of value we’re working with) do we have to worry about possibly
|
||||
not having a value, and the compiler will make sure we handle that case before
|
||||
using the value.
|
||||
|
||||
In other words, you have to convert an `Option<T>` to a `T` before you can
|
||||
perform `T` operations with it. Generally, this helps catch one of the most
|
||||
common issues with null: assuming that something isn’t null when it actually
|
||||
is.
|
||||
|
||||
Not having to worry about incorrectly assuming a not-null value helps you to be
|
||||
more confident in your code. In order to have a value that can possibly be
|
||||
null, you must explicitly opt in by making the type of that value `Option<T>`.
|
||||
Then, when you use that value, you are required to explicitly handle the case
|
||||
when the value is null. Everywhere that a value has a type that isn’t an
|
||||
`Option<T>`, you *can* safely assume that the value isn’t null. This was a
|
||||
deliberate design decision for Rust to limit null’s pervasiveness and increase
|
||||
the safety of Rust code.
|
||||
|
||||
So, how do you get the `T` value out of a `Some` variant when you have a value
|
||||
of type `Option<T>` so you can use that value? The `Option<T>` enum has a large
|
||||
number of methods that are useful in a variety of situations; you can check
|
||||
them out in [its documentation][docs]<!-- ignore -->. Becoming familiar with
|
||||
the methods on `Option<T>` will be extremely useful in your journey with Rust.
|
||||
|
||||
[docs]: ../../std/option/enum.Option.html
|
||||
|
||||
In general, in order to use an `Option<T>` value, you want to have code that
|
||||
will handle each variant. You want some code that will run only when you have a
|
||||
`Some(T)` value, and this code is allowed to use the inner `T`. You want some
|
||||
other code to run if you have a `None` value, and that code doesn’t have a `T`
|
||||
value available. The `match` expression is a control flow construct that does
|
||||
just this when used with enums: it will run different code depending on which
|
||||
variant of the enum it has, and that code can use the data inside the matching
|
||||
value.
|
||||
295
src/doc/book/2018-edition/src/ch06-02-match.md
Normal file
295
src/doc/book/2018-edition/src/ch06-02-match.md
Normal file
@ -0,0 +1,295 @@
|
||||
## The `match` Control Flow Operator
|
||||
|
||||
Rust has an extremely powerful control flow operator called `match` that allows
|
||||
you to compare a value against a series of patterns and then execute code based
|
||||
on which pattern matches. Patterns can be made up of literal values, variable
|
||||
names, wildcards, and many other things; Chapter 18 covers all the different
|
||||
kinds of patterns and what they do. The power of `match` comes from the
|
||||
expressiveness of the patterns and the fact that the compiler confirms that all
|
||||
possible cases are handled.
|
||||
|
||||
Think of a `match` expression as being like a coin-sorting machine: coins slide
|
||||
down a track with variously sized holes along it, and each coin falls through
|
||||
the first hole it encounters that it fits into. In the same way, values go
|
||||
through each pattern in a `match`, and at the first pattern the value “fits,”
|
||||
the value falls into the associated code block to be used during execution.
|
||||
|
||||
Because we just mentioned coins, let’s use them as an example using `match`! We
|
||||
can write a function that can take an unknown United States coin and, in a
|
||||
similar way as the counting machine, determine which coin it is and return its
|
||||
value in cents, as shown here in Listing 6-3:
|
||||
|
||||
```rust
|
||||
enum Coin {
|
||||
Penny,
|
||||
Nickel,
|
||||
Dime,
|
||||
Quarter,
|
||||
}
|
||||
|
||||
fn value_in_cents(coin: Coin) -> u32 {
|
||||
match coin {
|
||||
Coin::Penny => 1,
|
||||
Coin::Nickel => 5,
|
||||
Coin::Dime => 10,
|
||||
Coin::Quarter => 25,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-3: An enum and a `match` expression that has
|
||||
the variants of the enum as its patterns</span>
|
||||
|
||||
Let’s break down the `match` in the `value_in_cents` function. First, we list
|
||||
the `match` keyword followed by an expression, which in this case is the value
|
||||
`coin`. This seems very similar to an expression used with `if`, but there’s a
|
||||
big difference: with `if`, the expression needs to return a Boolean value, but
|
||||
here, it can be any type. The type of `coin` in this example is the `Coin` enum
|
||||
that we defined on line 1.
|
||||
|
||||
Next are the `match` arms. An arm has two parts: a pattern and some code. The
|
||||
first arm here has a pattern that is the value `Coin::Penny` and then the `=>`
|
||||
operator that separates the pattern and the code to run. The code in this case
|
||||
is just the value `1`. Each arm is separated from the next with a comma.
|
||||
|
||||
When the `match` expression executes, it compares the resulting value against
|
||||
the pattern of each arm, in order. If a pattern matches the value, the code
|
||||
associated with that pattern is executed. If that pattern doesn’t match the
|
||||
value, execution continues to the next arm, much as in a coin-sorting machine.
|
||||
We can have as many arms as we need: in Listing 6-3, our `match` has four arms.
|
||||
|
||||
The code associated with each arm is an expression, and the resulting value of
|
||||
the expression in the matching arm is the value that gets returned for the
|
||||
entire `match` expression.
|
||||
|
||||
Curly brackets typically aren’t used if the match arm code is short, as it is
|
||||
in Listing 6-3 where each arm just returns a value. If you want to run multiple
|
||||
lines of code in a match arm, you can use curly brackets. For example, the
|
||||
following code would print “Lucky penny!” every time the method was called with
|
||||
a `Coin::Penny` but would still return the last value of the block, `1`:
|
||||
|
||||
```rust
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter,
|
||||
# }
|
||||
#
|
||||
fn value_in_cents(coin: Coin) -> u32 {
|
||||
match coin {
|
||||
Coin::Penny => {
|
||||
println!("Lucky penny!");
|
||||
1
|
||||
},
|
||||
Coin::Nickel => 5,
|
||||
Coin::Dime => 10,
|
||||
Coin::Quarter => 25,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Patterns that Bind to Values
|
||||
|
||||
Another useful feature of match arms is that they can bind to the parts of the
|
||||
values that match the pattern. This is how we can extract values out of enum
|
||||
variants.
|
||||
|
||||
As an example, let’s change one of our enum variants to hold data inside it.
|
||||
From 1999 through 2008, the United States minted quarters with different
|
||||
designs for each of the 50 states on one side. No other coins got state
|
||||
designs, so only quarters have this extra value. We can add this information to
|
||||
our `enum` by changing the `Quarter` variant to include a `UsState` value stored
|
||||
inside it, which we’ve done here in Listing 6-4:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)] // So we can inspect the state in a minute
|
||||
enum UsState {
|
||||
Alabama,
|
||||
Alaska,
|
||||
// --snip--
|
||||
}
|
||||
|
||||
enum Coin {
|
||||
Penny,
|
||||
Nickel,
|
||||
Dime,
|
||||
Quarter(UsState),
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-4: A `Coin` enum in which the `Quarter` variant
|
||||
also holds a `UsState` value</span>
|
||||
|
||||
Let’s imagine that a friend of ours is trying to collect all 50 state quarters.
|
||||
While we sort our loose change by coin type, we’ll also call out the name of
|
||||
the state associated with each quarter so if it’s one our friend doesn’t have,
|
||||
they can add it to their collection.
|
||||
|
||||
In the match expression for this code, we add a variable called `state` to the
|
||||
pattern that matches values of the variant `Coin::Quarter`. When a
|
||||
`Coin::Quarter` matches, the `state` variable will bind to the value of that
|
||||
quarter’s state. Then we can use `state` in the code for that arm, like so:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# enum UsState {
|
||||
# Alabama,
|
||||
# Alaska,
|
||||
# }
|
||||
#
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter(UsState),
|
||||
# }
|
||||
#
|
||||
fn value_in_cents(coin: Coin) -> u32 {
|
||||
match coin {
|
||||
Coin::Penny => 1,
|
||||
Coin::Nickel => 5,
|
||||
Coin::Dime => 10,
|
||||
Coin::Quarter(state) => {
|
||||
println!("State quarter from {:?}!", state);
|
||||
25
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin`
|
||||
would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each
|
||||
of the match arms, none of them match until we reach `Coin::Quarter(state)`. At
|
||||
that point, the binding for `state` will be the value `UsState::Alaska`. We can
|
||||
then use that binding in the `println!` expression, thus getting the inner
|
||||
state value out of the `Coin` enum variant for `Quarter`.
|
||||
|
||||
### Matching with `Option<T>`
|
||||
|
||||
In the previous section, we wanted to get the inner `T` value out of the `Some`
|
||||
case when using `Option<T>`; we can also handle `Option<T>` using `match` as we
|
||||
did with the `Coin` enum! Instead of comparing coins, we’ll compare the
|
||||
variants of `Option<T>`, but the way that the `match` expression works remains
|
||||
the same.
|
||||
|
||||
Let’s say we want to write a function that takes an `Option<i32>` and, if
|
||||
there’s a value inside, adds 1 to that value. If there isn’t a value inside,
|
||||
the function should return the `None` value and not attempt to perform any
|
||||
operations.
|
||||
|
||||
This function is very easy to write, thanks to `match`, and will look like
|
||||
Listing 6-5:
|
||||
|
||||
```rust
|
||||
fn plus_one(x: Option<i32>) -> Option<i32> {
|
||||
match x {
|
||||
None => None,
|
||||
Some(i) => Some(i + 1),
|
||||
}
|
||||
}
|
||||
|
||||
let five = Some(5);
|
||||
let six = plus_one(five);
|
||||
let none = plus_one(None);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-5: A function that uses a `match` expression on
|
||||
an `Option<i32>`</span>
|
||||
|
||||
Let’s examine the first execution of `plus_one` in more detail. When we call
|
||||
`plus_one(five)`, the variable `x` in the body of `plus_one` will have the
|
||||
value `Some(5)`. We then compare that against each match arm.
|
||||
|
||||
```rust,ignore
|
||||
None => None,
|
||||
```
|
||||
|
||||
The `Some(5)` value doesn’t match the pattern `None`, so we continue to the
|
||||
next arm.
|
||||
|
||||
```rust,ignore
|
||||
Some(i) => Some(i + 1),
|
||||
```
|
||||
|
||||
Does `Some(5)` match `Some(i)`? Why yes it does! We have the same variant. The
|
||||
`i` binds to the value contained in `Some`, so `i` takes the value `5`. The
|
||||
code in the match arm is then executed, so we add 1 to the value of `i` and
|
||||
create a new `Some` value with our total `6` inside.
|
||||
|
||||
Now let’s consider the second call of `plus_one` in Listing 6-5, where `x` is
|
||||
`None`. We enter the `match` and compare to the first arm.
|
||||
|
||||
```rust,ignore
|
||||
None => None,
|
||||
```
|
||||
|
||||
It matches! There’s no value to add to, so the program stops and returns the
|
||||
`None` value on the right side of `=>`. Because the first arm matched, no other
|
||||
arms are compared.
|
||||
|
||||
Combining `match` and enums is useful in many situations. You’ll see this
|
||||
pattern a lot in Rust code: `match` against an enum, bind a variable to the
|
||||
data inside, and then execute code based on it. It’s a bit tricky at first, but
|
||||
once you get used to it, you’ll wish you had it in all languages. It’s
|
||||
consistently a user favorite.
|
||||
|
||||
### Matches Are Exhaustive
|
||||
|
||||
There’s one other aspect of `match` we need to discuss. Consider this version
|
||||
of our `plus_one` function that has a bug and won’t compile:
|
||||
|
||||
```rust,ignore
|
||||
fn plus_one(x: Option<i32>) -> Option<i32> {
|
||||
match x {
|
||||
Some(i) => Some(i + 1),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s
|
||||
a bug Rust knows how to catch. If we try to compile this code, we’ll get this
|
||||
error:
|
||||
|
||||
```text
|
||||
error[E0004]: non-exhaustive patterns: `None` not covered
|
||||
-->
|
||||
|
|
||||
6 | match x {
|
||||
| ^ pattern `None` not covered
|
||||
```
|
||||
|
||||
Rust knows that we didn’t cover every possible case and even knows which
|
||||
pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last
|
||||
possibility in order for the code to be valid. Especially in the case of
|
||||
`Option<T>`, when Rust prevents us from forgetting to explicitly handle the
|
||||
`None` case, it protects us from assuming that we have a value when we might
|
||||
have null, thus making the billion-dollar mistake discussed earlier.
|
||||
|
||||
### The `_` Placeholder
|
||||
|
||||
Rust also has a pattern we can use when we don’t want to list all possible
|
||||
values. For example, a `u8` can have valid values of 0 through 255. If we only
|
||||
care about the values 1, 3, 5, and 7, we don’t want to have to list out 0, 2,
|
||||
4, 6, 8, 9 all the way up to 255. Fortunately, we don’t have to: we can use the
|
||||
special pattern `_` instead:
|
||||
|
||||
```rust
|
||||
let some_u8_value = 0u8;
|
||||
match some_u8_value {
|
||||
1 => println!("one"),
|
||||
3 => println!("three"),
|
||||
5 => println!("five"),
|
||||
7 => println!("seven"),
|
||||
_ => (),
|
||||
}
|
||||
```
|
||||
|
||||
The `_` pattern will match any value. By putting it after our other arms, the
|
||||
`_` will match all the possible cases that aren’t specified before it. The `()`
|
||||
is just the unit value, so nothing will happen in the `_` case. As a result, we
|
||||
can say that we want to do nothing for all the possible values that we don’t
|
||||
list before the `_` placeholder.
|
||||
|
||||
However, the `match` expression can be a bit wordy in a situation in which we
|
||||
only care about *one* of the cases. For this situation, Rust provides `if let`.
|
||||
118
src/doc/book/2018-edition/src/ch06-03-if-let.md
Normal file
118
src/doc/book/2018-edition/src/ch06-03-if-let.md
Normal file
@ -0,0 +1,118 @@
|
||||
## Concise Control Flow with `if let`
|
||||
|
||||
The `if let` syntax lets you combine `if` and `let` into a less verbose way to
|
||||
handle values that match one pattern while ignoring the rest. Consider the
|
||||
program in Listing 6-6 that matches on an `Option<u8>` value but only wants to
|
||||
execute code if the value is 3:
|
||||
|
||||
```rust
|
||||
let some_u8_value = Some(0u8);
|
||||
match some_u8_value {
|
||||
Some(3) => println!("three"),
|
||||
_ => (),
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 6-6: A `match` that only cares about executing
|
||||
code when the value is `Some(3)`</span>
|
||||
|
||||
We want to do something with the `Some(3)` match but do nothing with any other
|
||||
`Some<u8>` value or the `None` value. To satisfy the `match` expression, we
|
||||
have to add `_ => ()` after processing just one variant, which is a lot of
|
||||
boilerplate code to add.
|
||||
|
||||
Instead, we could write this in a shorter way using `if let`. The following
|
||||
code behaves the same as the `match` in Listing 6-6:
|
||||
|
||||
```rust
|
||||
# let some_u8_value = Some(0u8);
|
||||
if let Some(3) = some_u8_value {
|
||||
println!("three");
|
||||
}
|
||||
```
|
||||
|
||||
The syntax `if let` takes a pattern and an expression separated by an `=`. It
|
||||
works the same way as a `match`, where the expression is given to the `match`
|
||||
and the pattern is its first arm.
|
||||
|
||||
Using `if let` means you have less typing, less indentation, and less
|
||||
boilerplate code. However, you lose the exhaustive checking that `match`
|
||||
enforces. Choosing between `match` and `if let` depends on what you’re doing in
|
||||
your particular situation and whether gaining conciseness is an appropriate
|
||||
trade-off for losing exhaustive checking.
|
||||
|
||||
In other words, you can think of `if let` as syntax sugar for a `match` that
|
||||
runs code when the value matches one pattern and then ignores all other values.
|
||||
|
||||
We can include an `else` with an `if let`. The block of code that goes with the
|
||||
`else` is the same as the block of code that would go with the `_` case in the
|
||||
`match` expression that is equivalent to the `if let` and `else`. Recall the
|
||||
`Coin` enum definition in Listing 6-4, where the `Quarter` variant also held a
|
||||
`UsState` value. If we wanted to count all non-quarter coins we see while also
|
||||
announcing the state of the quarters, we could do that with a `match`
|
||||
expression like this:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# enum UsState {
|
||||
# Alabama,
|
||||
# Alaska,
|
||||
# }
|
||||
#
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter(UsState),
|
||||
# }
|
||||
# let coin = Coin::Penny;
|
||||
let mut count = 0;
|
||||
match coin {
|
||||
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
|
||||
_ => count += 1,
|
||||
}
|
||||
```
|
||||
|
||||
Or we could use an `if let` and `else` expression like this:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# enum UsState {
|
||||
# Alabama,
|
||||
# Alaska,
|
||||
# }
|
||||
#
|
||||
# enum Coin {
|
||||
# Penny,
|
||||
# Nickel,
|
||||
# Dime,
|
||||
# Quarter(UsState),
|
||||
# }
|
||||
# let coin = Coin::Penny;
|
||||
let mut count = 0;
|
||||
if let Coin::Quarter(state) = coin {
|
||||
println!("State quarter from {:?}!", state);
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
```
|
||||
|
||||
If you have a situation in which your program has logic that is too verbose to
|
||||
express using a `match`, remember that `if let` is in your Rust toolbox as well.
|
||||
|
||||
## Summary
|
||||
|
||||
We’ve now covered how to use enums to create custom types that can be one of a
|
||||
set of enumerated values. We’ve shown how the standard library’s `Option<T>`
|
||||
type helps you use the type system to prevent errors. When enum values have
|
||||
data inside them, you can use `match` or `if let` to extract and use those
|
||||
values, depending on how many cases you need to handle.
|
||||
|
||||
Your Rust programs can now express concepts in your domain using structs and
|
||||
enums. Creating custom types to use in your API ensures type safety: the
|
||||
compiler will make certain your functions get only values of the type each
|
||||
function expects.
|
||||
|
||||
In order to provide a well-organized API to your users that is straightforward
|
||||
to use and only exposes exactly what your users will need, let’s now turn to
|
||||
Rust’s modules.
|
||||
24
src/doc/book/2018-edition/src/ch07-00-modules.md
Normal file
24
src/doc/book/2018-edition/src/ch07-00-modules.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Using Modules to Reuse and Organize Code
|
||||
|
||||
When you start writing programs in Rust, your code might live solely in the
|
||||
`main` function. As your code grows, you’ll eventually move functionality into
|
||||
other functions for reuse and better organization. By splitting your code into
|
||||
smaller chunks, you make each chunk easier to understand on its own. But what
|
||||
happens if you have too many functions? Rust has a module system that enables
|
||||
the reuse of code in an organized fashion.
|
||||
|
||||
In the same way that you extract lines of code into a function, you can extract
|
||||
functions (and other code, like structs and enums) into different modules. A
|
||||
*module* is a namespace that contains definitions of functions or types, and
|
||||
you can choose whether those definitions are visible outside their module
|
||||
(public) or not (private). Here’s an overview of how modules work:
|
||||
|
||||
* The `mod` keyword declares a new module. Code within the module appears
|
||||
either immediately following this declaration within curly brackets or in
|
||||
another file.
|
||||
* By default, functions, types, constants, and modules are private. The `pub`
|
||||
keyword makes an item public and therefore visible outside its namespace.
|
||||
* The `use` keyword brings modules, or the definitions inside modules, into
|
||||
scope so it’s easier to refer to them.
|
||||
|
||||
We’ll look at each of these parts to see how they fit into the whole.
|
||||
481
src/doc/book/2018-edition/src/ch07-01-mod-and-the-filesystem.md
Normal file
481
src/doc/book/2018-edition/src/ch07-01-mod-and-the-filesystem.md
Normal file
@ -0,0 +1,481 @@
|
||||
## `mod` and the Filesystem
|
||||
|
||||
We’ll start our module example by making a new project with Cargo, but instead
|
||||
of creating a binary crate, we’ll make a library crate: a project that other
|
||||
people can pull into their projects as a dependency. For example, the `rand`
|
||||
crate discussed in Chapter 2 is a library crate that we used as a dependency in
|
||||
the guessing game project.
|
||||
|
||||
We’ll create a skeleton of a library that provides some general networking
|
||||
functionality; we’ll concentrate on the organization of the modules and
|
||||
functions, but we won’t worry about what code goes in the function bodies.
|
||||
We’ll call our library `communicator`. To create a library, pass the `--lib`
|
||||
option instead of `--bin`:
|
||||
|
||||
```text
|
||||
$ cargo new communicator --lib
|
||||
$ cd communicator
|
||||
```
|
||||
|
||||
Notice that Cargo generated *src/lib.rs* instead of *src/main.rs*. Inside
|
||||
*src/lib.rs* we’ll find the following:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cargo creates an example test to help us get our library started, rather than
|
||||
the “Hello, world!” binary that we get when we use the `--bin` option. We’ll
|
||||
look at the `#[]` and `mod tests` syntax in the “Using `super` to Access a
|
||||
Parent Module” section later in this chapter, but for now, leave this code at
|
||||
the bottom of *src/lib.rs*.
|
||||
|
||||
Because we don’t have a *src/main.rs* file, there’s nothing for Cargo to
|
||||
execute with the `cargo run` command. Therefore, we’ll use the `cargo build`
|
||||
command to compile our library crate’s code.
|
||||
|
||||
We’ll look at different options for organizing your library’s code that will be
|
||||
suitable in a variety of situations, depending on the intent of the code.
|
||||
|
||||
### Module Definitions
|
||||
|
||||
For our `communicator` networking library, we’ll first define a module named
|
||||
`network` that contains the definition of a function called `connect`. Every
|
||||
module definition in Rust starts with the `mod` keyword. Add this code to the
|
||||
beginning of the *src/lib.rs* file, above the test code:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
mod network {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After the `mod` keyword, we put the name of the module, `network`, and then a
|
||||
block of code in curly brackets. Everything inside this block is inside the
|
||||
namespace `network`. In this case, we have a single function, `connect`. If we
|
||||
wanted to call this function from code outside the `network` module, we
|
||||
would need to specify the module and use the namespace syntax `::` like so:
|
||||
`network::connect()`.
|
||||
|
||||
We can also have multiple modules, side by side, in the same *src/lib.rs* file.
|
||||
For example, to also have a `client` module that has a function named
|
||||
`connect`, we can add it as shown in Listing 7-1:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
mod network {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
|
||||
mod client {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-1: The `network` module and the `client` module
|
||||
defined side by side in *src/lib.rs*</span>
|
||||
|
||||
Now we have a `network::connect` function and a `client::connect` function.
|
||||
These can have completely different functionality, and the function names do
|
||||
not conflict with each other because they’re in different modules.
|
||||
|
||||
In this case, because we’re building a library, the file that serves as the
|
||||
entry point for building our library is *src/lib.rs*. However, in respect to
|
||||
creating modules, there’s nothing special about *src/lib.rs*. We could also
|
||||
create modules in *src/main.rs* for a binary crate in the same way as we’re
|
||||
creating modules in *src/lib.rs* for the library crate. In fact, we can put
|
||||
modules inside of modules, which can be useful as your modules grow to keep
|
||||
related functionality organized together and separate functionality apart. The
|
||||
way you choose to organize your code depends on how you think about the
|
||||
relationship between the parts of your code. For instance, the `client` code
|
||||
and its `connect` function might make more sense to users of our library if
|
||||
they were inside the `network` namespace instead, as in Listing 7-2:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
mod network {
|
||||
fn connect() {
|
||||
}
|
||||
|
||||
mod client {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-2: Moving the `client` module inside the
|
||||
`network` module</span>
|
||||
|
||||
In your *src/lib.rs* file, replace the existing `mod network` and `mod client`
|
||||
definitions with the ones in Listing 7-2, which have the `client` module as an
|
||||
inner module of `network`. The functions `network::connect` and
|
||||
`network::client::connect` are both named `connect`, but they don’t conflict
|
||||
with each other because they’re in different namespaces.
|
||||
|
||||
In this way, modules form a hierarchy. The contents of *src/lib.rs* are at the
|
||||
topmost level, and the submodules are at lower levels. Here’s what the
|
||||
organization of our example in Listing 7-1 looks like when thought of as a
|
||||
hierarchy:
|
||||
|
||||
```text
|
||||
communicator
|
||||
├── network
|
||||
└── client
|
||||
```
|
||||
|
||||
And here’s the hierarchy corresponding to the example in Listing 7-2:
|
||||
|
||||
```text
|
||||
communicator
|
||||
└── network
|
||||
└── client
|
||||
```
|
||||
|
||||
The hierarchy shows that in Listing 7-2, `client` is a child of the `network`
|
||||
module rather than a sibling. More complicated projects can have many modules,
|
||||
and they’ll need to be organized logically in order for you to keep track of
|
||||
them. What “logically” means in your project is up to you and depends on how
|
||||
you and your library’s users think about your project’s domain. Use the
|
||||
techniques shown here to create side-by-side modules and nested modules in
|
||||
whatever structure you would like.
|
||||
|
||||
### Moving Modules to Other Files
|
||||
|
||||
Modules form a hierarchical structure, much like another structure in computing
|
||||
that you’re used to: filesystems! We can use Rust’s module system along with
|
||||
multiple files to split up Rust projects so not everything lives in
|
||||
*src/lib.rs* or *src/main.rs*. For this example, let’s start with the code in
|
||||
Listing 7-3:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
mod client {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
|
||||
mod network {
|
||||
fn connect() {
|
||||
}
|
||||
|
||||
mod server {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-3: Three modules, `client`, `network`, and
|
||||
`network::server`, all defined in *src/lib.rs*</span>
|
||||
|
||||
The file *src/lib.rs* has this module hierarchy:
|
||||
|
||||
```text
|
||||
communicator
|
||||
├── client
|
||||
└── network
|
||||
└── server
|
||||
```
|
||||
|
||||
If these modules had many functions, and those functions were becoming lengthy,
|
||||
it would be difficult to scroll through this file to find the code we wanted to
|
||||
work with. Because the functions are nested inside one or more `mod` blocks,
|
||||
the lines of code inside the functions will start getting lengthy as well.
|
||||
These would be good reasons to separate the `client`, `network`, and `server`
|
||||
modules from *src/lib.rs* and place them into their own files.
|
||||
|
||||
First, let’s replace the `client` module code with only the declaration of the
|
||||
`client` module so that *src/lib.rs* looks like code shown in Listing 7-4:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
mod client;
|
||||
|
||||
mod network {
|
||||
fn connect() {
|
||||
}
|
||||
|
||||
mod server {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-4: Extracting the contents of the `client` module but leaving the declaration in *src/lib.rs*</span>
|
||||
|
||||
We’re still *declaring* the `client` module here, but by replacing the block
|
||||
with a semicolon, we’re telling Rust to look in another location for the code
|
||||
defined within the scope of the `client` module. In other words, the line `mod
|
||||
client;` means this:
|
||||
|
||||
```rust,ignore
|
||||
mod client {
|
||||
// contents of client.rs
|
||||
}
|
||||
```
|
||||
|
||||
Now we need to create the external file with that module name. Create a
|
||||
*client.rs* file in your *src/* directory and open it. Then enter the
|
||||
following, which is the `connect` function in the `client` module that we
|
||||
removed in the previous step:
|
||||
|
||||
<span class="filename">Filename: src/client.rs</span>
|
||||
|
||||
```rust
|
||||
fn connect() {
|
||||
}
|
||||
```
|
||||
|
||||
Note that we don’t need a `mod` declaration in this file because we already
|
||||
declared the `client` module with `mod` in *src/lib.rs*. This file just
|
||||
provides the *contents* of the `client` module. If we put a `mod client` here,
|
||||
we’d be giving the `client` module its own submodule named `client`!
|
||||
|
||||
Rust only knows to look in *src/lib.rs* by default. If we want to add more
|
||||
files to our project, we need to tell Rust in *src/lib.rs* to look in other
|
||||
files; this is why `mod client` needs to be defined in *src/lib.rs* and can’t
|
||||
be defined in *src/client.rs*.
|
||||
|
||||
Now the project should compile successfully, although you’ll get a few
|
||||
warnings. Remember to use `cargo build` instead of `cargo run` because we have
|
||||
a library crate rather than a binary crate:
|
||||
|
||||
```text
|
||||
$ cargo build
|
||||
Compiling communicator v0.1.0 (file:///projects/communicator)
|
||||
warning: function is never used: `connect`
|
||||
--> src/client.rs:1:1
|
||||
|
|
||||
1 | / fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: #[warn(dead_code)] on by default
|
||||
|
||||
warning: function is never used: `connect`
|
||||
--> src/lib.rs:4:5
|
||||
|
|
||||
4 | / fn connect() {
|
||||
5 | | }
|
||||
| |_____^
|
||||
|
||||
warning: function is never used: `connect`
|
||||
--> src/lib.rs:8:9
|
||||
|
|
||||
8 | / fn connect() {
|
||||
9 | | }
|
||||
| |_________^
|
||||
```
|
||||
|
||||
These warnings tell us that we have functions that are never used. Don’t worry
|
||||
about these warnings for now; we’ll address them later in this chapter in the
|
||||
“Controlling Visibility with `pub`” section. The good news is that they’re just
|
||||
warnings; our project built successfully!
|
||||
|
||||
Next, let’s extract the `network` module into its own file using the same
|
||||
pattern. In *src/lib.rs*, delete the body of the `network` module and add a
|
||||
semicolon to the declaration, like so:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
mod client;
|
||||
|
||||
mod network;
|
||||
```
|
||||
|
||||
Then create a new *src/network.rs* file and enter the following:
|
||||
|
||||
<span class="filename">Filename: src/network.rs</span>
|
||||
|
||||
```rust
|
||||
fn connect() {
|
||||
}
|
||||
|
||||
mod server {
|
||||
fn connect() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that we still have a `mod` declaration within this module file; this is
|
||||
because we still want `server` to be a submodule of `network`.
|
||||
|
||||
Run `cargo build` again. Success! We have one more module to extract: `server`.
|
||||
Because it’s a submodule—that is, a module within a module—our current tactic
|
||||
of extracting a module into a file named after that module won’t work. We’ll
|
||||
try anyway so you can see the error. First, change *src/network.rs* to have
|
||||
`mod server;` instead of the `server` module’s contents:
|
||||
|
||||
<span class="filename">Filename: src/network.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn connect() {
|
||||
}
|
||||
|
||||
mod server;
|
||||
```
|
||||
|
||||
Then create a *src/server.rs* file and enter the contents of the `server`
|
||||
module that we extracted:
|
||||
|
||||
<span class="filename">Filename: src/server.rs</span>
|
||||
|
||||
```rust
|
||||
fn connect() {
|
||||
}
|
||||
```
|
||||
|
||||
When we try to `cargo build`, we’ll get the error shown in Listing 7-5:
|
||||
|
||||
```text
|
||||
$ cargo build
|
||||
Compiling communicator v0.1.0 (file:///projects/communicator)
|
||||
error: cannot declare a new module at this location
|
||||
--> src/network.rs:4:5
|
||||
|
|
||||
4 | mod server;
|
||||
| ^^^^^^
|
||||
|
|
||||
note: maybe move this module `src/network.rs` to its own directory via `src/network/mod.rs`
|
||||
--> src/network.rs:4:5
|
||||
|
|
||||
4 | mod server;
|
||||
| ^^^^^^
|
||||
note: ... or maybe `use` the module `server` instead of possibly redeclaring it
|
||||
--> src/network.rs:4:5
|
||||
|
|
||||
4 | mod server;
|
||||
| ^^^^^^
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-5: Error when trying to extract the `server`
|
||||
submodule into *src/server.rs*</span>
|
||||
|
||||
The error says we `cannot declare a new module at this location` and is
|
||||
pointing to the `mod server;` line in *src/network.rs*. So *src/network.rs* is
|
||||
different than *src/lib.rs* somehow: keep reading to understand why.
|
||||
|
||||
The note in the middle of Listing 7-5 is actually very helpful because it
|
||||
points out something we haven’t yet talked about doing:
|
||||
|
||||
```text
|
||||
note: maybe move this module `network` to its own directory via
|
||||
`network/mod.rs`
|
||||
```
|
||||
|
||||
Instead of continuing to follow the same file-naming pattern we used
|
||||
previously, we can do what the note suggests:
|
||||
|
||||
1. Make a new *directory* named *network*, the parent module’s name.
|
||||
2. Move the *src/network.rs* file into the new *network* directory and
|
||||
rename it *src/network/mod.rs*.
|
||||
3. Move the submodule file *src/server.rs* into the *network* directory.
|
||||
|
||||
Here are commands to carry out these steps:
|
||||
|
||||
```text
|
||||
$ mkdir src/network
|
||||
$ mv src/network.rs src/network/mod.rs
|
||||
$ mv src/server.rs src/network
|
||||
```
|
||||
|
||||
Now when we try to run `cargo build`, compilation will work (we’ll still have
|
||||
warnings though). Our module layout still looks exactly the same as it did when
|
||||
we had all the code in *src/lib.rs* in Listing 7-3:
|
||||
|
||||
```text
|
||||
communicator
|
||||
├── client
|
||||
└── network
|
||||
└── server
|
||||
```
|
||||
|
||||
The corresponding file layout now looks like this:
|
||||
|
||||
```text
|
||||
└── src
|
||||
├── client.rs
|
||||
├── lib.rs
|
||||
└── network
|
||||
├── mod.rs
|
||||
└── server.rs
|
||||
```
|
||||
|
||||
So when we wanted to extract the `network::server` module, why did we have to
|
||||
also change the *src/network.rs* file to the *src/network/mod.rs* file and put
|
||||
the code for `network::server` in the *network* directory in
|
||||
*src/network/server.rs*? Why couldn’t we just extract the `network::server`
|
||||
module into *src/server.rs*? The reason is that Rust wouldn’t be able to
|
||||
recognize that `server` was supposed to be a submodule of `network` if the
|
||||
*server.rs* file was in the *src* directory. To clarify Rust’s behavior here,
|
||||
let’s consider a different example with the following module hierarchy, where
|
||||
all the definitions are in *src/lib.rs*:
|
||||
|
||||
```text
|
||||
communicator
|
||||
├── client
|
||||
└── network
|
||||
└── client
|
||||
```
|
||||
|
||||
In this example, we have three modules again: `client`, `network`, and
|
||||
`network::client`. Following the same steps we did earlier for extracting
|
||||
modules into files, we would create *src/client.rs* for the `client` module.
|
||||
For the `network` module, we would create *src/network.rs*. But we wouldn’t be
|
||||
able to extract the `network::client` module into a *src/client.rs* file
|
||||
because that already exists for the top-level `client` module! If we could put
|
||||
the code for *both* the `client` and `network::client` modules in the
|
||||
*src/client.rs* file, Rust wouldn’t have any way to know whether the code was
|
||||
for `client` or for `network::client`.
|
||||
|
||||
Therefore, in order to extract a file for the `network::client` submodule of
|
||||
the `network` module, we needed to create a directory for the `network` module
|
||||
instead of a *src/network.rs* file. The code that is in the `network` module
|
||||
then goes into the *src/network/mod.rs* file, and the submodule
|
||||
`network::client` can have its own *src/network/client.rs* file. Now the
|
||||
top-level *src/client.rs* is unambiguously the code that belongs to the
|
||||
`client` module.
|
||||
|
||||
### Rules of Module Filesystems
|
||||
|
||||
Let’s summarize the rules of modules with regard to files:
|
||||
|
||||
* If a module named `foo` has no submodules, you should put the declarations
|
||||
for `foo` in a file named *foo.rs*.
|
||||
* If a module named `foo` does have submodules, you should put the declarations
|
||||
for `foo` in a file named *foo/mod.rs*.
|
||||
|
||||
These rules apply recursively, so if a module named `foo` has a submodule named
|
||||
`bar` and `bar` does not have submodules, you should have the following files
|
||||
in your *src* directory:
|
||||
|
||||
```text
|
||||
└── foo
|
||||
├── bar.rs (contains the declarations in `foo::bar`)
|
||||
└── mod.rs (contains the declarations in `foo`, including `mod bar`)
|
||||
```
|
||||
|
||||
The modules should be declared in their parent module’s file using the `mod`
|
||||
keyword.
|
||||
|
||||
Next, we’ll talk about the `pub` keyword and get rid of those warnings!
|
||||
@ -0,0 +1,288 @@
|
||||
## Controlling Visibility with `pub`
|
||||
|
||||
We resolved the error messages shown in Listing 7-5 by moving the `network` and
|
||||
`network::server` code into the *src/network/mod.rs* and
|
||||
*src/network/server.rs* files, respectively. At that point, `cargo build` was
|
||||
able to build our project, but we still get warning messages about the
|
||||
`client::connect`, `network::connect`, and `network::server::connect` functions
|
||||
not being used.
|
||||
|
||||
So why are we receiving these warnings? After all, we’re building a library
|
||||
with functions that are intended to be used by our *users*, not necessarily by
|
||||
us within our own project, so it shouldn’t matter that these `connect`
|
||||
functions go unused. The point of creating them is that they will be used by
|
||||
another project, not our own.
|
||||
|
||||
To understand why this program invokes these warnings, let’s try using the
|
||||
`communicator` library from another project, calling it externally. To do that,
|
||||
we’ll create a binary crate in the same directory as our library crate by
|
||||
making a *src/main.rs* file containing this code:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
extern crate communicator;
|
||||
|
||||
fn main() {
|
||||
communicator::client::connect();
|
||||
}
|
||||
```
|
||||
|
||||
We use the `extern crate` command to bring the `communicator` library crate
|
||||
into scope. Our package now contains *two* crates. Cargo treats *src/main.rs*
|
||||
as the root file of a binary crate, which is separate from the existing library
|
||||
crate whose root file is *src/lib.rs*. This pattern is quite common for
|
||||
executable projects: most functionality is in a library crate, and the binary
|
||||
crate uses that library crate. As a result, other programs can also use the
|
||||
library crate, and it’s a nice separation of concerns.
|
||||
|
||||
From the point of view of a crate outside the `communicator` library looking
|
||||
in, all the modules we’ve been creating are within a module that has the same
|
||||
name as the crate, `communicator`. We call the top-level module of a crate the
|
||||
*root module*.
|
||||
|
||||
Also note that even if we’re using an external crate within a submodule of our
|
||||
project, the `extern crate` should go in our root module (so in *src/main.rs*
|
||||
or *src/lib.rs*). Then, in our submodules, we can refer to items from external
|
||||
crates as if the items are top-level modules.
|
||||
|
||||
Right now, our binary crate just calls our library’s `connect` function from
|
||||
the `client` module. However, invoking `cargo build` will now give us an error
|
||||
after the warnings:
|
||||
|
||||
```text
|
||||
error[E0603]: module `client` is private
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | communicator::client::connect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
```
|
||||
|
||||
Ah ha! This error tells us that the `client` module is private, which is the
|
||||
crux of the warnings. It’s also the first time we’ve run into the concepts of
|
||||
*public* and *private* in the context of Rust. The default state of all code in
|
||||
Rust is private: no one else is allowed to use the code. If you don’t use a
|
||||
private function within your program, because your program is the only code
|
||||
allowed to use that function, Rust will warn you that the function has gone
|
||||
unused.
|
||||
|
||||
After you specify that a function such as `client::connect` is public, not only
|
||||
will your call to that function from your binary crate be allowed, but also the
|
||||
warning that the function is unused will go away. Marking a function as public
|
||||
lets Rust know that the function will be used by code outside of your program.
|
||||
Rust considers the theoretical external usage that’s now possible as the
|
||||
function “being used.” Thus, when a function is marked public, Rust will not
|
||||
require that it be used in your program and will stop warning that the function
|
||||
is unused.
|
||||
|
||||
### Making a Function Public
|
||||
|
||||
To tell Rust to make a function public, we add the `pub` keyword to the start
|
||||
of the declaration. We’ll focus on fixing the warning that indicates
|
||||
`client::connect` has gone unused for now, as well as the `` module `client` is
|
||||
private `` error from our binary crate. Modify *src/lib.rs* to make the
|
||||
`client` module public, like so:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
pub mod client;
|
||||
|
||||
mod network;
|
||||
```
|
||||
|
||||
The `pub` keyword is placed right before `mod`. Let’s try building again:
|
||||
|
||||
```text
|
||||
error[E0603]: function `connect` is private
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | communicator::client::connect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
```
|
||||
|
||||
Hooray! We have a different error! Yes, different error messages are a cause
|
||||
for celebration. The new error shows `` function `connect` is private ``, so
|
||||
let’s edit *src/client.rs* to make `client::connect` public too:
|
||||
|
||||
<span class="filename">Filename: src/client.rs</span>
|
||||
|
||||
```rust
|
||||
pub fn connect() {
|
||||
}
|
||||
```
|
||||
|
||||
Now run `cargo build` again:
|
||||
|
||||
```text
|
||||
warning: function is never used: `connect`
|
||||
--> src/network/mod.rs:1:1
|
||||
|
|
||||
1 | / fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: #[warn(dead_code)] on by default
|
||||
|
||||
warning: function is never used: `connect`
|
||||
--> src/network/server.rs:1:1
|
||||
|
|
||||
1 | / fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
```
|
||||
|
||||
The code compiled, and the warning that `client::connect` is not being used is
|
||||
gone!
|
||||
|
||||
Unused code warnings don’t always indicate that an item in your code needs to
|
||||
be made public: if you *didn’t* want these functions to be part of your public
|
||||
API, unused code warnings could be alerting you to code you no longer need that
|
||||
you can safely delete. They could also be alerting you to a bug if you had just
|
||||
accidentally removed all places within your library where this function is
|
||||
called.
|
||||
|
||||
But in this case, we *do* want the other two functions to be part of our
|
||||
crate’s public API, so let’s mark them as `pub` as well to get rid of the
|
||||
remaining warnings. Modify *src/network/mod.rs* to look like the following:
|
||||
|
||||
<span class="filename">Filename: src/network/mod.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
pub fn connect() {
|
||||
}
|
||||
|
||||
mod server;
|
||||
```
|
||||
|
||||
Then compile the code:
|
||||
|
||||
```text
|
||||
warning: function is never used: `connect`
|
||||
--> src/network/mod.rs:1:1
|
||||
|
|
||||
1 | / pub fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: #[warn(dead_code)] on by default
|
||||
|
||||
warning: function is never used: `connect`
|
||||
--> src/network/server.rs:1:1
|
||||
|
|
||||
1 | / fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
```
|
||||
|
||||
Hmmm, we’re still getting an unused function warning, even though
|
||||
`network::connect` is set to `pub`. The reason is that the function is public
|
||||
within the module, but the `network` module that the function resides in is not
|
||||
public. We’re working from the interior of the library out this time, whereas
|
||||
with `client::connect` we worked from the outside in. We need to change
|
||||
*src/lib.rs* to make `network` public too, like so:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
pub mod client;
|
||||
|
||||
pub mod network;
|
||||
```
|
||||
|
||||
Now when we compile, that warning is gone:
|
||||
|
||||
```text
|
||||
warning: function is never used: `connect`
|
||||
--> src/network/server.rs:1:1
|
||||
|
|
||||
1 | / fn connect() {
|
||||
2 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: #[warn(dead_code)] on by default
|
||||
```
|
||||
|
||||
Only one warning is left—try to fix this one on your own!
|
||||
|
||||
### Privacy Rules
|
||||
|
||||
Overall, these are the rules for item visibility:
|
||||
|
||||
- If an item is public, it can be accessed through any of its parent modules.
|
||||
- If an item is private, it can be accessed only by its immediate parent
|
||||
module and any of the parent’s child modules.
|
||||
|
||||
### Privacy Examples
|
||||
|
||||
Let’s look at a few more privacy examples to get some practice. Create a new
|
||||
library project and enter the code in Listing 7-6 into your new project’s
|
||||
*src/lib.rs*:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
mod outermost {
|
||||
pub fn middle_function() {}
|
||||
|
||||
fn middle_secret_function() {}
|
||||
|
||||
mod inside {
|
||||
pub fn inner_function() {}
|
||||
|
||||
fn secret_function() {}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_me() {
|
||||
outermost::middle_function();
|
||||
outermost::middle_secret_function();
|
||||
outermost::inside::inner_function();
|
||||
outermost::inside::secret_function();
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-6: Examples of private and public functions,
|
||||
some of which are incorrect</span>
|
||||
|
||||
Before you try to compile this code, make a guess about which lines in the
|
||||
`try_me` function will have errors. Then, try compiling the code to see whether
|
||||
you were right—and read on for the discussion of the errors!
|
||||
|
||||
#### Looking at the Errors
|
||||
|
||||
The `try_me` function is in the root module of our project. The module named
|
||||
`outermost` is private, but the second privacy rule states that the `try_me`
|
||||
function is allowed to access the `outermost` module because `outermost` is in
|
||||
the current (root) module, as is `try_me`.
|
||||
|
||||
The call to `outermost::middle_function` will work because `middle_function` is
|
||||
public and `try_me` is accessing `middle_function` through its parent module
|
||||
`outermost`. We determined in the previous paragraph that this module is
|
||||
accessible.
|
||||
|
||||
The call to `outermost::middle_secret_function` will cause a compilation error.
|
||||
Because `middle_secret_function` is private, the second rule applies. The root
|
||||
module is neither the current module of `middle_secret_function` (`outermost`
|
||||
is), nor is it a child module of the current module of `middle_secret_function`.
|
||||
|
||||
The module named `inside` is private and has no child modules, so it can be
|
||||
accessed only by its current module `outermost`. That means the `try_me`
|
||||
function is not allowed to call `outermost::inside::inner_function` or
|
||||
`outermost::inside::secret_function`.
|
||||
|
||||
#### Fixing the Errors
|
||||
|
||||
Here are some suggestions for changing the code in an attempt to fix the
|
||||
errors. Make a guess as to whether it will fix the errors before you try each
|
||||
one. Then compile the code to see whether or not you’re right, using the
|
||||
privacy rules to understand why. Feel free to design more experiments and try
|
||||
them out!
|
||||
|
||||
* What if the `inside` module were public?
|
||||
* What if `outermost` were public and `inside` were private?
|
||||
* What if, in the body of `inner_function`, you called
|
||||
`::outermost::middle_secret_function()`? (The two colons at the beginning mean
|
||||
that we want to refer to the modules starting from the root module.)
|
||||
|
||||
Next, let’s talk about bringing items into scope with the `use` keyword.
|
||||
@ -0,0 +1,271 @@
|
||||
## Referring to Names in Different Modules
|
||||
|
||||
We’ve covered how to call functions defined within a module using the module
|
||||
name as part of the call, as in the call to the `nested_modules` function shown
|
||||
here in Listing 7-7:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
pub mod a {
|
||||
pub mod series {
|
||||
pub mod of {
|
||||
pub fn nested_modules() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a::series::of::nested_modules();
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-7: Calling a function by fully specifying its
|
||||
enclosing module’s path</span>
|
||||
|
||||
As you can see, referring to the fully qualified name can get quite lengthy.
|
||||
Fortunately, Rust has a keyword to make these calls more concise.
|
||||
|
||||
### Bringing Names into Scope with the `use` Keyword
|
||||
|
||||
Rust’s `use` keyword shortens lengthy function calls by bringing the modules of
|
||||
the function you want to call into scope. Here’s an example of bringing the
|
||||
`a::series::of` module into a binary crate’s root scope:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
pub mod a {
|
||||
pub mod series {
|
||||
pub mod of {
|
||||
pub fn nested_modules() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use a::series::of;
|
||||
|
||||
fn main() {
|
||||
of::nested_modules();
|
||||
}
|
||||
```
|
||||
|
||||
The line `use a::series::of;` means that rather than using the full
|
||||
`a::series::of` path wherever we want to refer to the `of` module, we can use
|
||||
`of`.
|
||||
|
||||
The `use` keyword brings only what we’ve specified into scope: it does not
|
||||
bring children of modules into scope. That’s why we still have to use
|
||||
`of::nested_modules` when we want to call the `nested_modules` function.
|
||||
|
||||
We could have chosen to bring the function into scope by instead specifying the
|
||||
function in the `use` as follows:
|
||||
|
||||
```rust
|
||||
pub mod a {
|
||||
pub mod series {
|
||||
pub mod of {
|
||||
pub fn nested_modules() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use a::series::of::nested_modules;
|
||||
|
||||
fn main() {
|
||||
nested_modules();
|
||||
}
|
||||
```
|
||||
|
||||
Doing so allows us to exclude all the modules and reference the function
|
||||
directly.
|
||||
|
||||
Because enums also form a sort of namespace like modules, we can bring an
|
||||
enum’s variants into scope with `use` as well. For any kind of `use` statement,
|
||||
if you’re bringing multiple items from one namespace into scope, you can list
|
||||
them using curly brackets and commas in the last position, like so:
|
||||
|
||||
```rust
|
||||
enum TrafficLight {
|
||||
Red,
|
||||
Yellow,
|
||||
Green,
|
||||
}
|
||||
|
||||
use TrafficLight::{Red, Yellow};
|
||||
|
||||
fn main() {
|
||||
let red = Red;
|
||||
let yellow = Yellow;
|
||||
let green = TrafficLight::Green;
|
||||
}
|
||||
```
|
||||
|
||||
We’re still specifying the `TrafficLight` namespace for the `Green` variant
|
||||
because we didn’t include `Green` in the `use` statement.
|
||||
|
||||
### Bringing All Names into Scope with a Glob
|
||||
|
||||
To bring all the items in a namespace into scope at once, we can use the `*`
|
||||
syntax, which is called the *glob operator*. This example brings all the
|
||||
variants of an enum into scope without having to list each specifically:
|
||||
|
||||
```rust
|
||||
enum TrafficLight {
|
||||
Red,
|
||||
Yellow,
|
||||
Green,
|
||||
}
|
||||
|
||||
use TrafficLight::*;
|
||||
|
||||
fn main() {
|
||||
let red = Red;
|
||||
let yellow = Yellow;
|
||||
let green = Green;
|
||||
}
|
||||
```
|
||||
|
||||
The `*` will bring into scope all the visible items in the `TrafficLight`
|
||||
namespace. You should use globs sparingly: they are convenient, but a glob
|
||||
might also pull in more items than you expected and cause naming conflicts.
|
||||
|
||||
### Using `super` to Access a Parent Module
|
||||
|
||||
As you saw at the beginning of this chapter, when you create a library crate,
|
||||
Cargo makes a `tests` module for you. Let’s go into more detail about that now.
|
||||
In your `communicator` project, open *src/lib.rs*:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
pub mod client;
|
||||
|
||||
pub mod network;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Chapter 11 explains more about testing, but parts of this example should make
|
||||
sense now: we have a module named `tests` that lives next to our other modules
|
||||
and contains one function named `it_works`. Even though there are special
|
||||
annotations, the `tests` module is just another module! So our module hierarchy
|
||||
looks like this:
|
||||
|
||||
```text
|
||||
communicator
|
||||
├── client
|
||||
├── network
|
||||
| └── client
|
||||
└── tests
|
||||
```
|
||||
|
||||
Tests are for exercising the code within our library, so let’s try to call our
|
||||
`client::connect` function from this `it_works` function, even though we won’t
|
||||
be checking any functionality right now. This won’t work yet:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
client::connect();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run the tests by invoking the `cargo test` command:
|
||||
|
||||
```text
|
||||
$ cargo test
|
||||
Compiling communicator v0.1.0 (file:///projects/communicator)
|
||||
error[E0433]: failed to resolve. Use of undeclared type or module `client`
|
||||
--> src/lib.rs:9:9
|
||||
|
|
||||
9 | client::connect();
|
||||
| ^^^^^^ Use of undeclared type or module `client`
|
||||
```
|
||||
|
||||
The compilation failed, but why? We don’t need to place `communicator::` in
|
||||
front of the function, as we did in *src/main.rs*, because we are definitely
|
||||
within the `communicator` library crate here. The reason is that paths are
|
||||
always relative to the current module, which here is `tests`. The only
|
||||
exception is in a `use` statement, where paths are relative to the crate root
|
||||
by default. Our `tests` module needs the `client` module in its scope!
|
||||
|
||||
So how do we get back up one module in the module hierarchy to call the
|
||||
`client::connect` function in the `tests` module? In the `tests` module, we can
|
||||
either use leading colons to let Rust know that we want to start from the root
|
||||
and list the whole path, like this:
|
||||
|
||||
```rust,ignore
|
||||
::client::connect();
|
||||
```
|
||||
|
||||
Or, we can use `super` to move up one module in the hierarchy from our current
|
||||
module, like this:
|
||||
|
||||
```rust,ignore
|
||||
super::client::connect();
|
||||
```
|
||||
|
||||
These two options don’t look that different in this example, but if you’re
|
||||
deeper in a module hierarchy, starting from the root every time would make your
|
||||
code lengthy. In those cases, using `super` to get from the current module to
|
||||
sibling modules is a good shortcut. Plus, if you’ve specified the path from the
|
||||
root in many places in your code and then rearrange your modules by moving a
|
||||
subtree to another place, you’ll end up needing to update the path in several
|
||||
places, which would be tedious.
|
||||
|
||||
It would also be annoying to have to type `super::` in each test, but you’ve
|
||||
already seen the tool for that solution: `use`! The `super::` functionality
|
||||
changes the path you give to `use` so it is relative to the parent module
|
||||
instead of to the root module.
|
||||
|
||||
For these reasons, in the `tests` module especially, `use super::something` is
|
||||
usually the best solution. So now our test looks like this:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::client;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
client::connect();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When we run `cargo test` again, the test will pass, and the first part of the
|
||||
test result output will be the following:
|
||||
|
||||
```text
|
||||
$ cargo test
|
||||
Compiling communicator v0.1.0 (file:///projects/communicator)
|
||||
Running target/debug/communicator-92007ddb5330fa5a
|
||||
|
||||
running 1 test
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Now you know some new techniques for organizing your code! Use these techniques
|
||||
to group related functionality together, keep files from becoming too long, and
|
||||
present a tidy public API to your library users.
|
||||
|
||||
Next, we’ll look at some collection data structures in the standard library
|
||||
that you can use in your nice, neat code.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user