mirror of
https://git.proxmox.com/git/rustc
synced 2025-12-30 18:58:53 +00:00
New upstream version 1.28.0~beta.14+dfsg1
This commit is contained in:
parent
5d61e2aca7
commit
94b46f3498
@ -48,7 +48,7 @@ always work, and sometimes it's hard to know what to search for, so consider thi
|
||||
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.
|
||||
consider filing an issue 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**.
|
||||
@ -142,7 +142,7 @@ 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.
|
||||
A default configuration requires 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)
|
||||
|
||||
@ -38,6 +38,7 @@ Read ["Installation"] from [The Book].
|
||||
3. Build and install:
|
||||
|
||||
```sh
|
||||
$ git submodule update --init --recursive --progress
|
||||
$ ./x.py build && sudo ./x.py install
|
||||
```
|
||||
|
||||
@ -119,7 +120,7 @@ shell with:
|
||||
> python x.py build
|
||||
```
|
||||
|
||||
Currently building Rust only works with some known versions of Visual Studio. If
|
||||
Currently, building Rust only works with some known versions of Visual Studio. If
|
||||
you have a more recent version installed the build system doesn't understand
|
||||
then you may need to force rustbuild to use an older version. This can be done
|
||||
by manually calling the appropriate vcvars file before running the bootstrap.
|
||||
@ -133,7 +134,7 @@ python x.py build
|
||||
[specifying-an-abi]: #specifying-an-abi
|
||||
|
||||
Each specific ABI can also be used from either environment (for example, using
|
||||
the GNU ABI in powershell) by using an explicit build triple. The available
|
||||
the GNU ABI in PowerShell) by using an explicit build triple. The available
|
||||
Windows build triples are:
|
||||
- GNU ABI (using GCC)
|
||||
- `i686-pc-windows-gnu`
|
||||
@ -179,7 +180,7 @@ the ABI used. I.e., if the ABI was `x86_64-pc-windows-msvc`, the directory will
|
||||
[notes]: #notes
|
||||
|
||||
Since the Rust compiler is written in Rust, it must be built by a
|
||||
precompiled "snapshot" version of itself (made in an earlier state of
|
||||
precompiled "snapshot" version of itself (made in an earlier stage of
|
||||
development). As such, source builds require a connection to the Internet, to
|
||||
fetch snapshots, and an OS that can execute the available snapshot binaries.
|
||||
|
||||
|
||||
162
RELEASES.md
162
RELEASES.md
@ -1,13 +1,144 @@
|
||||
Version 1.27.2 (2018-07-20)
|
||||
Version 1.28.0 (2018-08-02)
|
||||
===========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [The `#[repr(transparent)]` attribute is now stable.][51562] This attribute
|
||||
allows a Rust newtype wrapper (`struct NewType<T>(T);`) to be represented as
|
||||
the inner type across Foreign Function Interface (FFI) boundaries.
|
||||
- [The keywords `pure`, `sizeof`, `alignof`, and `offsetof` have been unreserved
|
||||
and can now be used as identifiers.][51196]
|
||||
- [The `GlobalAlloc` trait and `#[global_allocator]` attribute are now
|
||||
stable.][51241] This will allow users to specify a global allocator for
|
||||
their program.
|
||||
- [Unit test functions marked with the `#[test]` attribute can now return
|
||||
`Result<(), E: Debug>` in addition to `()`.][51298]
|
||||
- [The `lifetime` specifier for `macro_rules!` is now stable.][50385] This
|
||||
allows macros to easily target lifetimes.
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [The `s` and `z` optimisation levels are now stable.][50265] These optimisations
|
||||
prioritise making smaller binary sizes. `z` is the same as `s` with the
|
||||
exception that it does not vectorise loops, which typically results in an even
|
||||
smaller binary.
|
||||
- [The short error format is now stable.][49546] Specified with
|
||||
`--error-format=short` this option will provide a more compressed output of
|
||||
rust error messages.
|
||||
- [Added a lint warning when you have duplicated `macro_export`s.][50143]
|
||||
- [Reduced the number of allocations in the macro parser.][50855] This can
|
||||
improve compile times of macro heavy crates on average by 5%.
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Implemented `Default` for `&mut str`.][51306]
|
||||
- [Implemented `From<bool>` for all integer and unsigned number types.][50554]
|
||||
- [Implemented `Extend` for `()`.][50234]
|
||||
- [The `Debug` implementation of `time::Duration` should now be more easily
|
||||
human readable.][50364] Previously a `Duration` of one second would printed as
|
||||
`Duration { secs: 1, nanos: 0 }` and will now be printed as `1s`.
|
||||
- [Implemented `From<&String>` for `Cow<str>`, `From<&Vec<T>>` for `Cow<[T]>`,
|
||||
`From<Cow<CStr>>` for `CString`, `From<CString>, From<CStr>, From<&CString>`
|
||||
for `Cow<CStr>`, `From<OsString>, From<OsStr>, From<&OsString>` for
|
||||
`Cow<OsStr>`, `From<&PathBuf>` for `Cow<Path>`, and `From<Cow<Path>>`
|
||||
for `PathBuf`.][50170]
|
||||
- [Implemented `Shl` and `Shr` for `Wrapping<u128>`
|
||||
and `Wrapping<i128>`.][50465]
|
||||
- [`DirEntry::metadata` now uses `fstatat` instead of `lstat` when
|
||||
possible.][51050] This can provide up to a 40% speed increase.
|
||||
- [Improved error messages when using `format!`.][50610]
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
- [`Iterator::step_by`]
|
||||
- [`Path::ancestors`]
|
||||
- [`btree_map::Entry::or_default`]
|
||||
- [`fmt::Alignment`]
|
||||
- [`hash_map::Entry::or_default`]
|
||||
- [`iter::repeat_with`]
|
||||
- [`num::NonZeroUsize`]
|
||||
- [`num::NonZeroU128`]
|
||||
- [`num::NonZeroU16`]
|
||||
- [`num::NonZeroU32`]
|
||||
- [`num::NonZeroU64`]
|
||||
- [`num::NonZeroU8`]
|
||||
- [`ops::RangeBounds`]
|
||||
- [`slice::SliceIndex`]
|
||||
- [`slice::from_mut`]
|
||||
- [`slice::from_ref`]
|
||||
- [`{Any + Send + Sync}::downcast_mut`]
|
||||
- [`{Any + Send + Sync}::downcast_ref`]
|
||||
- [`{Any + Send + Sync}::is`]
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Cargo will now no longer allow you to publish crates with build scripts that
|
||||
modify the `src` directory.][cargo/5584] The `src` directory in a crate should be
|
||||
considered to be immutable.
|
||||
|
||||
Misc
|
||||
----
|
||||
- [The `suggestion_applicability` field in `rustc`'s json output is now
|
||||
stable.][50486] This will allow dev tools to check whether a code suggestion
|
||||
would apply to them.
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Rust will no longer consider trait objects with duplicated constraints to
|
||||
have implementations.][51276] For example the below code will now fail
|
||||
to compile.
|
||||
```rust
|
||||
trait Trait {}
|
||||
|
||||
- The borrow checker was fixed to avoid potential unsoundness when using
|
||||
match ergonomics: [#52213][52213].
|
||||
impl Trait + Send {
|
||||
fn test(&self) { println!("one"); } //~ ERROR duplicate definitions with name `test`
|
||||
}
|
||||
|
||||
[52213]: https://github.com/rust-lang/rust/issues/52213
|
||||
impl Trait + Send + Send {
|
||||
fn test(&self) { println!("two"); }
|
||||
}
|
||||
```
|
||||
|
||||
[49546]: https://github.com/rust-lang/rust/pull/49546/
|
||||
[50143]: https://github.com/rust-lang/rust/pull/50143/
|
||||
[50170]: https://github.com/rust-lang/rust/pull/50170/
|
||||
[50234]: https://github.com/rust-lang/rust/pull/50234/
|
||||
[50265]: https://github.com/rust-lang/rust/pull/50265/
|
||||
[50364]: https://github.com/rust-lang/rust/pull/50364/
|
||||
[50385]: https://github.com/rust-lang/rust/pull/50385/
|
||||
[50465]: https://github.com/rust-lang/rust/pull/50465/
|
||||
[50486]: https://github.com/rust-lang/rust/pull/50486/
|
||||
[50554]: https://github.com/rust-lang/rust/pull/50554/
|
||||
[50610]: https://github.com/rust-lang/rust/pull/50610/
|
||||
[50855]: https://github.com/rust-lang/rust/pull/50855/
|
||||
[51050]: https://github.com/rust-lang/rust/pull/51050/
|
||||
[51196]: https://github.com/rust-lang/rust/pull/51196/
|
||||
[51200]: https://github.com/rust-lang/rust/pull/51200/
|
||||
[51241]: https://github.com/rust-lang/rust/pull/51241/
|
||||
[51276]: https://github.com/rust-lang/rust/pull/51276/
|
||||
[51298]: https://github.com/rust-lang/rust/pull/51298/
|
||||
[51306]: https://github.com/rust-lang/rust/pull/51306/
|
||||
[51562]: https://github.com/rust-lang/rust/pull/51562/
|
||||
[cargo/5584]: https://github.com/rust-lang/cargo/pull/5584/
|
||||
[`Iterator::step_by`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.step_by
|
||||
[`Path::ancestors`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.ancestors
|
||||
[`btree_map::Entry::or_default`]: https://doc.rust-lang.org/std/collections/btree_map/enum.Entry.html#method.or_default
|
||||
[`fmt::Alignment`]: https://doc.rust-lang.org/std/fmt/enum.Alignment.html
|
||||
[`hash_map::Entry::or_default`]: https://doc.rust-lang.org/std/collections/btree_map/enum.Entry.html#method.or_default
|
||||
[`iter::repeat_with`]: https://doc.rust-lang.org/std/iter/fn.repeat_with.html
|
||||
[`num::NonZeroUsize`]: https://doc.rust-lang.org/std/num/struct.NonZeroUsize.html
|
||||
[`num::NonZeroU128`]: https://doc.rust-lang.org/std/num/struct.NonZeroU128.html
|
||||
[`num::NonZeroU16`]: https://doc.rust-lang.org/std/num/struct.NonZeroU16.html
|
||||
[`num::NonZeroU32`]: https://doc.rust-lang.org/std/num/struct.NonZeroU32.html
|
||||
[`num::NonZeroU64`]: https://doc.rust-lang.org/std/num/struct.NonZeroU64.html
|
||||
[`num::NonZeroU8`]: https://doc.rust-lang.org/std/num/struct.NonZeroU8.html
|
||||
[`ops::RangeBounds`]: https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
|
||||
[`slice::SliceIndex`]: https://doc.rust-lang.org/std/slice/trait.SliceIndex.html
|
||||
[`slice::from_mut`]: https://doc.rust-lang.org/std/slice/fn.from_mut.html
|
||||
[`slice::from_ref`]: https://doc.rust-lang.org/std/slice/fn.from_ref.html
|
||||
[`{Any + Send + Sync}::downcast_mut`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_mut-2
|
||||
[`{Any + Send + Sync}::downcast_ref`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_ref-2
|
||||
[`{Any + Send + Sync}::is`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.is-2
|
||||
|
||||
Version 1.27.1 (2018-07-10)
|
||||
===========================
|
||||
@ -18,7 +149,7 @@ 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].
|
||||
more about this on the [blog][rustdoc-sec]. The associated CVE is [CVE-2018-1000622].
|
||||
|
||||
Thank you to Red Hat for responsibily disclosing this vulnerability to us.
|
||||
|
||||
@ -31,6 +162,7 @@ Compatibility Notes
|
||||
[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
|
||||
[CVE-2018-1000622]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=%20CVE-2018-1000622
|
||||
|
||||
Version 1.27.0 (2018-06-21)
|
||||
==========================
|
||||
@ -90,7 +222,6 @@ Stabilized APIs
|
||||
- [`Take::set_limit`]
|
||||
- [`hint::unreachable_unchecked`]
|
||||
- [`os::unix::process::parent_id`]
|
||||
- [`process::id`]
|
||||
- [`ptr::swap_nonoverlapping`]
|
||||
- [`slice::rsplit_mut`]
|
||||
- [`slice::rsplit`]
|
||||
@ -100,6 +231,7 @@ Cargo
|
||||
-----
|
||||
- [`cargo-metadata` now includes `authors`, `categories`, `keywords`,
|
||||
`readme`, and `repository` fields.][cargo/5386]
|
||||
- [`cargo-metadata` now includes a package's `metadata` table.][cargo/5360]
|
||||
- [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,
|
||||
@ -149,6 +281,7 @@ Compatibility Notes
|
||||
[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/5360]: https://github.com/rust-lang/cargo/pull/5360/
|
||||
[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
|
||||
@ -165,17 +298,15 @@ Compatibility Notes
|
||||
[`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
|
||||
[`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
|
||||
[`process::id`]: https://doc.rust-lang.org/std/process/fn.id.html
|
||||
[`ptr::swap_nonoverlapping`]: https://doc.rust-lang.org/std/ptr/fn.swap_nonoverlapping.html
|
||||
[`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
|
||||
|
||||
|
||||
@ -199,6 +330,7 @@ Tools
|
||||
- [RLS now works on Windows][50646]
|
||||
- [Rustfmt stopped badly formatting text in some cases][rustfmt/2695]
|
||||
|
||||
|
||||
Compatibility Notes
|
||||
--------
|
||||
|
||||
@ -222,7 +354,7 @@ Language
|
||||
- [Closures now implement `Copy` and/or `Clone` if all captured variables
|
||||
implement either or both traits.][49299]
|
||||
- [The inclusive range syntax e.g. `for x in 0..=10` is now stable.][47813]
|
||||
- [Stablise `'_`. The underscore lifetime can be used anywhere where a
|
||||
- [The `'_` lifetime is now stable. The underscore lifetime can be used anywhere where a
|
||||
lifetime can be elided.][49458]
|
||||
- [`impl Trait` is now stable allowing you to have abstract types in returns
|
||||
or in function parameters.][49255] e.g. `fn foo() -> impl Iterator<Item=u8>` or
|
||||
@ -313,7 +445,6 @@ Cargo
|
||||
- [Cargo will now output path to custom commands when `-v` is
|
||||
passed with `--list`][cargo/5041]
|
||||
- [The Cargo binary version is now the same as the Rust version][cargo/5083]
|
||||
- [`Cargo.lock` files are now included in published crates.][cargo/5093]
|
||||
|
||||
Misc
|
||||
----
|
||||
@ -417,7 +548,6 @@ Compatibility Notes
|
||||
[`String::retain`]: https://doc.rust-lang.org/std/string/struct.String.html#method.retain
|
||||
[cargo/5041]: https://github.com/rust-lang/cargo/pull/5041
|
||||
[cargo/5083]: https://github.com/rust-lang/cargo/pull/5083
|
||||
[cargo/5093]: https://github.com/rust-lang/cargo/pull/5093
|
||||
|
||||
|
||||
Version 1.25.0 (2018-03-29)
|
||||
@ -425,7 +555,7 @@ Version 1.25.0 (2018-03-29)
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Stabilised `#[repr(align(x))]`.][47006] [RFC 1358]
|
||||
- [The `#[repr(align(x))]` attribute is now stable.][47006] [RFC 1358]
|
||||
- [You can now use nested groups of imports.][47948]
|
||||
e.g. `use std::{fs::File, io::Read, path::{Path, PathBuf}};`
|
||||
- [You can now have `|` at the start of a match arm.][47947] e.g.
|
||||
|
||||
@ -76,6 +76,10 @@
|
||||
# passed to prefer linking to shared libraries.
|
||||
#link-shared = false
|
||||
|
||||
# On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass
|
||||
# with clang-cl, so this is special in that it only compiles LLVM with clang-cl
|
||||
#clang-cl = '/path/to/clang-cl.exe'
|
||||
|
||||
# =============================================================================
|
||||
# General build configuration options
|
||||
# =============================================================================
|
||||
@ -275,6 +279,9 @@
|
||||
# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
|
||||
#backtrace = true
|
||||
|
||||
# Whether to always use incremental compilation when building rustc
|
||||
#incremental = false
|
||||
|
||||
# Build rustc with experimental parallelization
|
||||
#experimental-parallel-queries = false
|
||||
|
||||
@ -294,9 +301,9 @@
|
||||
# desired in distributions, for example.
|
||||
#rpath = true
|
||||
|
||||
# Suppresses extraneous output from tests to ensure the output of the test
|
||||
# harness is relatively clean.
|
||||
#quiet-tests = false
|
||||
# Emits extraneous output from tests to ensure that failures of the test
|
||||
# harness are debuggable just from logfiles.
|
||||
#verbose-tests = false
|
||||
|
||||
# Flag indicating whether tests are compiled with optimizations (the -O flag) or
|
||||
# with debuginfo (the -g flag)
|
||||
|
||||
@ -1 +1 @@
|
||||
58cc626de3301192d5d8c6dcbde43b5b44211ae2
|
||||
0aaa819fea00a6bf2b1303b9eb47f142f099cf5a
|
||||
617
src/Cargo.lock
generated
617
src/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ members = [
|
||||
"rustc",
|
||||
"libstd",
|
||||
"libtest",
|
||||
"librustc_trans",
|
||||
"librustc_codegen_llvm",
|
||||
"tools/cargotest",
|
||||
"tools/clippy",
|
||||
"tools/compiletest",
|
||||
@ -40,6 +40,13 @@ members = [
|
||||
"tools/rls/test_data/workspace_symbol",
|
||||
]
|
||||
|
||||
# Curiously, libtest will segfault if compiled with opt-level=3
|
||||
# with some versions of XCode: https://github.com/rust-lang/rust/issues/50867
|
||||
[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]
|
||||
@ -57,11 +64,8 @@ debug-assertions = false
|
||||
[patch."https://github.com/rust-lang/cargo"]
|
||||
cargo = { path = "tools/cargo" }
|
||||
|
||||
[patch.crates-io]
|
||||
[patch."https://github.com/rust-lang-nursery/rustfmt"]
|
||||
# Similar to Cargo above we want the RLS to use a vendored version of `rustfmt`
|
||||
# that we're shipping as well (to ensure that the rustfmt in RLS and the
|
||||
# `rustfmt` executable are the same exact vesion). Unlike Cargo, however, the
|
||||
# RLS depends on `rustfmt` from crates.io, so we put this in a `[patch]` section
|
||||
# for crates.io
|
||||
# `rustfmt` executable are the same exact vesion).
|
||||
rustfmt-nightly = { path = "tools/rustfmt" }
|
||||
clippy_lints = { path = "tools/clippy/clippy_lints" }
|
||||
|
||||
@ -6,7 +6,7 @@ This directory contains the source code of the rust project, including:
|
||||
For more information on how various parts of the compiler work, see the [rustc guide].
|
||||
|
||||
Their is also useful content in the following READMEs, which are gradually being moved over to the guide:
|
||||
- https://github.com/rust-lang/rust/tree/master/src/librustc/ty/maps
|
||||
- https://github.com/rust-lang/rust/tree/master/src/librustc/ty/query
|
||||
- https://github.com/rust-lang/rust/tree/master/src/librustc/dep_graph
|
||||
- https://github.com/rust-lang/rust/blob/master/src/librustc/infer/region_constraints
|
||||
- https://github.com/rust-lang/rust/tree/master/src/librustc/infer/higher_ranked
|
||||
|
||||
@ -28,6 +28,11 @@ name = "sccache-plus-cl"
|
||||
path = "bin/sccache-plus-cl.rs"
|
||||
test = false
|
||||
|
||||
[[bin]]
|
||||
name = "llvm-config-wrapper"
|
||||
path = "bin/llvm-config-wrapper.rs"
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
build_helper = { path = "../build_helper" }
|
||||
cmake = "0.1.23"
|
||||
|
||||
27
src/bootstrap/bin/llvm-config-wrapper.rs
Normal file
27
src/bootstrap/bin/llvm-config-wrapper.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// The sheer existence of this file is an awful hack. See the comments in
|
||||
// `src/bootstrap/native.rs` for why this is needed when compiling LLD.
|
||||
|
||||
use std::env;
|
||||
use std::process::{self, Stdio, Command};
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
let real_llvm_config = env::var_os("LLVM_CONFIG_REAL").unwrap();
|
||||
let mut cmd = Command::new(real_llvm_config);
|
||||
cmd.args(env::args().skip(1)).stderr(Stdio::piped());
|
||||
let output = cmd.output().expect("failed to spawn llvm-config");
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
print!("{}", stdout.replace("\\", "/"));
|
||||
io::stdout().flush().unwrap();
|
||||
process::exit(output.status.code().unwrap_or(1));
|
||||
}
|
||||
@ -268,6 +268,15 @@ fn main() {
|
||||
if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
|
||||
cmd.arg(format!("-Clinker={}", host_linker));
|
||||
}
|
||||
|
||||
if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
|
||||
if s == "true" {
|
||||
cmd.arg("-C").arg("target-feature=+crt-static");
|
||||
}
|
||||
if s == "false" {
|
||||
cmd.arg("-C").arg("target-feature=-crt-static");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() {
|
||||
@ -288,7 +297,12 @@ fn main() {
|
||||
}
|
||||
|
||||
if verbose > 1 {
|
||||
eprintln!("rustc command: {:?}", cmd);
|
||||
eprintln!(
|
||||
"rustc command: {:?}={:?} {:?}",
|
||||
bootstrap::util::dylib_path_var(),
|
||||
env::join_paths(&dylib_path).unwrap(),
|
||||
cmd,
|
||||
);
|
||||
eprintln!("sysroot: {:?}", sysroot);
|
||||
eprintln!("libdir: {:?}", libdir);
|
||||
}
|
||||
|
||||
@ -16,8 +16,8 @@ use std::process::{self, Command};
|
||||
fn main() {
|
||||
let target = env::var("SCCACHE_TARGET").unwrap();
|
||||
// Locate the actual compiler that we're invoking
|
||||
env::remove_var("CC");
|
||||
env::remove_var("CXX");
|
||||
env::set_var("CC", env::var_os("SCCACHE_CC").unwrap());
|
||||
env::set_var("CXX", env::var_os("SCCACHE_CXX").unwrap());
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false)
|
||||
.out_dir("/")
|
||||
@ -39,6 +39,12 @@ fn main() {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
|
||||
if let Ok(s) = env::var("SCCACHE_EXTRA_ARGS") {
|
||||
for s in s.split_whitespace() {
|
||||
cmd.arg(s);
|
||||
}
|
||||
}
|
||||
|
||||
let status = cmd.status().expect("failed to spawn");
|
||||
process::exit(status.code().unwrap_or(2))
|
||||
}
|
||||
|
||||
@ -489,7 +489,7 @@ class RustBuild(object):
|
||||
"""
|
||||
return os.path.join(self.build_dir, self.build, "stage0")
|
||||
|
||||
def get_toml(self, key):
|
||||
def get_toml(self, key, section=None):
|
||||
"""Returns the value of the given key in config.toml, otherwise returns None
|
||||
|
||||
>>> rb = RustBuild()
|
||||
@ -501,12 +501,29 @@ class RustBuild(object):
|
||||
|
||||
>>> rb.get_toml("key3") is None
|
||||
True
|
||||
|
||||
Optionally also matches the section the key appears in
|
||||
|
||||
>>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
|
||||
>>> rb.get_toml('key', 'a')
|
||||
'value1'
|
||||
>>> rb.get_toml('key', 'b')
|
||||
'value2'
|
||||
>>> rb.get_toml('key', 'c') is None
|
||||
True
|
||||
"""
|
||||
|
||||
cur_section = None
|
||||
for line in self.config_toml.splitlines():
|
||||
section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
|
||||
if section_match is not None:
|
||||
cur_section = section_match.group(1)
|
||||
|
||||
match = re.match(r'^{}\s*=(.*)$'.format(key), line)
|
||||
if match is not None:
|
||||
value = match.group(1)
|
||||
return self.get_string(value) or value.strip()
|
||||
if section is None or section == cur_section:
|
||||
return self.get_string(value) or value.strip()
|
||||
return None
|
||||
|
||||
def cargo(self):
|
||||
@ -589,7 +606,17 @@ class RustBuild(object):
|
||||
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
|
||||
(os.pathsep + env["LIBRARY_PATH"]) \
|
||||
if "LIBRARY_PATH" in env else ""
|
||||
env["RUSTFLAGS"] = "-Cdebuginfo=2"
|
||||
env["RUSTFLAGS"] = "-Cdebuginfo=2 "
|
||||
|
||||
build_section = "target.{}".format(self.build_triple())
|
||||
target_features = []
|
||||
if self.get_toml("crt-static", build_section) == "true":
|
||||
target_features += ["+crt-static"]
|
||||
elif self.get_toml("crt-static", build_section) == "false":
|
||||
target_features += ["-crt-static"]
|
||||
if target_features:
|
||||
env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " "
|
||||
|
||||
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
|
||||
os.pathsep + env["PATH"]
|
||||
if not os.path.isfile(self.cargo()):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ use Build;
|
||||
use config::Config;
|
||||
|
||||
// The version number
|
||||
pub const CFG_RELEASE_NUM: &str = "1.27.2";
|
||||
pub const CFG_RELEASE_NUM: &str = "1.28.0";
|
||||
|
||||
pub struct GitInfo {
|
||||
inner: Option<Info>,
|
||||
|
||||
@ -40,10 +40,10 @@ impl Step for Std {
|
||||
let target = self.target;
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libstd);
|
||||
let out_dir = builder.stage_out(compiler, Mode::Std);
|
||||
builder.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "check");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Std, target, "check");
|
||||
std_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
@ -87,11 +87,11 @@ impl Step for Rustc {
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
|
||||
let stage_out = builder.stage_out(compiler, Mode::Librustc);
|
||||
let stage_out = builder.stage_out(compiler, Mode::Rustc);
|
||||
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");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "check");
|
||||
rustc_cargo(builder, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
@ -118,7 +118,7 @@ impl Step for CodegenBackend {
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.all_krates("rustc_trans")
|
||||
run.all_krates("rustc_codegen_llvm")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -137,14 +137,14 @@ impl Step for CodegenBackend {
|
||||
let target = self.target;
|
||||
let backend = self.backend;
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Codegen, target, "check");
|
||||
let features = builder.rustc_features().to_string();
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_trans/Cargo.toml"));
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_codegen_llvm/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));
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage));
|
||||
run_cargo(builder,
|
||||
cargo.arg("--features").arg(features),
|
||||
&codegen_backend_stamp(builder, compiler, target, backend),
|
||||
@ -175,10 +175,10 @@ impl Step for Test {
|
||||
let compiler = builder.compiler(0, builder.config.build);
|
||||
let target = self.target;
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libtest);
|
||||
let out_dir = builder.stage_out(compiler, Mode::Test);
|
||||
builder.clear_if_dirty(&out_dir, &libstd_stamp(builder, compiler, target));
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "check");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Test, target, "check");
|
||||
test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
@ -219,6 +219,7 @@ impl Step for Rustdoc {
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
Mode::ToolRustc,
|
||||
target,
|
||||
"check",
|
||||
"src/tools/rustdoc");
|
||||
@ -236,7 +237,7 @@ impl Step for Rustdoc {
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler,
|
||||
target,
|
||||
mode: Mode::Tool,
|
||||
cause: Mode::Rustc,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -244,33 +245,33 @@ impl Step for Rustdoc {
|
||||
/// 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libstd, target).join(".libstd-check.stamp")
|
||||
builder.cargo_out(compiler, Mode::Std, 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libtest, target).join(".libtest-check.stamp")
|
||||
builder.cargo_out(compiler, Mode::Test, 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Librustc, target).join(".librustc-check.stamp")
|
||||
builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for librustc_trans in a given stage, compiled by a particular
|
||||
/// Cargo's output path for librustc_codegen_llvm 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))
|
||||
builder.cargo_out(compiler, Mode::Codegen, target)
|
||||
.join(format!(".librustc_codegen_llvm-{}-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")
|
||||
builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rustdoc-check.stamp")
|
||||
}
|
||||
|
||||
@ -98,9 +98,9 @@ impl Step for Std {
|
||||
copy_musl_third_party_objects(builder, target, &libdir);
|
||||
}
|
||||
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Libstd, target);
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Std, target);
|
||||
builder.clear_if_dirty(&out_dir, &builder.rustc(compiler));
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libstd, target, "build");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Std, target, "build");
|
||||
std_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-std", compiler.stage));
|
||||
@ -240,7 +240,7 @@ impl Step for StdLink {
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler: target_compiler,
|
||||
target,
|
||||
mode: Mode::Libstd,
|
||||
cause: Mode::Std,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -368,9 +368,9 @@ impl Step for Test {
|
||||
return;
|
||||
}
|
||||
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Libtest, target);
|
||||
let out_dir = builder.cargo_out(compiler, Mode::Test, target);
|
||||
builder.clear_if_dirty(&out_dir, &libstd_stamp(builder, compiler, target));
|
||||
let mut cargo = builder.cargo(compiler, Mode::Libtest, target, "build");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Test, target, "build");
|
||||
test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-test", compiler.stage));
|
||||
@ -431,7 +431,7 @@ impl Step for TestLink {
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler: target_compiler,
|
||||
target,
|
||||
mode: Mode::Libtest,
|
||||
cause: Mode::Test,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -489,11 +489,11 @@ impl Step for Rustc {
|
||||
compiler: builder.compiler(self.compiler.stage, builder.config.build),
|
||||
target: builder.config.build,
|
||||
});
|
||||
let cargo_out = builder.cargo_out(compiler, Mode::Librustc, target);
|
||||
let cargo_out = builder.cargo_out(compiler, Mode::Rustc, 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");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "build");
|
||||
rustc_cargo(builder, &mut cargo);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc", compiler.stage));
|
||||
@ -585,7 +585,7 @@ impl Step for RustcLink {
|
||||
builder.ensure(tool::CleanTools {
|
||||
compiler: target_compiler,
|
||||
target,
|
||||
mode: Mode::Librustc,
|
||||
cause: Mode::Rustc,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -603,7 +603,7 @@ impl Step for CodegenBackend {
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.all_krates("rustc_trans")
|
||||
run.all_krates("rustc_codegen_llvm")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -634,18 +634,18 @@ impl Step for CodegenBackend {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Codegen, target, "build");
|
||||
let mut features = builder.rustc_features().to_string();
|
||||
cargo.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/librustc_trans/Cargo.toml"));
|
||||
.arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml"));
|
||||
rustc_cargo_env(builder, &mut cargo);
|
||||
|
||||
features += &build_codegen_backend(&builder, &mut cargo, &compiler, target, backend);
|
||||
|
||||
let tmp_stamp = builder.cargo_out(compiler, Mode::Librustc, target)
|
||||
let tmp_stamp = builder.cargo_out(compiler, Mode::Codegen, target)
|
||||
.join(".tmp.stamp");
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage));
|
||||
let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage));
|
||||
let files = run_cargo(builder,
|
||||
cargo.arg("--features").arg(features),
|
||||
&tmp_stamp,
|
||||
@ -656,7 +656,7 @@ impl Step for CodegenBackend {
|
||||
let mut files = files.into_iter()
|
||||
.filter(|f| {
|
||||
let filename = f.file_name().unwrap().to_str().unwrap();
|
||||
is_dylib(filename) && filename.contains("rustc_trans-")
|
||||
is_dylib(filename) && filename.contains("rustc_codegen_llvm-")
|
||||
});
|
||||
let codegen_backend = match files.next() {
|
||||
Some(f) => f,
|
||||
@ -697,7 +697,7 @@ pub fn build_codegen_backend(builder: &Builder,
|
||||
compiler.stage, &compiler.host, target, backend));
|
||||
|
||||
// Pass down configuration from the LLVM build into the build of
|
||||
// librustc_llvm and librustc_trans.
|
||||
// librustc_llvm and librustc_codegen_llvm.
|
||||
if builder.is_rust_llvm(target) {
|
||||
cargo.env("LLVM_RUSTLLVM", "1");
|
||||
}
|
||||
@ -762,7 +762,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder,
|
||||
t!(t!(File::open(&stamp)).read_to_string(&mut dylib));
|
||||
let file = Path::new(&dylib);
|
||||
let filename = file.file_name().unwrap().to_str().unwrap();
|
||||
// change `librustc_trans-xxxxxx.so` to `librustc_trans-llvm.so`
|
||||
// change `librustc_codegen_llvm-xxxxxx.so` to `librustc_codegen_llvm-llvm.so`
|
||||
let target_filename = {
|
||||
let dash = filename.find("-").unwrap();
|
||||
let dot = filename.find(".").unwrap();
|
||||
@ -793,29 +793,29 @@ fn copy_lld_to_sysroot(builder: &Builder,
|
||||
/// 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libstd, target).join(".libstd.stamp")
|
||||
builder.cargo_out(compiler, Mode::Std, 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Libtest, target).join(".libtest.stamp")
|
||||
builder.cargo_out(compiler, Mode::Test, 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(builder: &Builder, compiler: Compiler, target: Interned<String>) -> PathBuf {
|
||||
builder.cargo_out(compiler, Mode::Librustc, target).join(".librustc.stamp")
|
||||
builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp")
|
||||
}
|
||||
|
||||
/// Cargo's output path for librustc_trans in a given stage, compiled by a particular
|
||||
/// Cargo's output path for librustc_codegen_llvm 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-{}.stamp", backend))
|
||||
builder.cargo_out(compiler, Mode::Codegen, target)
|
||||
.join(format!(".librustc_codegen_llvm-{}.stamp", backend))
|
||||
}
|
||||
|
||||
pub fn compiler_file(builder: &Builder,
|
||||
@ -971,8 +971,8 @@ impl Step for Assemble {
|
||||
}
|
||||
|
||||
// Link the compiler binary itself into place
|
||||
let out_dir = builder.cargo_out(build_compiler, Mode::Librustc, host);
|
||||
let rustc = out_dir.join(exe("rustc", &*host));
|
||||
let out_dir = builder.cargo_out(build_compiler, Mode::Rustc, host);
|
||||
let rustc = out_dir.join(exe("rustc_binary", &*host));
|
||||
let bindir = sysroot.join("bin");
|
||||
t!(fs::create_dir_all(&bindir));
|
||||
let compiler = builder.rustc(target_compiler);
|
||||
|
||||
@ -82,6 +82,7 @@ pub struct Config {
|
||||
pub llvm_version_check: bool,
|
||||
pub llvm_static_stdcpp: bool,
|
||||
pub llvm_link_shared: bool,
|
||||
pub llvm_clang_cl: Option<String>,
|
||||
pub llvm_targets: Option<String>,
|
||||
pub llvm_experimental_targets: String,
|
||||
pub llvm_link_jobs: Option<u32>,
|
||||
@ -124,7 +125,7 @@ pub struct Config {
|
||||
// misc
|
||||
pub low_priority: bool,
|
||||
pub channel: String,
|
||||
pub quiet_tests: bool,
|
||||
pub verbose_tests: bool,
|
||||
pub test_miri: bool,
|
||||
pub save_toolstates: Option<PathBuf>,
|
||||
pub print_step_timings: bool,
|
||||
@ -250,6 +251,7 @@ struct Llvm {
|
||||
experimental_targets: Option<String>,
|
||||
link_jobs: Option<u32>,
|
||||
link_shared: Option<bool>,
|
||||
clang_cl: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
@ -299,8 +301,9 @@ struct Rust {
|
||||
ignore_git: Option<bool>,
|
||||
debug: Option<bool>,
|
||||
dist_src: Option<bool>,
|
||||
quiet_tests: Option<bool>,
|
||||
verbose_tests: Option<bool>,
|
||||
test_miri: Option<bool>,
|
||||
incremental: Option<bool>,
|
||||
save_toolstates: Option<String>,
|
||||
codegen_backends: Option<Vec<String>>,
|
||||
codegen_backends_dir: Option<String>,
|
||||
@ -504,6 +507,7 @@ impl Config {
|
||||
config.llvm_experimental_targets = llvm.experimental_targets.clone()
|
||||
.unwrap_or("WebAssembly".to_string());
|
||||
config.llvm_link_jobs = llvm.link_jobs;
|
||||
config.llvm_clang_cl = llvm.clang_cl.clone();
|
||||
}
|
||||
|
||||
if let Some(ref rust) = toml.rust {
|
||||
@ -524,8 +528,12 @@ impl Config {
|
||||
set(&mut config.backtrace, rust.backtrace);
|
||||
set(&mut config.channel, rust.channel.clone());
|
||||
set(&mut config.rust_dist_src, rust.dist_src);
|
||||
set(&mut config.quiet_tests, rust.quiet_tests);
|
||||
set(&mut config.verbose_tests, rust.verbose_tests);
|
||||
set(&mut config.test_miri, rust.test_miri);
|
||||
// in the case "false" is set explicitly, do not overwrite the command line args
|
||||
if let Some(true) = rust.incremental {
|
||||
config.incremental = true;
|
||||
}
|
||||
set(&mut config.wasm_syscall, rust.wasm_syscall);
|
||||
set(&mut config.lld_enabled, rust.lld);
|
||||
config.rustc_parallel_queries = rust.experimental_parallel_queries.unwrap_or(false);
|
||||
|
||||
@ -47,7 +47,7 @@ o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
|
||||
o("experimental-parallel-queries", "rust.experimental-parallel-queries", "build rustc with experimental parallelization")
|
||||
o("test-miri", "rust.test-miri", "run miri's test suite")
|
||||
o("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
|
||||
o("quiet-tests", "rust.quiet-tests", "enable quieter output when running tests")
|
||||
o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
|
||||
o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
|
||||
o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
|
||||
o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
|
||||
|
||||
@ -722,7 +722,7 @@ impl Step for Analysis {
|
||||
|
||||
let image = tmpdir(builder).join(format!("{}-{}-image", name, target));
|
||||
|
||||
let src = builder.stage_out(compiler, Mode::Libstd)
|
||||
let src = builder.stage_out(compiler, Mode::Std)
|
||||
.join(target).join(builder.cargo_dir()).join("deps");
|
||||
|
||||
let image_src = src.join("save-analysis");
|
||||
@ -951,13 +951,16 @@ impl Step for PlainSourceTarball {
|
||||
has_cargo_vendor |= line.starts_with("cargo-vendor ");
|
||||
}
|
||||
if !has_cargo_vendor {
|
||||
let mut cmd = Command::new(&builder.initial_cargo);
|
||||
cmd.arg("install")
|
||||
.arg("--force")
|
||||
let mut cmd = builder.cargo(
|
||||
builder.compiler(0, builder.config.build),
|
||||
Mode::ToolRustc,
|
||||
builder.config.build,
|
||||
"install"
|
||||
);
|
||||
cmd.arg("--force")
|
||||
.arg("--debug")
|
||||
.arg("--vers").arg(CARGO_VENDOR_VERSION)
|
||||
.arg("cargo-vendor")
|
||||
.env("RUSTC", &builder.initial_rustc);
|
||||
.arg("cargo-vendor");
|
||||
if let Some(dir) = builder.openssl_install_dir(builder.config.build) {
|
||||
builder.ensure(native::Openssl {
|
||||
target: builder.config.build,
|
||||
|
||||
@ -28,7 +28,7 @@ use build_helper::up_to_date;
|
||||
|
||||
use util::symlink_dir;
|
||||
use builder::{Builder, Compiler, RunConfig, ShouldRun, Step};
|
||||
use tool::Tool;
|
||||
use tool::{self, prepare_tool_cargo, Tool};
|
||||
use compile;
|
||||
use cache::{INTERNER, Interned};
|
||||
use config::Config;
|
||||
@ -70,7 +70,7 @@ macro_rules! book {
|
||||
book!(
|
||||
Nomicon, "src/doc/nomicon", "nomicon";
|
||||
Reference, "src/doc/reference", "reference";
|
||||
Rustdoc, "src/doc/rustdoc", "rustdoc";
|
||||
RustdocBook, "src/doc/rustdoc", "rustdoc";
|
||||
RustcBook, "src/doc/rustc", "rustc";
|
||||
RustByExample, "src/doc/rust-by-example", "rust-by-example";
|
||||
);
|
||||
@ -272,6 +272,12 @@ impl Step for TheBook {
|
||||
name: INTERNER.intern_string(format!("{}/second-edition", name)),
|
||||
});
|
||||
|
||||
// build book 2018 edition
|
||||
builder.ensure(Rustbook {
|
||||
target,
|
||||
name: INTERNER.intern_string(format!("{}/2018-edition", name)),
|
||||
});
|
||||
|
||||
// build the version info page and CSS
|
||||
builder.ensure(Standalone {
|
||||
compiler,
|
||||
@ -457,7 +463,7 @@ impl Step for Std {
|
||||
};
|
||||
|
||||
builder.ensure(compile::Std { compiler, target });
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libstd)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Std)
|
||||
.join(target).join("doc");
|
||||
|
||||
// Here what we're doing is creating a *symlink* (directory junction on
|
||||
@ -477,7 +483,7 @@ impl Step for Std {
|
||||
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");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Std, target, "doc");
|
||||
compile::std_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
// Keep a whitelist so we do not build internal stdlib crates, these will be
|
||||
@ -540,7 +546,7 @@ impl Step for Test {
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Test { compiler, target });
|
||||
let out_dir = builder.stage_out(compiler, Mode::Libtest)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Test)
|
||||
.join(target).join("doc");
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
@ -548,7 +554,7 @@ impl Step for Test {
|
||||
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");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Test, target, "doc");
|
||||
compile::test_cargo(builder, &compiler, target, &mut cargo);
|
||||
|
||||
cargo.arg("--no-deps").arg("-p").arg("test");
|
||||
@ -608,7 +614,7 @@ impl Step for WhitelistedRustc {
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
let out_dir = builder.stage_out(compiler, Mode::Librustc)
|
||||
let out_dir = builder.stage_out(compiler, Mode::Rustc)
|
||||
.join(target).join("doc");
|
||||
|
||||
// See docs in std above for why we symlink
|
||||
@ -616,7 +622,7 @@ impl Step for WhitelistedRustc {
|
||||
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");
|
||||
let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
|
||||
compile::rustc_cargo(builder, &mut cargo);
|
||||
|
||||
// We don't want to build docs for internal compiler dependencies in this
|
||||
@ -665,8 +671,12 @@ impl Step for Rustc {
|
||||
let stage = self.stage;
|
||||
let target = self.target;
|
||||
builder.info(&format!("Documenting stage{} compiler ({})", stage, target));
|
||||
|
||||
// This is the intended out directory for compiler documentation.
|
||||
let out = builder.compiler_doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
// Get the correct compiler for this stage.
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
@ -676,22 +686,24 @@ impl Step for Rustc {
|
||||
};
|
||||
|
||||
if !builder.config.compiler_docs {
|
||||
builder.info(&format!("\tskipping - compiler docs disabled"));
|
||||
builder.info(&format!("\tskipping - compiler/librustdoc docs disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build libstd docs so that we generate relative links
|
||||
// Build libstd docs so that we generate relative links.
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
// Build rustc.
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
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.
|
||||
let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target).join("doc");
|
||||
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");
|
||||
// Build cargo command.
|
||||
let mut cargo = builder.cargo(compiler, Mode::Rustc, target, "doc");
|
||||
cargo.env("RUSTDOCFLAGS", "--document-private-items");
|
||||
compile::rustc_cargo(builder, &mut cargo);
|
||||
|
||||
@ -729,6 +741,78 @@ fn find_compiler_crates(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rustdoc {
|
||||
stage: u32,
|
||||
target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustdoc {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.krate("rustdoc-tool")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rustdoc {
|
||||
stage: run.builder.top_stage,
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Generate compiler documentation.
|
||||
///
|
||||
/// This will generate all documentation for compiler and dependencies.
|
||||
/// Compiler documentation is distributed separately, so we make sure
|
||||
/// 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 stage = self.stage;
|
||||
let target = self.target;
|
||||
builder.info(&format!("Documenting stage{} rustdoc ({})", stage, target));
|
||||
|
||||
// This is the intended out directory for compiler documentation.
|
||||
let out = builder.compiler_doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
||||
// Get the correct compiler for this stage.
|
||||
let compiler = builder.compiler(stage, builder.config.build);
|
||||
let rustdoc = builder.rustdoc(compiler.host);
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
if !builder.config.compiler_docs {
|
||||
builder.info(&format!("\tskipping - compiler/librustdoc docs disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build libstd docs so that we generate relative links.
|
||||
builder.ensure(Std { stage, target });
|
||||
|
||||
// Build rustdoc.
|
||||
builder.ensure(tool::Rustdoc { host: compiler.host });
|
||||
|
||||
// Symlink compiler docs to the output directory of rustdoc documentation.
|
||||
let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target).join("doc");
|
||||
t!(fs::create_dir_all(&out_dir));
|
||||
builder.clear_if_dirty(&out, &rustdoc);
|
||||
t!(symlink_dir_force(&builder.config, &out, &out_dir));
|
||||
|
||||
// Build cargo command.
|
||||
let mut cargo = prepare_tool_cargo(
|
||||
builder, compiler, Mode::ToolRustc, target, "doc", "src/tools/rustdoc");
|
||||
|
||||
cargo.env("RUSTDOCFLAGS", "--document-private-items");
|
||||
builder.run(&mut cargo);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct ErrorIndex {
|
||||
target: Interned<String>,
|
||||
|
||||
@ -19,10 +19,10 @@ use std::process;
|
||||
|
||||
use getopts::Options;
|
||||
|
||||
use {Build, DocTests};
|
||||
use builder::Builder;
|
||||
use config::Config;
|
||||
use metadata;
|
||||
use builder::Builder;
|
||||
use {Build, DocTests};
|
||||
|
||||
use cache::{Interned, INTERNER};
|
||||
|
||||
@ -59,6 +59,9 @@ pub enum Subcommand {
|
||||
},
|
||||
Test {
|
||||
paths: Vec<PathBuf>,
|
||||
/// Whether to automatically update stderr/stdout files
|
||||
bless: bool,
|
||||
compare_mode: Option<String>,
|
||||
test_args: Vec<String>,
|
||||
rustc_args: Vec<String>,
|
||||
fail_fast: bool,
|
||||
@ -90,7 +93,8 @@ impl Default for Subcommand {
|
||||
impl Flags {
|
||||
pub fn parse(args: &[String]) -> Flags {
|
||||
let mut extra_help = String::new();
|
||||
let mut subcommand_help = format!("\
|
||||
let mut subcommand_help = format!(
|
||||
"\
|
||||
Usage: x.py <subcommand> [options] [<paths>...]
|
||||
|
||||
Subcommands:
|
||||
@ -103,7 +107,8 @@ Subcommands:
|
||||
dist Build distribution artifacts
|
||||
install Install distribution artifacts
|
||||
|
||||
To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
To learn more about a subcommand, run `./x.py <subcommand> -h`"
|
||||
);
|
||||
|
||||
let mut opts = Options::new();
|
||||
// Options common to all subcommands
|
||||
@ -121,33 +126,39 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
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(
|
||||
"",
|
||||
"warnings",
|
||||
"if value is deny, will deny warnings, otherwise use default",
|
||||
"VALUE",
|
||||
);
|
||||
opts.optopt("", "error-format", "rustc error format", "FORMAT");
|
||||
|
||||
// fn usage()
|
||||
let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
|
||||
println!("{}", opts.usage(subcommand_help));
|
||||
if !extra_help.is_empty() {
|
||||
println!("{}", extra_help);
|
||||
}
|
||||
process::exit(exit_code);
|
||||
};
|
||||
let usage =
|
||||
|exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
|
||||
println!("{}", opts.usage(subcommand_help));
|
||||
if !extra_help.is_empty() {
|
||||
println!("{}", extra_help);
|
||||
}
|
||||
process::exit(exit_code);
|
||||
};
|
||||
|
||||
// We can't use getopt to parse the options until we have completed specifying which
|
||||
// options are valid, but under the current implementation, some options are conditional on
|
||||
// the subcommand. Therefore we must manually identify the subcommand first, so that we can
|
||||
// complete the definition of the options. Then we can use the getopt::Matches object from
|
||||
// there on out.
|
||||
let subcommand = args.iter().find(|&s|
|
||||
let subcommand = args.iter().find(|&s| {
|
||||
(s == "build")
|
||||
|| (s == "check")
|
||||
|| (s == "test")
|
||||
|| (s == "bench")
|
||||
|| (s == "doc")
|
||||
|| (s == "clean")
|
||||
|| (s == "dist")
|
||||
|| (s == "install"));
|
||||
|| (s == "check")
|
||||
|| (s == "test")
|
||||
|| (s == "bench")
|
||||
|| (s == "doc")
|
||||
|| (s == "clean")
|
||||
|| (s == "dist")
|
||||
|| (s == "install")
|
||||
});
|
||||
let subcommand = match subcommand {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
@ -162,7 +173,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
|
||||
// Some subcommands get extra options
|
||||
match subcommand.as_str() {
|
||||
"test" => {
|
||||
"test" => {
|
||||
opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
|
||||
opts.optmulti("", "test-args", "extra arguments", "ARGS");
|
||||
opts.optmulti(
|
||||
@ -173,10 +184,25 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
);
|
||||
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"); },
|
||||
_ => { },
|
||||
opts.optflag(
|
||||
"",
|
||||
"bless",
|
||||
"update all stderr/stdout files of failing ui tests",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"compare-mode",
|
||||
"mode describing what file the actual ui output will be compared to",
|
||||
"COMPARE MODE",
|
||||
);
|
||||
}
|
||||
"bench" => {
|
||||
opts.optmulti("", "test-args", "extra arguments", "ARGS");
|
||||
}
|
||||
"clean" => {
|
||||
opts.optflag("", "all", "clean all build artifacts");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
// Done specifying what options are possible, so do the getopts parsing
|
||||
@ -196,21 +222,24 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
if check_subcommand != subcommand {
|
||||
pass_sanity_check = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
pass_sanity_check = false;
|
||||
}
|
||||
}
|
||||
if !pass_sanity_check {
|
||||
println!("{}\n", subcommand_help);
|
||||
println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
|
||||
You may need to move some options to after the subcommand.\n");
|
||||
println!(
|
||||
"Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
|
||||
You may need to move some options to after the subcommand.\n"
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
// Extra help text for some commands
|
||||
match subcommand.as_str() {
|
||||
"build" => {
|
||||
subcommand_help.push_str("\n
|
||||
subcommand_help.push_str(
|
||||
"\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to compile. For example:
|
||||
@ -232,10 +261,12 @@ Arguments:
|
||||
This will first build everything once (like --stage 0 without further
|
||||
arguments would), and then use the compiler built in stage 0 to build
|
||||
src/libtest and its dependencies.
|
||||
Once this is done, build/$ARCH/stage1 contains a usable compiler.");
|
||||
Once this is done, build/$ARCH/stage1 contains a usable compiler.",
|
||||
);
|
||||
}
|
||||
"check" => {
|
||||
subcommand_help.push_str("\n
|
||||
subcommand_help.push_str(
|
||||
"\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to the crates
|
||||
and/or artifacts to compile. For example:
|
||||
@ -247,10 +278,12 @@ Arguments:
|
||||
also that since we use `cargo check`, by default this will automatically enable incremental
|
||||
compilation, so there's no need to pass it separately, though it won't hurt. We also completely
|
||||
ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
|
||||
the compiler.");
|
||||
the compiler.",
|
||||
);
|
||||
}
|
||||
"test" => {
|
||||
subcommand_help.push_str("\n
|
||||
subcommand_help.push_str(
|
||||
"\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories to tests that
|
||||
should be compiled and run. For example:
|
||||
@ -258,15 +291,19 @@ Arguments:
|
||||
./x.py test src/test/run-pass
|
||||
./x.py test src/libstd --test-args hash_map
|
||||
./x.py test src/libstd --stage 0
|
||||
./x.py test src/test/ui --bless
|
||||
./x.py test src/test/ui --compare-mode nll
|
||||
|
||||
If no arguments are passed then the complete artifacts for that stage are
|
||||
compiled and tested.
|
||||
|
||||
./x.py test
|
||||
./x.py test --stage 1");
|
||||
./x.py test --stage 1",
|
||||
);
|
||||
}
|
||||
"doc" => {
|
||||
subcommand_help.push_str("\n
|
||||
subcommand_help.push_str(
|
||||
"\n
|
||||
Arguments:
|
||||
This subcommand accepts a number of paths to directories of documentation
|
||||
to build. For example:
|
||||
@ -278,12 +315,16 @@ Arguments:
|
||||
If no arguments are passed then everything is documented:
|
||||
|
||||
./x.py doc
|
||||
./x.py doc --stage 1");
|
||||
./x.py doc --stage 1",
|
||||
);
|
||||
}
|
||||
_ => { }
|
||||
_ => {}
|
||||
};
|
||||
// Get any optional paths which occur after the subcommand
|
||||
let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
|
||||
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(|| {
|
||||
if fs::metadata("config.toml").is_ok() {
|
||||
@ -302,9 +343,12 @@ Arguments:
|
||||
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
|
||||
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
|
||||
} else if subcommand.as_str() != "clean" {
|
||||
extra_help.push_str(format!(
|
||||
"Run `./x.py {} -h -v` to see a list of available paths.",
|
||||
subcommand).as_str());
|
||||
extra_help.push_str(
|
||||
format!(
|
||||
"Run `./x.py {} -h -v` to see a list of available paths.",
|
||||
subcommand
|
||||
).as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
// User passed in -h/--help?
|
||||
@ -313,36 +357,28 @@ Arguments:
|
||||
}
|
||||
|
||||
let cmd = match subcommand.as_str() {
|
||||
"build" => {
|
||||
Subcommand::Build { paths: paths }
|
||||
}
|
||||
"check" => {
|
||||
Subcommand::Check { paths: paths }
|
||||
}
|
||||
"test" => {
|
||||
Subcommand::Test {
|
||||
paths,
|
||||
test_args: matches.opt_strs("test-args"),
|
||||
rustc_args: matches.opt_strs("rustc-args"),
|
||||
fail_fast: !matches.opt_present("no-fail-fast"),
|
||||
doc_tests: if matches.opt_present("doc") {
|
||||
DocTests::Only
|
||||
} else if matches.opt_present("no-doc") {
|
||||
DocTests::No
|
||||
} else {
|
||||
DocTests::Yes
|
||||
}
|
||||
}
|
||||
}
|
||||
"bench" => {
|
||||
Subcommand::Bench {
|
||||
paths,
|
||||
test_args: matches.opt_strs("test-args"),
|
||||
}
|
||||
}
|
||||
"doc" => {
|
||||
Subcommand::Doc { paths: paths }
|
||||
}
|
||||
"build" => Subcommand::Build { paths: paths },
|
||||
"check" => Subcommand::Check { paths: paths },
|
||||
"test" => Subcommand::Test {
|
||||
paths,
|
||||
bless: matches.opt_present("bless"),
|
||||
compare_mode: matches.opt_str("compare-mode"),
|
||||
test_args: matches.opt_strs("test-args"),
|
||||
rustc_args: matches.opt_strs("rustc-args"),
|
||||
fail_fast: !matches.opt_present("no-fail-fast"),
|
||||
doc_tests: if matches.opt_present("doc") {
|
||||
DocTests::Only
|
||||
} else if matches.opt_present("no-doc") {
|
||||
DocTests::No
|
||||
} else {
|
||||
DocTests::Yes
|
||||
},
|
||||
},
|
||||
"bench" => Subcommand::Bench {
|
||||
paths,
|
||||
test_args: matches.opt_strs("test-args"),
|
||||
},
|
||||
"doc" => Subcommand::Doc { paths: paths },
|
||||
"clean" => {
|
||||
if paths.len() > 0 {
|
||||
println!("\nclean does not take a path argument\n");
|
||||
@ -353,22 +389,13 @@ Arguments:
|
||||
all: matches.opt_present("all"),
|
||||
}
|
||||
}
|
||||
"dist" => {
|
||||
Subcommand::Dist {
|
||||
paths,
|
||||
}
|
||||
}
|
||||
"install" => {
|
||||
Subcommand::Install {
|
||||
paths,
|
||||
}
|
||||
}
|
||||
"dist" => Subcommand::Dist { paths },
|
||||
"install" => Subcommand::Install { paths },
|
||||
_ => {
|
||||
usage(1, &opts, &subcommand_help, &extra_help);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Flags {
|
||||
verbose: matches.opt_count("verbose"),
|
||||
stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
|
||||
@ -377,15 +404,21 @@ Arguments:
|
||||
rustc_error_format: matches.opt_str("error-format"),
|
||||
keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
|
||||
host: split(matches.opt_strs("host"))
|
||||
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
|
||||
.into_iter()
|
||||
.map(|x| INTERNER.intern_string(x))
|
||||
.collect::<Vec<_>>(),
|
||||
target: split(matches.opt_strs("target"))
|
||||
.into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
|
||||
.into_iter()
|
||||
.map(|x| INTERNER.intern_string(x))
|
||||
.collect::<Vec<_>>(),
|
||||
config: cfg_file,
|
||||
jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
|
||||
cmd,
|
||||
incremental: matches.opt_present("incremental"),
|
||||
exclude: split(matches.opt_strs("exclude"))
|
||||
.into_iter().map(|p| p.into()).collect::<Vec<_>>(),
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.collect::<Vec<_>>(),
|
||||
warnings: matches.opt_str("warnings").map(|v| v == "deny"),
|
||||
}
|
||||
}
|
||||
@ -394,9 +427,11 @@ Arguments:
|
||||
impl Subcommand {
|
||||
pub fn test_args(&self) -> Vec<&str> {
|
||||
match *self {
|
||||
Subcommand::Test { ref test_args, .. } |
|
||||
Subcommand::Bench { ref test_args, .. } => {
|
||||
test_args.iter().flat_map(|s| s.split_whitespace()).collect()
|
||||
Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
|
||||
test_args
|
||||
.iter()
|
||||
.flat_map(|s| s.split_whitespace())
|
||||
.collect()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
@ -404,9 +439,10 @@ impl Subcommand {
|
||||
|
||||
pub fn rustc_args(&self) -> Vec<&str> {
|
||||
match *self {
|
||||
Subcommand::Test { ref rustc_args, .. } => {
|
||||
rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
|
||||
}
|
||||
Subcommand::Test { ref rustc_args, .. } => rustc_args
|
||||
.iter()
|
||||
.flat_map(|s| s.split_whitespace())
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -424,8 +460,27 @@ impl Subcommand {
|
||||
_ => DocTests::Yes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bless(&self) -> bool {
|
||||
match *self {
|
||||
Subcommand::Test { bless, .. } => bless,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_mode(&self) -> Option<&str> {
|
||||
match *self {
|
||||
Subcommand::Test {
|
||||
ref compare_mode, ..
|
||||
} => compare_mode.as_ref().map(|s| &s[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn split(s: Vec<String>) -> Vec<String> {
|
||||
s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
|
||||
s.iter()
|
||||
.flat_map(|s| s.split(','))
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -280,7 +280,8 @@ pub struct Build {
|
||||
struct Crate {
|
||||
name: Interned<String>,
|
||||
version: String,
|
||||
deps: Vec<Interned<String>>,
|
||||
deps: HashSet<Interned<String>>,
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
doc_step: String,
|
||||
build_step: String,
|
||||
@ -307,16 +308,30 @@ impl Crate {
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Mode {
|
||||
/// Build the standard library, placing output in the "stageN-std" directory.
|
||||
Libstd,
|
||||
Std,
|
||||
|
||||
/// Build libtest, placing output in the "stageN-test" directory.
|
||||
Libtest,
|
||||
Test,
|
||||
|
||||
/// Build librustc and compiler libraries, placing output in the "stageN-rustc" directory.
|
||||
Librustc,
|
||||
/// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory.
|
||||
Rustc,
|
||||
|
||||
/// Build some tool, placing output in the "stageN-tools" directory.
|
||||
Tool,
|
||||
/// Build codegen libraries, placing output in the "stageN-codegen" directory
|
||||
Codegen,
|
||||
|
||||
/// Build some tools, placing output in the "stageN-tools" directory.
|
||||
ToolStd,
|
||||
ToolTest,
|
||||
ToolRustc,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn is_tool(&self) -> bool {
|
||||
match self {
|
||||
Mode::ToolStd | Mode::ToolTest | Mode::ToolRustc => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Build {
|
||||
@ -517,10 +532,11 @@ impl Build {
|
||||
/// The mode indicates what the root directory is for.
|
||||
fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
|
||||
let suffix = match mode {
|
||||
Mode::Libstd => "-std",
|
||||
Mode::Libtest => "-test",
|
||||
Mode::Tool => "-tools",
|
||||
Mode::Librustc => "-rustc",
|
||||
Mode::Std => "-std",
|
||||
Mode::Test => "-test",
|
||||
Mode::Codegen => "-rustc",
|
||||
Mode::Rustc => "-rustc",
|
||||
Mode::ToolStd | Mode::ToolTest | Mode::ToolRustc => "-tools",
|
||||
};
|
||||
self.out.join(&*compiler.host)
|
||||
.join(format!("stage{}{}", compiler.stage, suffix))
|
||||
@ -592,12 +608,20 @@ impl Build {
|
||||
Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target))
|
||||
} else {
|
||||
let base = self.llvm_out(self.config.build).join("build");
|
||||
let exe = exe("FileCheck", &*target);
|
||||
if !self.config.ninja && self.config.build.contains("msvc") {
|
||||
base.join("Release/bin").join(exe)
|
||||
let base = if !self.config.ninja && self.config.build.contains("msvc") {
|
||||
if self.config.llvm_optimize {
|
||||
if self.config.llvm_release_debuginfo {
|
||||
base.join("RelWithDebInfo")
|
||||
} else {
|
||||
base.join("Release")
|
||||
}
|
||||
} else {
|
||||
base.join("Debug")
|
||||
}
|
||||
} else {
|
||||
base.join("bin").join(exe)
|
||||
}
|
||||
base
|
||||
};
|
||||
base.join("bin").join(exe("FileCheck", &*target))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use build_helper::output;
|
||||
use serde_json;
|
||||
@ -45,45 +46,17 @@ struct ResolveNode {
|
||||
}
|
||||
|
||||
pub fn build(build: &mut Build) {
|
||||
build_krate(build, "src/libstd");
|
||||
build_krate(build, "src/libtest");
|
||||
build_krate(build, "src/rustc");
|
||||
}
|
||||
let mut resolves = Vec::new();
|
||||
build_krate(&build.std_features(), build, &mut resolves, "src/libstd");
|
||||
build_krate("", build, &mut resolves, "src/libtest");
|
||||
build_krate(&build.rustc_features(), build, &mut resolves, "src/rustc");
|
||||
|
||||
fn build_krate(build: &mut Build, krate: &str) {
|
||||
// Run `cargo metadata` to figure out what crates we're testing.
|
||||
//
|
||||
// Down below we're going to call `cargo test`, but to test the right set
|
||||
// of packages we're going to have to know what `-p` arguments to pass it
|
||||
// to know what crates to test. Here we run `cargo metadata` to learn about
|
||||
// the dependency graph and what `-p` arguments there are.
|
||||
let mut cargo = Command::new(&build.initial_cargo);
|
||||
cargo.arg("metadata")
|
||||
.arg("--format-version").arg("1")
|
||||
.arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml"));
|
||||
let output = output(&mut cargo);
|
||||
let output: Output = serde_json::from_str(&output).unwrap();
|
||||
let mut id2name = HashMap::new();
|
||||
for package in output.packages {
|
||||
if package.source.is_none() {
|
||||
let name = INTERNER.intern_string(package.name);
|
||||
id2name.insert(package.id, name);
|
||||
let mut path = PathBuf::from(package.manifest_path);
|
||||
path.pop();
|
||||
build.crates.insert(name, Crate {
|
||||
build_step: format!("build-crate-{}", name),
|
||||
doc_step: format!("doc-crate-{}", name),
|
||||
test_step: format!("test-crate-{}", name),
|
||||
bench_step: format!("bench-crate-{}", name),
|
||||
name,
|
||||
version: package.version,
|
||||
deps: Vec::new(),
|
||||
path,
|
||||
});
|
||||
}
|
||||
for (name, krate) in build.crates.iter() {
|
||||
id2name.insert(krate.id.clone(), name.clone());
|
||||
}
|
||||
|
||||
for node in output.resolve.nodes {
|
||||
for node in resolves {
|
||||
let name = match id2name.get(&node.id) {
|
||||
Some(name) => name,
|
||||
None => continue,
|
||||
@ -95,7 +68,42 @@ fn build_krate(build: &mut Build, krate: &str) {
|
||||
Some(dep) => dep,
|
||||
None => continue,
|
||||
};
|
||||
krate.deps.push(*dep);
|
||||
krate.deps.insert(*dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec<ResolveNode>, krate: &str) {
|
||||
// Run `cargo metadata` to figure out what crates we're testing.
|
||||
//
|
||||
// Down below we're going to call `cargo test`, but to test the right set
|
||||
// of packages we're going to have to know what `-p` arguments to pass it
|
||||
// to know what crates to test. Here we run `cargo metadata` to learn about
|
||||
// the dependency graph and what `-p` arguments there are.
|
||||
let mut cargo = Command::new(&build.initial_cargo);
|
||||
cargo.arg("metadata")
|
||||
.arg("--format-version").arg("1")
|
||||
.arg("--features").arg(features)
|
||||
.arg("--manifest-path").arg(build.src.join(krate).join("Cargo.toml"));
|
||||
let output = output(&mut cargo);
|
||||
let output: Output = serde_json::from_str(&output).unwrap();
|
||||
for package in output.packages {
|
||||
if package.source.is_none() {
|
||||
let name = INTERNER.intern_string(package.name);
|
||||
let mut path = PathBuf::from(package.manifest_path);
|
||||
path.pop();
|
||||
build.crates.insert(name, Crate {
|
||||
build_step: format!("build-crate-{}", name),
|
||||
doc_step: format!("doc-crate-{}", name),
|
||||
test_step: format!("test-crate-{}", name),
|
||||
bench_step: format!("bench-crate-{}", name),
|
||||
name,
|
||||
version: package.version,
|
||||
id: package.id,
|
||||
deps: HashSet::new(),
|
||||
path,
|
||||
});
|
||||
}
|
||||
}
|
||||
resolves.extend(output.resolve.nodes);
|
||||
}
|
||||
|
||||
@ -275,21 +275,53 @@ fn configure_cmake(builder: &Builder,
|
||||
return
|
||||
}
|
||||
|
||||
let cc = builder.cc(target);
|
||||
let cxx = builder.cxx(target).unwrap();
|
||||
let (cc, cxx) = match builder.config.llvm_clang_cl {
|
||||
Some(ref cl) => (cl.as_ref(), cl.as_ref()),
|
||||
None => (builder.cc(target), builder.cxx(target).unwrap()),
|
||||
};
|
||||
|
||||
// Handle msvc + ninja + ccache specially (this is what the bots use)
|
||||
if target.contains("msvc") &&
|
||||
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");
|
||||
builder.config.ccache.is_some()
|
||||
{
|
||||
let mut wrap_cc = env::current_exe().expect("failed to get cwd");
|
||||
wrap_cc.set_file_name("sccache-plus-cl.exe");
|
||||
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(&cc));
|
||||
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&wrap_cc))
|
||||
.define("CMAKE_CXX_COMPILER", sanitize_cc(&wrap_cc));
|
||||
cfg.env("SCCACHE_PATH",
|
||||
builder.config.ccache.as_ref().unwrap())
|
||||
.env("SCCACHE_TARGET", target);
|
||||
.env("SCCACHE_TARGET", target)
|
||||
.env("SCCACHE_CC", &cc)
|
||||
.env("SCCACHE_CXX", &cxx);
|
||||
|
||||
// Building LLVM on MSVC can be a little ludicrous at times. We're so far
|
||||
// off the beaten path here that I'm not really sure this is even half
|
||||
// supported any more. Here we're trying to:
|
||||
//
|
||||
// * Build LLVM on MSVC
|
||||
// * Build LLVM with `clang-cl` instead of `cl.exe`
|
||||
// * Build a project with `sccache`
|
||||
// * Build for 32-bit as well
|
||||
// * Build with Ninja
|
||||
//
|
||||
// For `cl.exe` there are different binaries to compile 32/64 bit which
|
||||
// we use but for `clang-cl` there's only one which internally
|
||||
// multiplexes via flags. As a result it appears that CMake's detection
|
||||
// of a compiler's architecture and such on MSVC **doesn't** pass any
|
||||
// custom flags we pass in CMAKE_CXX_FLAGS below. This means that if we
|
||||
// use `clang-cl.exe` it's always diagnosed as a 64-bit compiler which
|
||||
// definitely causes problems since all the env vars are pointing to
|
||||
// 32-bit libraries.
|
||||
//
|
||||
// To hack aroudn this... again... we pass an argument that's
|
||||
// unconditionally passed in the sccache shim. This'll get CMake to
|
||||
// correctly diagnose it's doing a 32-bit compilation and LLVM will
|
||||
// internally configure itself appropriately.
|
||||
if builder.config.llvm_clang_cl.is_some() && target.contains("i686") {
|
||||
cfg.env("SCCACHE_EXTRA_ARGS", "-m32");
|
||||
}
|
||||
|
||||
// If ccache is configured we inform the build a little differently hwo
|
||||
// to invoke ccache while also invoking our compilers.
|
||||
@ -368,9 +400,27 @@ impl Step for Lld {
|
||||
let mut cfg = cmake::Config::new(builder.src.join("src/tools/lld"));
|
||||
configure_cmake(builder, target, &mut cfg, true);
|
||||
|
||||
// This is an awful, awful hack. Discovered when we migrated to using
|
||||
// clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
|
||||
// tree, will execute `llvm-config --cmakedir` and then tell CMake about
|
||||
// that directory for later processing. Unfortunately if this path has
|
||||
// forward slashes in it (which it basically always does on Windows)
|
||||
// then CMake will hit a syntax error later on as... something isn't
|
||||
// escaped it seems?
|
||||
//
|
||||
// Instead of attempting to fix this problem in upstream CMake and/or
|
||||
// LLVM/LLD we just hack around it here. This thin wrapper will take the
|
||||
// output from llvm-config and replace all instances of `\` with `/` to
|
||||
// ensure we don't hit the same bugs with escaping. It means that you
|
||||
// can't build on a system where your paths require `\` on Windows, but
|
||||
// there's probably a lot of reasons you can't do that other than this.
|
||||
let llvm_config_shim = env::current_exe()
|
||||
.unwrap()
|
||||
.with_file_name("llvm-config-wrapper");
|
||||
cfg.out_dir(&out_dir)
|
||||
.profile("Release")
|
||||
.define("LLVM_CONFIG_PATH", llvm_config)
|
||||
.env("LLVM_CONFIG_REAL", llvm_config)
|
||||
.define("LLVM_CONFIG_PATH", llvm_config_shim)
|
||||
.define("LLVM_INCLUDE_TESTS", "OFF");
|
||||
|
||||
cfg.build();
|
||||
@ -546,8 +596,10 @@ impl Step for Openssl {
|
||||
"arm-linux-androideabi" => "android",
|
||||
"arm-unknown-linux-gnueabi" => "linux-armv4",
|
||||
"arm-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"armv6-unknown-netbsd-eabihf" => "BSD-generic32",
|
||||
"armv7-linux-androideabi" => "android-armv7",
|
||||
"armv7-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
"armv7-unknown-netbsd-eabihf" => "BSD-generic32",
|
||||
"i586-unknown-linux-gnu" => "linux-elf",
|
||||
"i586-unknown-linux-musl" => "linux-elf",
|
||||
"i686-apple-darwin" => "darwin-i386-cc",
|
||||
|
||||
@ -15,25 +15,26 @@
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::iter;
|
||||
use std::fmt;
|
||||
use std::fs::{self, File};
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::Command;
|
||||
use std::io::Read;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use build_helper::{self, output};
|
||||
|
||||
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
|
||||
use Crate as CargoCrate;
|
||||
use cache::{INTERNER, Interned};
|
||||
use builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
|
||||
use cache::{Interned, INTERNER};
|
||||
use compile;
|
||||
use dist;
|
||||
use flags::Subcommand;
|
||||
use native;
|
||||
use tool::{self, Tool};
|
||||
use util::{self, dylib_path, dylib_path_var};
|
||||
use {Mode, DocTests};
|
||||
use toolstate::ToolState;
|
||||
use util::{self, dylib_path, dylib_path_var};
|
||||
use Crate as CargoCrate;
|
||||
use {DocTests, Mode};
|
||||
|
||||
const ADB_TEST_DIR: &str = "/data/tmp/work";
|
||||
|
||||
@ -46,6 +47,16 @@ pub enum TestKind {
|
||||
Bench,
|
||||
}
|
||||
|
||||
impl From<Kind> for TestKind {
|
||||
fn from(kind: Kind) -> Self {
|
||||
match kind {
|
||||
Kind::Test => TestKind::Test,
|
||||
Kind::Bench => TestKind::Bench,
|
||||
_ => panic!("unexpected kind in crate: {:?}", kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestKind {
|
||||
// Return the cargo subcommand for this test kind
|
||||
fn subcommand(self) -> &'static str {
|
||||
@ -113,13 +124,18 @@ impl Step for Linkcheck {
|
||||
builder.default_doc(None);
|
||||
|
||||
let _time = util::timeit(&builder);
|
||||
try_run(builder, builder.tool_cmd(Tool::Linkchecker)
|
||||
.arg(builder.out.join(host).join("doc")));
|
||||
try_run(
|
||||
builder,
|
||||
builder
|
||||
.tool_cmd(Tool::Linkchecker)
|
||||
.arg(builder.out.join(host).join("doc")),
|
||||
);
|
||||
}
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/linkchecker").default_condition(builder.config.docs)
|
||||
run.path("src/tools/linkchecker")
|
||||
.default_condition(builder.config.docs)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -154,7 +170,10 @@ impl Step for Cargotest {
|
||||
/// test` to ensure that we don't regress the test suites there.
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = builder.compiler(self.stage, self.host);
|
||||
builder.ensure(compile::Rustc { compiler, target: compiler.host });
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler,
|
||||
target: compiler.host,
|
||||
});
|
||||
|
||||
// Note that this is a short, cryptic, and not scoped directory name. This
|
||||
// is currently to minimize the length of path on Windows where we otherwise
|
||||
@ -164,10 +183,13 @@ impl Step for Cargotest {
|
||||
|
||||
let _time = util::timeit(&builder);
|
||||
let mut cmd = builder.tool_cmd(Tool::CargoTest);
|
||||
try_run(builder, cmd.arg(&builder.initial_cargo)
|
||||
.arg(&out_dir)
|
||||
.env("RUSTC", builder.rustc(compiler))
|
||||
.env("RUSTDOC", builder.rustdoc(compiler.host)));
|
||||
try_run(
|
||||
builder,
|
||||
cmd.arg(&builder.initial_cargo)
|
||||
.arg(&out_dir)
|
||||
.env("RUSTC", builder.rustc(compiler))
|
||||
.env("RUSTDOC", builder.rustdoc(compiler.host)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,9 +218,14 @@ impl Step for Cargo {
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = builder.compiler(self.stage, self.host);
|
||||
|
||||
builder.ensure(tool::Cargo { compiler, target: self.host });
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, self.host, "test");
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/tools/cargo/Cargo.toml"));
|
||||
builder.ensure(tool::Cargo {
|
||||
compiler,
|
||||
target: self.host,
|
||||
});
|
||||
let mut cargo = builder.cargo(compiler, Mode::ToolRustc, self.host, "test");
|
||||
cargo
|
||||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/tools/cargo/Cargo.toml"));
|
||||
if !builder.fail_fast {
|
||||
cargo.arg("--no-fail-fast");
|
||||
}
|
||||
@ -210,7 +237,10 @@ impl Step for Cargo {
|
||||
// available.
|
||||
cargo.env("CFG_DISABLE_CROSS_TESTS", "1");
|
||||
|
||||
try_run(builder, cargo.env("PATH", &path_for_cargo(builder, compiler)));
|
||||
try_run(
|
||||
builder,
|
||||
cargo.env("PATH", &path_for_cargo(builder, compiler)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +283,7 @@ impl Step for Rls {
|
||||
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
Mode::ToolRustc,
|
||||
host,
|
||||
"test",
|
||||
"src/tools/rls");
|
||||
@ -307,6 +338,7 @@ impl Step for Rustfmt {
|
||||
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
Mode::ToolRustc,
|
||||
host,
|
||||
"test",
|
||||
"src/tools/rustfmt");
|
||||
@ -360,8 +392,10 @@ impl Step for Miri {
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
if let Some(miri) = miri {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/tools/miri/Cargo.toml"));
|
||||
let mut cargo = builder.cargo(compiler, Mode::ToolRustc, host, "test");
|
||||
cargo
|
||||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/tools/miri/Cargo.toml"));
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
@ -416,8 +450,10 @@ impl Step for Clippy {
|
||||
extra_features: Vec::new(),
|
||||
});
|
||||
if let Some(clippy) = clippy {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("src/tools/clippy/Cargo.toml"));
|
||||
let mut cargo = builder.cargo(compiler, Mode::ToolRustc, host, "test");
|
||||
cargo
|
||||
.arg("--manifest-path")
|
||||
.arg(builder.src.join("src/tools/clippy/Cargo.toml"));
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
@ -425,7 +461,9 @@ impl Step for Clippy {
|
||||
cargo.env("SYSROOT", builder.sysroot(compiler));
|
||||
cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler));
|
||||
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
|
||||
let host_libs = builder.stage_out(compiler, Mode::Tool).join(builder.cargo_dir());
|
||||
let host_libs = builder
|
||||
.stage_out(compiler, Mode::ToolRustc)
|
||||
.join(builder.cargo_dir());
|
||||
cargo.env("HOST_LIBS", host_libs);
|
||||
// clippy tests need to find the driver
|
||||
cargo.env("CLIPPY_DRIVER_PATH", clippy);
|
||||
@ -467,23 +505,30 @@ impl Step for RustdocTheme {
|
||||
fn make_run(run: RunConfig) {
|
||||
let compiler = run.builder.compiler(run.builder.top_stage, run.host);
|
||||
|
||||
run.builder.ensure(RustdocTheme {
|
||||
compiler: compiler,
|
||||
});
|
||||
run.builder.ensure(RustdocTheme { compiler: compiler });
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let rustdoc = builder.out.join("bootstrap/debug/rustdoc");
|
||||
let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
|
||||
cmd.arg(rustdoc.to_str().unwrap())
|
||||
.arg(builder.src.join("src/librustdoc/html/static/themes").to_str().unwrap())
|
||||
.env("RUSTC_STAGE", self.compiler.stage.to_string())
|
||||
.env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
|
||||
.env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host))
|
||||
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
|
||||
.env("RUSTDOC_REAL", builder.rustdoc(self.compiler.host))
|
||||
.env("RUSTDOC_CRATE_VERSION", builder.rust_version())
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
.arg(
|
||||
builder
|
||||
.src
|
||||
.join("src/librustdoc/html/static/themes")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)
|
||||
.env("RUSTC_STAGE", self.compiler.stage.to_string())
|
||||
.env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
|
||||
.env(
|
||||
"RUSTDOC_LIBDIR",
|
||||
builder.sysroot_libdir(self.compiler, self.compiler.host),
|
||||
)
|
||||
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
|
||||
.env("RUSTDOC_REAL", builder.rustdoc(self.compiler.host))
|
||||
.env("RUSTDOC_CRATE_VERSION", builder.rust_version())
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
if let Some(linker) = builder.linker(self.compiler.host) {
|
||||
cmd.env("RUSTC_TARGET_LINKER", linker);
|
||||
}
|
||||
@ -523,7 +568,9 @@ impl Step for RustdocJS {
|
||||
});
|
||||
builder.run(&mut command);
|
||||
} else {
|
||||
builder.info(&format!("No nodejs found, skipping \"src/test/rustdoc-js\" tests"));
|
||||
builder.info(&format!(
|
||||
"No nodejs found, skipping \"src/test/rustdoc-js\" tests"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -559,6 +606,7 @@ impl Step for RustdocUi {
|
||||
target: self.target,
|
||||
mode: "ui",
|
||||
suite: "rustdoc-ui",
|
||||
path: None,
|
||||
compare_mode: None,
|
||||
})
|
||||
}
|
||||
@ -584,7 +632,7 @@ impl Step for Tidy {
|
||||
if !builder.config.vendor {
|
||||
cmd.arg("--no-vendor");
|
||||
}
|
||||
if builder.config.quiet_tests {
|
||||
if !builder.config.verbose_tests {
|
||||
cmd.arg("--quiet");
|
||||
}
|
||||
|
||||
@ -663,7 +711,7 @@ macro_rules! test_definitions {
|
||||
const ONLY_HOSTS: bool = $host;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path($path)
|
||||
run.suite_path($path)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
@ -681,6 +729,7 @@ macro_rules! test_definitions {
|
||||
target: self.target,
|
||||
mode: $mode,
|
||||
suite: $suite,
|
||||
path: Some($path),
|
||||
compare_mode: $compare_mode,
|
||||
})
|
||||
}
|
||||
@ -835,7 +884,7 @@ test!(RunFailFullDepsPretty {
|
||||
host: true
|
||||
});
|
||||
|
||||
host_test!(RunMake {
|
||||
default_test!(RunMake {
|
||||
path: "src/test/run-make",
|
||||
mode: "run-make",
|
||||
suite: "run-make"
|
||||
@ -853,6 +902,7 @@ struct Compiletest {
|
||||
target: Interned<String>,
|
||||
mode: &'static str,
|
||||
suite: &'static str,
|
||||
path: Option<&'static str>,
|
||||
compare_mode: Option<&'static str>,
|
||||
}
|
||||
|
||||
@ -873,7 +923,9 @@ impl Step for Compiletest {
|
||||
let target = self.target;
|
||||
let mode = self.mode;
|
||||
let suite = self.suite;
|
||||
let compare_mode = self.compare_mode;
|
||||
|
||||
// Path for test suite
|
||||
let suite_path = self.path.unwrap_or("");
|
||||
|
||||
// Skip codegen tests if they aren't enabled in configuration.
|
||||
if !builder.config.codegen_tests && suite == "codegen" {
|
||||
@ -902,15 +954,15 @@ impl Step for Compiletest {
|
||||
|
||||
builder.ensure(dist::DebuggerScripts {
|
||||
sysroot: builder.sysroot(compiler),
|
||||
host: target
|
||||
host: target,
|
||||
});
|
||||
}
|
||||
|
||||
if suite.ends_with("fulldeps") ||
|
||||
// FIXME: Does pretty need librustc compiled? Note that there are
|
||||
// fulldeps test suites with mode = pretty as well.
|
||||
mode == "pretty" ||
|
||||
mode == "rustdoc" {
|
||||
mode == "pretty"
|
||||
{
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
}
|
||||
|
||||
@ -923,26 +975,40 @@ impl Step for Compiletest {
|
||||
// compiletest currently has... a lot of arguments, so let's just pass all
|
||||
// of them!
|
||||
|
||||
cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler));
|
||||
cmd.arg("--run-lib-path").arg(builder.sysroot_libdir(compiler, target));
|
||||
cmd.arg("--compile-lib-path")
|
||||
.arg(builder.rustc_libdir(compiler));
|
||||
cmd.arg("--run-lib-path")
|
||||
.arg(builder.sysroot_libdir(compiler, target));
|
||||
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
|
||||
|
||||
let is_rustdoc_ui = suite.ends_with("rustdoc-ui");
|
||||
|
||||
// Avoid depending on rustdoc when we don't need it.
|
||||
if mode == "rustdoc" ||
|
||||
(mode == "run-make" && suite.ends_with("fulldeps")) ||
|
||||
(mode == "ui" && is_rustdoc_ui) {
|
||||
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
|
||||
if mode == "rustdoc"
|
||||
|| (mode == "run-make" && suite.ends_with("fulldeps"))
|
||||
|| (mode == "ui" && is_rustdoc_ui)
|
||||
{
|
||||
cmd.arg("--rustdoc-path")
|
||||
.arg(builder.rustdoc(compiler.host));
|
||||
}
|
||||
|
||||
cmd.arg("--src-base").arg(builder.src.join("src/test").join(suite));
|
||||
cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
|
||||
cmd.arg("--stage-id").arg(format!("stage{}-{}", compiler.stage, target));
|
||||
cmd.arg("--src-base")
|
||||
.arg(builder.src.join("src/test").join(suite));
|
||||
cmd.arg("--build-base")
|
||||
.arg(testdir(builder, compiler.host).join(suite));
|
||||
cmd.arg("--stage-id")
|
||||
.arg(format!("stage{}-{}", compiler.stage, target));
|
||||
cmd.arg("--mode").arg(mode);
|
||||
cmd.arg("--target").arg(target);
|
||||
cmd.arg("--host").arg(&*compiler.host);
|
||||
cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build));
|
||||
cmd.arg("--llvm-filecheck")
|
||||
.arg(builder.llvm_filecheck(builder.config.build));
|
||||
|
||||
if builder.config.cmd.bless() {
|
||||
cmd.arg("--bless");
|
||||
}
|
||||
|
||||
let compare_mode = builder.config.cmd.compare_mode().or(self.compare_mode);
|
||||
|
||||
if let Some(ref nodejs) = builder.config.nodejs {
|
||||
cmd.arg("--nodejs").arg(nodejs);
|
||||
@ -972,8 +1038,10 @@ impl Step for Compiletest {
|
||||
cmd.arg("--host-rustcflags").arg(hostflags.join(" "));
|
||||
|
||||
let mut targetflags = flags.clone();
|
||||
targetflags.push(format!("-Lnative={}",
|
||||
builder.test_helpers_out(target).display()));
|
||||
targetflags.push(format!(
|
||||
"-Lnative={}",
|
||||
builder.test_helpers_out(target).display()
|
||||
));
|
||||
cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
|
||||
|
||||
cmd.arg("--docck-python").arg(builder.python());
|
||||
@ -997,13 +1065,28 @@ impl Step for Compiletest {
|
||||
cmd.arg("--lldb-python-dir").arg(dir);
|
||||
}
|
||||
|
||||
cmd.args(&builder.config.cmd.test_args());
|
||||
// Get paths from cmd args
|
||||
let paths = match &builder.config.cmd {
|
||||
Subcommand::Test { ref paths, .. } => &paths[..],
|
||||
_ => &[],
|
||||
};
|
||||
|
||||
// Get test-args by striping suite path
|
||||
let mut test_args: Vec<&str> = paths
|
||||
.iter()
|
||||
.filter(|p| p.starts_with(suite_path) && p.is_file())
|
||||
.map(|p| p.strip_prefix(suite_path).unwrap().to_str().unwrap())
|
||||
.collect();
|
||||
|
||||
test_args.append(&mut builder.config.cmd.test_args());
|
||||
|
||||
cmd.args(&test_args);
|
||||
|
||||
if builder.is_verbose() {
|
||||
cmd.arg("--verbose");
|
||||
}
|
||||
|
||||
if builder.config.quiet_tests {
|
||||
if !builder.config.verbose_tests {
|
||||
cmd.arg("--quiet");
|
||||
}
|
||||
|
||||
@ -1022,35 +1105,47 @@ impl Step for Compiletest {
|
||||
|
||||
// Only pass correct values for these flags for the `run-make` suite as it
|
||||
// requires that a C++ compiler was configured which isn't always the case.
|
||||
if !builder.config.dry_run && mode == "run-make" {
|
||||
if !builder.config.dry_run && suite == "run-make-fulldeps" {
|
||||
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
|
||||
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
|
||||
cmd.arg("--cc").arg(builder.cc(target))
|
||||
.arg("--cxx").arg(builder.cxx(target).unwrap())
|
||||
.arg("--cflags").arg(builder.cflags(target).join(" "))
|
||||
.arg("--llvm-components").arg(llvm_components.trim())
|
||||
.arg("--llvm-cxxflags").arg(llvm_cxxflags.trim());
|
||||
cmd.arg("--cc")
|
||||
.arg(builder.cc(target))
|
||||
.arg("--cxx")
|
||||
.arg(builder.cxx(target).unwrap())
|
||||
.arg("--cflags")
|
||||
.arg(builder.cflags(target).join(" "))
|
||||
.arg("--llvm-components")
|
||||
.arg(llvm_components.trim())
|
||||
.arg("--llvm-cxxflags")
|
||||
.arg(llvm_cxxflags.trim());
|
||||
if let Some(ar) = builder.ar(target) {
|
||||
cmd.arg("--ar").arg(ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
if mode == "run-make" && !builder.config.llvm_enabled {
|
||||
builder.info(
|
||||
&format!("Ignoring run-make test suite as they generally don't work without LLVM"));
|
||||
if suite == "run-make-fulldeps" && !builder.config.llvm_enabled {
|
||||
builder.info(&format!(
|
||||
"Ignoring run-make test suite as they generally don't work without LLVM"
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if mode != "run-make" {
|
||||
cmd.arg("--cc").arg("")
|
||||
.arg("--cxx").arg("")
|
||||
.arg("--cflags").arg("")
|
||||
.arg("--llvm-components").arg("")
|
||||
.arg("--llvm-cxxflags").arg("");
|
||||
if suite != "run-make-fulldeps" {
|
||||
cmd.arg("--cc")
|
||||
.arg("")
|
||||
.arg("--cxx")
|
||||
.arg("")
|
||||
.arg("--cflags")
|
||||
.arg("")
|
||||
.arg("--llvm-components")
|
||||
.arg("")
|
||||
.arg("--llvm-cxxflags")
|
||||
.arg("");
|
||||
}
|
||||
|
||||
if builder.remote_tested(target) {
|
||||
cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
|
||||
cmd.arg("--remote-test-client")
|
||||
.arg(builder.tool_exe(Tool::RemoteTestClient));
|
||||
}
|
||||
|
||||
// Running a C compiler on MSVC requires a few env vars to be set, to be
|
||||
@ -1083,7 +1178,7 @@ impl Step for Compiletest {
|
||||
if target.contains("android") {
|
||||
// Assume that cc for this target comes from the android sysroot
|
||||
cmd.arg("--android-cross-path")
|
||||
.arg(builder.cc(target).parent().unwrap().parent().unwrap());
|
||||
.arg(builder.cc(target).parent().unwrap().parent().unwrap());
|
||||
} else {
|
||||
cmd.arg("--android-cross-path").arg("");
|
||||
}
|
||||
@ -1091,16 +1186,20 @@ impl Step for Compiletest {
|
||||
builder.ci_env.force_coloring_in_ci(&mut cmd);
|
||||
|
||||
let _folder = builder.fold_output(|| format!("test_{}", suite));
|
||||
builder.info(&format!("Check compiletest suite={} mode={} ({} -> {})",
|
||||
suite, mode, &compiler.host, target));
|
||||
builder.info(&format!(
|
||||
"Check compiletest suite={} mode={} ({} -> {})",
|
||||
suite, mode, &compiler.host, target
|
||||
));
|
||||
let _time = util::timeit(&builder);
|
||||
try_run(builder, &mut cmd);
|
||||
|
||||
if let Some(compare_mode) = compare_mode {
|
||||
cmd.arg("--compare-mode").arg(compare_mode);
|
||||
let _folder = builder.fold_output(|| format!("test_{}_{}", suite, compare_mode));
|
||||
builder.info(&format!("Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
|
||||
suite, mode, compare_mode, &compiler.host, target));
|
||||
builder.info(&format!(
|
||||
"Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
|
||||
suite, mode, compare_mode, &compiler.host, target
|
||||
));
|
||||
let _time = util::timeit(&builder);
|
||||
try_run(builder, &mut cmd);
|
||||
}
|
||||
@ -1131,7 +1230,10 @@ impl Step for DocTest {
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = self.compiler;
|
||||
|
||||
builder.ensure(compile::Test { compiler, target: compiler.host });
|
||||
builder.ensure(compile::Test {
|
||||
compiler,
|
||||
target: compiler.host,
|
||||
});
|
||||
|
||||
// Do a breadth-first traversal of the `src/doc` directory and just run
|
||||
// tests for all files that end in `*.md`
|
||||
@ -1143,7 +1245,7 @@ impl Step for DocTest {
|
||||
while let Some(p) = stack.pop() {
|
||||
if p.is_dir() {
|
||||
stack.extend(t!(p.read_dir()).map(|p| t!(p).path()));
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if p.extension().and_then(|s| s.to_str()) != Some("md") {
|
||||
@ -1250,7 +1352,10 @@ impl Step for ErrorIndex {
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = self.compiler;
|
||||
|
||||
builder.ensure(compile::Std { compiler, target: compiler.host });
|
||||
builder.ensure(compile::Std {
|
||||
compiler,
|
||||
target: compiler.host,
|
||||
});
|
||||
|
||||
let dir = testdir(builder, compiler.host);
|
||||
t!(fs::create_dir_all(&dir));
|
||||
@ -1262,7 +1367,6 @@ impl Step for ErrorIndex {
|
||||
.env("CFG_BUILD", &builder.config.build)
|
||||
.env("RUSTC_ERROR_METADATA_DST", builder.extended_error_dir());
|
||||
|
||||
|
||||
let _folder = builder.fold_output(|| "test_error_index");
|
||||
builder.info(&format!("Testing error-index stage{}", compiler.stage));
|
||||
let _time = util::timeit(&builder);
|
||||
@ -1280,7 +1384,7 @@ fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) -> bool
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Err(_) => {},
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
builder.info(&format!("doc tests for: {}", markdown.display()));
|
||||
@ -1293,10 +1397,10 @@ fn markdown_test(builder: &Builder, compiler: Compiler, markdown: &Path) -> bool
|
||||
let test_args = builder.config.cmd.test_args().join(" ");
|
||||
cmd.arg("--test-args").arg(test_args);
|
||||
|
||||
if builder.config.quiet_tests {
|
||||
try_run_quiet(builder, &mut cmd)
|
||||
} else {
|
||||
if builder.config.verbose_tests {
|
||||
try_run(builder, &mut cmd)
|
||||
} else {
|
||||
try_run_quiet(builder, &mut cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1323,13 +1427,7 @@ impl Step for CrateLibrustc {
|
||||
|
||||
for krate in builder.in_tree_crates("rustc-main") {
|
||||
if run.path.ends_with(&krate.path) {
|
||||
let test_kind = if builder.kind == Kind::Test {
|
||||
TestKind::Test
|
||||
} else if builder.kind == Kind::Bench {
|
||||
TestKind::Bench
|
||||
} else {
|
||||
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
|
||||
};
|
||||
let test_kind = builder.kind.into();
|
||||
|
||||
builder.ensure(CrateLibrustc {
|
||||
compiler,
|
||||
@ -1345,7 +1443,7 @@ impl Step for CrateLibrustc {
|
||||
builder.ensure(Crate {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
mode: Mode::Librustc,
|
||||
mode: Mode::Rustc,
|
||||
test_kind: self.test_kind,
|
||||
krate: self.krate,
|
||||
});
|
||||
@ -1375,13 +1473,7 @@ impl Step for CrateNotDefault {
|
||||
let builder = run.builder;
|
||||
let compiler = builder.compiler(builder.top_stage, run.host);
|
||||
|
||||
let test_kind = if builder.kind == Kind::Test {
|
||||
TestKind::Test
|
||||
} else if builder.kind == Kind::Bench {
|
||||
TestKind::Bench
|
||||
} else {
|
||||
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
|
||||
};
|
||||
let test_kind = builder.kind.into();
|
||||
|
||||
builder.ensure(CrateNotDefault {
|
||||
compiler,
|
||||
@ -1402,14 +1494,13 @@ impl Step for CrateNotDefault {
|
||||
builder.ensure(Crate {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
mode: Mode::Libstd,
|
||||
mode: Mode::Std,
|
||||
test_kind: self.test_kind,
|
||||
krate: INTERNER.intern_str(self.krate),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Crate {
|
||||
pub compiler: Compiler,
|
||||
@ -1427,10 +1518,11 @@ impl Step for Crate {
|
||||
let builder = run.builder;
|
||||
run = run.krate("test");
|
||||
for krate in run.builder.in_tree_crates("std") {
|
||||
if krate.is_local(&run.builder) &&
|
||||
!krate.name.contains("jemalloc") &&
|
||||
!(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) &&
|
||||
krate.name != "dlmalloc" {
|
||||
if krate.is_local(&run.builder)
|
||||
&& !krate.name.contains("jemalloc")
|
||||
&& !(krate.name.starts_with("rustc_") && krate.name.ends_with("san"))
|
||||
&& krate.name != "dlmalloc"
|
||||
{
|
||||
run = run.path(krate.local_path(&builder).to_str().unwrap());
|
||||
}
|
||||
}
|
||||
@ -1442,13 +1534,7 @@ impl Step for Crate {
|
||||
let compiler = builder.compiler(builder.top_stage, run.host);
|
||||
|
||||
let make = |mode: Mode, krate: &CargoCrate| {
|
||||
let test_kind = if builder.kind == Kind::Test {
|
||||
TestKind::Test
|
||||
} else if builder.kind == Kind::Bench {
|
||||
TestKind::Bench
|
||||
} else {
|
||||
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
|
||||
};
|
||||
let test_kind = builder.kind.into();
|
||||
|
||||
builder.ensure(Crate {
|
||||
compiler,
|
||||
@ -1461,12 +1547,12 @@ impl Step for Crate {
|
||||
|
||||
for krate in builder.in_tree_crates("std") {
|
||||
if run.path.ends_with(&krate.local_path(&builder)) {
|
||||
make(Mode::Libstd, krate);
|
||||
make(Mode::Std, krate);
|
||||
}
|
||||
}
|
||||
for krate in builder.in_tree_crates("test") {
|
||||
if run.path.ends_with(&krate.local_path(&builder)) {
|
||||
make(Mode::Libtest, krate);
|
||||
make(Mode::Test, krate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1501,13 +1587,13 @@ impl Step for Crate {
|
||||
|
||||
let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand());
|
||||
match mode {
|
||||
Mode::Libstd => {
|
||||
Mode::Std => {
|
||||
compile::std_cargo(builder, &compiler, target, &mut cargo);
|
||||
}
|
||||
Mode::Libtest => {
|
||||
Mode::Test => {
|
||||
compile::test_cargo(builder, &compiler, target, &mut cargo);
|
||||
}
|
||||
Mode::Librustc => {
|
||||
Mode::Rustc => {
|
||||
builder.ensure(compile::Rustc { compiler, target });
|
||||
compile::rustc_cargo(builder, &mut cargo);
|
||||
}
|
||||
@ -1546,43 +1632,64 @@ impl Step for Crate {
|
||||
cargo.arg("--");
|
||||
cargo.args(&builder.config.cmd.test_args());
|
||||
|
||||
if builder.config.quiet_tests {
|
||||
if !builder.config.verbose_tests {
|
||||
cargo.arg("--quiet");
|
||||
}
|
||||
|
||||
if target.contains("emscripten") {
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
builder.config.nodejs.as_ref().expect("nodejs not configured"));
|
||||
cargo.env(
|
||||
format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
builder
|
||||
.config
|
||||
.nodejs
|
||||
.as_ref()
|
||||
.expect("nodejs not configured"),
|
||||
);
|
||||
} else if target.starts_with("wasm32") {
|
||||
// Warn about running tests without the `wasm_syscall` feature enabled.
|
||||
// The javascript shim implements the syscall interface so that test
|
||||
// output can be correctly reported.
|
||||
if !builder.config.wasm_syscall {
|
||||
builder.info(&format!("Libstd was built without `wasm_syscall` feature enabled: \
|
||||
test output may not be visible."));
|
||||
builder.info(&format!(
|
||||
"Libstd was built without `wasm_syscall` feature enabled: \
|
||||
test output may not be visible."
|
||||
));
|
||||
}
|
||||
|
||||
// On the wasm32-unknown-unknown target we're using LTO which is
|
||||
// incompatible with `-C prefer-dynamic`, so disable that here
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
|
||||
let node = builder.config.nodejs.as_ref()
|
||||
let node = builder
|
||||
.config
|
||||
.nodejs
|
||||
.as_ref()
|
||||
.expect("nodejs not configured");
|
||||
let runner = format!("{} {}/src/etc/wasm32-shim.js",
|
||||
node.display(),
|
||||
builder.src.display());
|
||||
let runner = format!(
|
||||
"{} {}/src/etc/wasm32-shim.js",
|
||||
node.display(),
|
||||
builder.src.display()
|
||||
);
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)), &runner);
|
||||
} else if builder.remote_tested(target) {
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
format!("{} run",
|
||||
builder.tool_exe(Tool::RemoteTestClient).display()));
|
||||
cargo.env(
|
||||
format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
format!("{} run", builder.tool_exe(Tool::RemoteTestClient).display()),
|
||||
);
|
||||
}
|
||||
|
||||
let _folder = builder.fold_output(|| {
|
||||
format!("{}_stage{}-{}", test_kind.subcommand(), compiler.stage, krate)
|
||||
format!(
|
||||
"{}_stage{}-{}",
|
||||
test_kind.subcommand(),
|
||||
compiler.stage,
|
||||
krate
|
||||
)
|
||||
});
|
||||
builder.info(&format!("{} {} stage{} ({} -> {})", test_kind, krate, compiler.stage,
|
||||
&compiler.host, target));
|
||||
builder.info(&format!(
|
||||
"{} {} stage{} ({} -> {})",
|
||||
test_kind, krate, compiler.stage, &compiler.host, target
|
||||
));
|
||||
let _time = util::timeit(&builder);
|
||||
try_run(builder, &mut cargo);
|
||||
}
|
||||
@ -1606,13 +1713,7 @@ impl Step for CrateRustdoc {
|
||||
fn make_run(run: RunConfig) {
|
||||
let builder = run.builder;
|
||||
|
||||
let test_kind = if builder.kind == Kind::Test {
|
||||
TestKind::Test
|
||||
} else if builder.kind == Kind::Bench {
|
||||
TestKind::Bench
|
||||
} else {
|
||||
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
|
||||
};
|
||||
let test_kind = builder.kind.into();
|
||||
|
||||
builder.ensure(CrateRustdoc {
|
||||
host: run.host,
|
||||
@ -1628,6 +1729,7 @@ impl Step for CrateRustdoc {
|
||||
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
Mode::ToolRustc,
|
||||
target,
|
||||
test_kind.subcommand(),
|
||||
"src/tools/rustdoc");
|
||||
@ -1640,15 +1742,16 @@ impl Step for CrateRustdoc {
|
||||
cargo.arg("--");
|
||||
cargo.args(&builder.config.cmd.test_args());
|
||||
|
||||
if builder.config.quiet_tests {
|
||||
if !builder.config.verbose_tests {
|
||||
cargo.arg("--quiet");
|
||||
}
|
||||
|
||||
let _folder = builder.fold_output(|| {
|
||||
format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage)
|
||||
});
|
||||
builder.info(&format!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage,
|
||||
&compiler.host, target));
|
||||
let _folder = builder
|
||||
.fold_output(|| format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage));
|
||||
builder.info(&format!(
|
||||
"{} rustdoc stage{} ({} -> {})",
|
||||
test_kind, compiler.stage, &compiler.host, target
|
||||
));
|
||||
let _time = util::timeit(&builder);
|
||||
|
||||
try_run(builder, &mut cargo);
|
||||
@ -1656,12 +1759,13 @@ impl Step for CrateRustdoc {
|
||||
}
|
||||
|
||||
fn envify(s: &str) -> String {
|
||||
s.chars().map(|c| {
|
||||
match c {
|
||||
s.chars()
|
||||
.map(|c| match c {
|
||||
'-' => '_',
|
||||
c => c,
|
||||
}
|
||||
}).flat_map(|c| c.to_uppercase()).collect()
|
||||
})
|
||||
.flat_map(|c| c.to_uppercase())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Some test suites are run inside emulators or on remote devices, and most
|
||||
@ -1690,7 +1794,7 @@ impl Step for RemoteCopyLibs {
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
if !builder.remote_tested(target) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
builder.ensure(compile::Test { compiler, target });
|
||||
@ -1704,9 +1808,9 @@ impl Step for RemoteCopyLibs {
|
||||
let tool = builder.tool_exe(Tool::RemoteTestClient);
|
||||
let mut cmd = Command::new(&tool);
|
||||
cmd.arg("spawn-emulator")
|
||||
.arg(target)
|
||||
.arg(&server)
|
||||
.arg(builder.out.join("tmp"));
|
||||
.arg(target)
|
||||
.arg(&server)
|
||||
.arg(builder.out.join("tmp"));
|
||||
if let Some(rootfs) = builder.qemu_rootfs(target) {
|
||||
cmd.arg(rootfs);
|
||||
}
|
||||
@ -1717,9 +1821,7 @@ impl Step for RemoteCopyLibs {
|
||||
let f = t!(f);
|
||||
let name = f.file_name().into_string().unwrap();
|
||||
if util::is_dylib(&name) {
|
||||
builder.run(Command::new(&tool)
|
||||
.arg("push")
|
||||
.arg(f.path()));
|
||||
builder.run(Command::new(&tool).arg("push").arg(f.path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1752,17 +1854,21 @@ impl Step for Distcheck {
|
||||
|
||||
let mut cmd = Command::new("tar");
|
||||
cmd.arg("-xzf")
|
||||
.arg(builder.ensure(dist::PlainSourceTarball))
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&dir);
|
||||
.arg(builder.ensure(dist::PlainSourceTarball))
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&dir);
|
||||
builder.run(&mut cmd);
|
||||
builder.run(Command::new("./configure")
|
||||
.args(&builder.config.configure_args)
|
||||
.arg("--enable-vendor")
|
||||
.current_dir(&dir));
|
||||
builder.run(Command::new(build_helper::make(&builder.config.build))
|
||||
.arg("check")
|
||||
.current_dir(&dir));
|
||||
builder.run(
|
||||
Command::new("./configure")
|
||||
.args(&builder.config.configure_args)
|
||||
.arg("--enable-vendor")
|
||||
.current_dir(&dir),
|
||||
);
|
||||
builder.run(
|
||||
Command::new(build_helper::make(&builder.config.build))
|
||||
.arg("check")
|
||||
.current_dir(&dir),
|
||||
);
|
||||
|
||||
// Now make sure that rust-src has all of libstd's dependencies
|
||||
builder.info(&format!("Distcheck rust-src"));
|
||||
@ -1772,17 +1878,19 @@ impl Step for Distcheck {
|
||||
|
||||
let mut cmd = Command::new("tar");
|
||||
cmd.arg("-xzf")
|
||||
.arg(builder.ensure(dist::Src))
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&dir);
|
||||
.arg(builder.ensure(dist::Src))
|
||||
.arg("--strip-components=1")
|
||||
.current_dir(&dir);
|
||||
builder.run(&mut cmd);
|
||||
|
||||
let toml = dir.join("rust-src/lib/rustlib/src/rust/src/libstd/Cargo.toml");
|
||||
builder.run(Command::new(&builder.initial_cargo)
|
||||
.arg("generate-lockfile")
|
||||
.arg("--manifest-path")
|
||||
.arg(&toml)
|
||||
.current_dir(&dir));
|
||||
builder.run(
|
||||
Command::new(&builder.initial_cargo)
|
||||
.arg("generate-lockfile")
|
||||
.arg("--manifest-path")
|
||||
.arg(&toml)
|
||||
.current_dir(&dir),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1798,11 +1906,11 @@ impl Step for Bootstrap {
|
||||
fn run(self, builder: &Builder) {
|
||||
let mut cmd = Command::new(&builder.initial_cargo);
|
||||
cmd.arg("test")
|
||||
.current_dir(builder.src.join("src/bootstrap"))
|
||||
.env("RUSTFLAGS", "-Cdebuginfo=2")
|
||||
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
|
||||
.env("RUSTC_BOOTSTRAP", "1")
|
||||
.env("RUSTC", &builder.initial_rustc);
|
||||
.current_dir(builder.src.join("src/bootstrap"))
|
||||
.env("RUSTFLAGS", "-Cdebuginfo=2")
|
||||
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
|
||||
.env("RUSTC_BOOTSTRAP", "1")
|
||||
.env("RUSTC", &builder.initial_rustc);
|
||||
if let Some(flags) = option_env!("RUSTFLAGS") {
|
||||
// Use the same rustc flags for testing as for "normal" compilation,
|
||||
// so that Cargo doesn’t recompile the entire dependency graph every time:
|
||||
@ -1813,6 +1921,9 @@ impl Step for Bootstrap {
|
||||
cmd.arg("--no-fail-fast");
|
||||
}
|
||||
cmd.arg("--").args(&builder.config.cmd.test_args());
|
||||
// rustbuild tests are racy on directory creation so just run them one at a time.
|
||||
// Since there's not many this shouldn't be a problem.
|
||||
cmd.arg("--test-threads=1");
|
||||
try_run(builder, &mut cmd);
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ use toolstate::ToolState;
|
||||
pub struct CleanTools {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
pub mode: Mode,
|
||||
pub cause: Mode,
|
||||
}
|
||||
|
||||
impl Step for CleanTools {
|
||||
@ -41,23 +41,23 @@ impl Step for CleanTools {
|
||||
fn run(self, builder: &Builder) {
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let mode = self.mode;
|
||||
let cause = self.cause;
|
||||
|
||||
// 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 = builder.stage_out(compiler, Mode::Tool);
|
||||
let tools_dir = builder.stage_out(compiler, Mode::ToolRustc);
|
||||
let compiler = if builder.force_use_stage1(compiler, target) {
|
||||
builder.compiler(1, compiler.host)
|
||||
} else {
|
||||
compiler
|
||||
};
|
||||
|
||||
for &cur_mode in &[Mode::Libstd, Mode::Libtest, Mode::Librustc] {
|
||||
for &cur_mode in &[Mode::Std, Mode::Test, Mode::Rustc] {
|
||||
let stamp = match cur_mode {
|
||||
Mode::Libstd => libstd_stamp(builder, compiler, target),
|
||||
Mode::Libtest => libtest_stamp(builder, compiler, target),
|
||||
Mode::Librustc => librustc_stamp(builder, compiler, target),
|
||||
Mode::Std => libstd_stamp(builder, compiler, target),
|
||||
Mode::Test => libtest_stamp(builder, compiler, target),
|
||||
Mode::Rustc => librustc_stamp(builder, compiler, target),
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
@ -67,7 +67,7 @@ impl Step for CleanTools {
|
||||
|
||||
// If we are a rustc tool, and std changed, we also need to clear ourselves out -- our
|
||||
// dependencies depend on std. Therefore, we iterate up until our own mode.
|
||||
if mode == cur_mode {
|
||||
if cause == cur_mode {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -104,13 +104,13 @@ impl Step for ToolBuild {
|
||||
let is_ext_tool = self.is_ext_tool;
|
||||
|
||||
match self.mode {
|
||||
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
||||
Mode::Libtest => builder.ensure(compile::Test { compiler, target }),
|
||||
Mode::Librustc => builder.ensure(compile::Rustc { compiler, target }),
|
||||
Mode::Tool => panic!("unexpected Mode::Tool for tool build")
|
||||
Mode::ToolStd => builder.ensure(compile::Std { compiler, target }),
|
||||
Mode::ToolTest => builder.ensure(compile::Test { compiler, target }),
|
||||
Mode::ToolRustc => builder.ensure(compile::Rustc { compiler, target }),
|
||||
_ => panic!("unexpected Mode for tool build")
|
||||
}
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, self.mode, target, "build", path);
|
||||
cargo.arg("--features").arg(self.extra_features.join(" "));
|
||||
|
||||
let _folder = builder.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
|
||||
@ -202,7 +202,7 @@ impl Step for ToolBuild {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
let cargo_out = builder.cargo_out(compiler, Mode::Tool, target)
|
||||
let cargo_out = builder.cargo_out(compiler, self.mode, target)
|
||||
.join(exe(tool, &compiler.host));
|
||||
let bin = builder.tools_dir(compiler).join(exe(tool, &compiler.host));
|
||||
builder.copy(&cargo_out, &bin);
|
||||
@ -214,11 +214,12 @@ impl Step for ToolBuild {
|
||||
pub fn prepare_tool_cargo(
|
||||
builder: &Builder,
|
||||
compiler: Compiler,
|
||||
mode: Mode,
|
||||
target: Interned<String>,
|
||||
command: &'static str,
|
||||
path: &'static str,
|
||||
) -> Command {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, target, command);
|
||||
let mut cargo = builder.cargo(compiler, mode, target, command);
|
||||
let dir = builder.src.join(path);
|
||||
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
|
||||
|
||||
@ -253,7 +254,7 @@ pub fn prepare_tool_cargo(
|
||||
}
|
||||
|
||||
macro_rules! tool {
|
||||
($($name:ident, $path:expr, $tool_name:expr, $mode:expr;)+) => {
|
||||
($($name:ident, $path:expr, $tool_name:expr, $mode:expr $(,llvm_tools = $llvm:expr)*;)+) => {
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Tool {
|
||||
$(
|
||||
@ -261,6 +262,22 @@ macro_rules! tool {
|
||||
)+
|
||||
}
|
||||
|
||||
impl Tool {
|
||||
pub fn get_mode(&self) -> Mode {
|
||||
let mode = match self {
|
||||
$(Tool::$name => $mode,)+
|
||||
};
|
||||
mode
|
||||
}
|
||||
|
||||
/// Whether this tool requires LLVM to run
|
||||
pub fn uses_llvm_tools(&self) -> bool {
|
||||
match self {
|
||||
$(Tool::$name => false $(|| $llvm)*,)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub fn tool_exe(&self, tool: Tool) -> PathBuf {
|
||||
let stage = self.tool_default_stage(tool);
|
||||
@ -324,17 +341,17 @@ macro_rules! tool {
|
||||
}
|
||||
|
||||
tool!(
|
||||
Rustbook, "src/tools/rustbook", "rustbook", Mode::Librustc;
|
||||
ErrorIndex, "src/tools/error_index_generator", "error_index_generator", Mode::Librustc;
|
||||
UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen", Mode::Libstd;
|
||||
Tidy, "src/tools/tidy", "tidy", Mode::Libstd;
|
||||
Linkchecker, "src/tools/linkchecker", "linkchecker", Mode::Libstd;
|
||||
CargoTest, "src/tools/cargotest", "cargotest", Mode::Libstd;
|
||||
Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest;
|
||||
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd;
|
||||
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::Libstd;
|
||||
RustInstaller, "src/tools/rust-installer", "fabricate", Mode::Libstd;
|
||||
RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes", Mode::Libstd;
|
||||
Rustbook, "src/tools/rustbook", "rustbook", Mode::ToolRustc;
|
||||
ErrorIndex, "src/tools/error_index_generator", "error_index_generator", Mode::ToolRustc;
|
||||
UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen", Mode::ToolStd;
|
||||
Tidy, "src/tools/tidy", "tidy", Mode::ToolStd;
|
||||
Linkchecker, "src/tools/linkchecker", "linkchecker", Mode::ToolStd;
|
||||
CargoTest, "src/tools/cargotest", "cargotest", Mode::ToolStd;
|
||||
Compiletest, "src/tools/compiletest", "compiletest", Mode::ToolTest, llvm_tools = true;
|
||||
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::ToolStd;
|
||||
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::ToolStd;
|
||||
RustInstaller, "src/tools/rust-installer", "fabricate", Mode::ToolStd;
|
||||
RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes", Mode::ToolStd;
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
@ -362,7 +379,7 @@ impl Step for RemoteTestServer {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "remote-test-server",
|
||||
mode: Mode::Libstd,
|
||||
mode: Mode::ToolStd,
|
||||
path: "src/tools/remote-test-server",
|
||||
is_ext_tool: false,
|
||||
extra_features: Vec::new(),
|
||||
@ -414,6 +431,7 @@ impl Step for Rustdoc {
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder,
|
||||
build_compiler,
|
||||
Mode::ToolRustc,
|
||||
target,
|
||||
"build",
|
||||
"src/tools/rustdoc");
|
||||
@ -430,8 +448,8 @@ impl Step for Rustdoc {
|
||||
// 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 = builder.cargo_out(build_compiler, Mode::Tool, target)
|
||||
.join(exe("rustdoc-tool-binary", &target_compiler.host));
|
||||
let tool_rustdoc = builder.cargo_out(build_compiler, Mode::ToolRustc, target)
|
||||
.join(exe("rustdoc_tool_binary", &target_compiler.host));
|
||||
|
||||
// don't create a stage0-sysroot/bin directory.
|
||||
if target_compiler.stage > 0 {
|
||||
@ -485,7 +503,7 @@ impl Step for Cargo {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "cargo",
|
||||
mode: Mode::Librustc,
|
||||
mode: Mode::ToolRustc,
|
||||
path: "src/tools/cargo",
|
||||
is_ext_tool: false,
|
||||
extra_features: Vec::new(),
|
||||
@ -533,7 +551,7 @@ macro_rules! tool_extended {
|
||||
compiler: $sel.compiler,
|
||||
target: $sel.target,
|
||||
tool: $tool_name,
|
||||
mode: Mode::Librustc,
|
||||
mode: Mode::ToolRustc,
|
||||
path: $path,
|
||||
extra_features: $sel.extra_features,
|
||||
is_ext_tool: true,
|
||||
@ -575,7 +593,7 @@ impl<'a> Builder<'a> {
|
||||
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.config.build);
|
||||
self.prepare_tool_cmd(compiler, &mut cmd);
|
||||
self.prepare_tool_cmd(compiler, tool, &mut cmd);
|
||||
cmd
|
||||
}
|
||||
|
||||
@ -583,11 +601,11 @@ impl<'a> Builder<'a> {
|
||||
///
|
||||
/// Notably this munges the dynamic library lookup path to point to the
|
||||
/// right location to run `compiler`.
|
||||
fn prepare_tool_cmd(&self, compiler: Compiler, cmd: &mut Command) {
|
||||
fn prepare_tool_cmd(&self, compiler: Compiler, tool: Tool, cmd: &mut Command) {
|
||||
let host = &compiler.host;
|
||||
let mut lib_paths: Vec<PathBuf> = vec![
|
||||
PathBuf::from(&self.sysroot_libdir(compiler, compiler.host)),
|
||||
self.cargo_out(compiler, Mode::Tool, *host).join("deps"),
|
||||
self.cargo_out(compiler, tool.get_mode(), *host).join("deps"),
|
||||
];
|
||||
|
||||
// On MSVC a tool may invoke a C compiler (e.g. compiletest in run-make
|
||||
@ -610,17 +628,19 @@ impl<'a> Builder<'a> {
|
||||
|
||||
// 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);
|
||||
if tool.uses_llvm_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
36
src/ci/docker/disabled/dist-armebv7r-none-eabihf/Dockerfile
Normal file
36
src/ci/docker/disabled/dist-armebv7r-none-eabihf/Dockerfile
Normal file
@ -0,0 +1,36 @@
|
||||
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 \
|
||||
xz-utils \
|
||||
bzip2 \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV BASE_URL=https://releases.linaro.org/components/toolchain/binaries/latest/armeb-eabi/
|
||||
ENV GCC_LINARO=gcc-linaro-7.2.1-2017.11-x86_64_armeb-eabi
|
||||
|
||||
RUN curl -sL $BASE_URL/$GCC_LINARO.tar.xz | tar -xJ
|
||||
|
||||
ENV PATH=$PATH:/$GCC_LINARO/bin
|
||||
|
||||
ENV TARGET=armebv7r-none-eabihf
|
||||
|
||||
ENV CC_armebv7r_none_eabihf=armeb-eabi-gcc \
|
||||
CFLAGS_armebv7r_none_eabihf="-march=armv7-r"
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --disable-docs
|
||||
|
||||
ENV SCRIPT python2.7 ../x.py dist --target $TARGET
|
||||
26
src/ci/docker/disabled/dist-sparc64-linux/Dockerfile
Normal file
26
src/ci/docker/disabled/dist-sparc64-linux/Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
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 \
|
||||
g++-sparc64-linux-gnu \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV HOSTS=sparc64-unknown-linux-gnu
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
|
||||
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
|
||||
@ -29,13 +29,13 @@ ENV PATH=/rustroot/bin:$PATH
|
||||
ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib
|
||||
ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig
|
||||
WORKDIR /tmp
|
||||
COPY dist-i686-linux/shared.sh dist-i686-linux/build-binutils.sh /tmp/
|
||||
COPY dist-x86_64-linux/shared.sh /tmp/
|
||||
|
||||
# We need a build of openssl which supports SNI to download artifacts from
|
||||
# static.rust-lang.org. This'll be used to link into libcurl below (and used
|
||||
# later as well), so build a copy of OpenSSL with dynamic libraries into our
|
||||
# generic root.
|
||||
COPY dist-i686-linux/build-openssl.sh /tmp/
|
||||
COPY dist-x86_64-linux/build-openssl.sh /tmp/
|
||||
RUN ./build-openssl.sh
|
||||
|
||||
# The `curl` binary on CentOS doesn't support SNI which is needed for fetching
|
||||
@ -44,36 +44,43 @@ RUN ./build-openssl.sh
|
||||
#
|
||||
# Note that we also disable a bunch of optional features of curl that we don't
|
||||
# really need.
|
||||
COPY dist-i686-linux/build-curl.sh /tmp/
|
||||
COPY dist-x86_64-linux/build-curl.sh /tmp/
|
||||
RUN ./build-curl.sh
|
||||
|
||||
# binutils < 2.22 has a bug where the 32-bit executables it generates
|
||||
# immediately segfault in Rust, so we need to install our own binutils.
|
||||
#
|
||||
# See https://github.com/rust-lang/rust/issues/20440 for more info
|
||||
COPY dist-x86_64-linux/build-binutils.sh /tmp/
|
||||
RUN ./build-binutils.sh
|
||||
|
||||
# Need a newer version of gcc than centos has to compile LLVM nowadays
|
||||
COPY dist-i686-linux/build-gcc.sh /tmp/
|
||||
RUN ./build-gcc.sh
|
||||
|
||||
# CentOS 5.5 has Python 2.4 by default, but LLVM needs 2.7+
|
||||
COPY dist-i686-linux/build-python.sh /tmp/
|
||||
RUN ./build-python.sh
|
||||
|
||||
# Apparently CentOS 5.5 desn't have `git` in yum, but we're gonna need it for
|
||||
# cloning, so download and build it here.
|
||||
COPY dist-i686-linux/build-git.sh /tmp/
|
||||
RUN ./build-git.sh
|
||||
|
||||
# libssh2 (a dependency of Cargo) requires cmake 2.8.11 or higher but CentOS
|
||||
# only has 2.6.4, so build our own
|
||||
COPY dist-i686-linux/build-cmake.sh /tmp/
|
||||
COPY dist-x86_64-linux/build-cmake.sh /tmp/
|
||||
RUN ./build-cmake.sh
|
||||
|
||||
# Need a newer version of gcc than centos has to compile LLVM nowadays
|
||||
COPY dist-x86_64-linux/build-gcc.sh /tmp/
|
||||
RUN ./build-gcc.sh
|
||||
|
||||
# CentOS 5.5 has Python 2.4 by default, but LLVM needs 2.7+
|
||||
COPY dist-x86_64-linux/build-python.sh /tmp/
|
||||
RUN ./build-python.sh
|
||||
|
||||
# Now build LLVM+Clang 6, afterwards configuring further compilations to use the
|
||||
# clang/clang++ compilers.
|
||||
COPY dist-x86_64-linux/build-clang.sh /tmp/
|
||||
RUN ./build-clang.sh
|
||||
ENV CC=clang CXX=clang++
|
||||
|
||||
# Apparently CentOS 5.5 desn't have `git` in yum, but we're gonna need it for
|
||||
# cloning, so download and build it here.
|
||||
COPY dist-x86_64-linux/build-git.sh /tmp/
|
||||
RUN ./build-git.sh
|
||||
|
||||
# for sanitizers, we need kernel headers files newer than the ones CentOS ships
|
||||
# with so we install newer ones here
|
||||
COPY dist-i686-linux/build-headers.sh /tmp/
|
||||
COPY dist-x86_64-linux/build-headers.sh /tmp/
|
||||
RUN ./build-headers.sh
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
@ -84,11 +91,21 @@ ENV HOSTS=i686-unknown-linux-gnu
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--enable-full-tools \
|
||||
--enable-sanitizers \
|
||||
--enable-profiler
|
||||
--enable-profiler \
|
||||
--set target.i686-unknown-linux-gnu.linker=clang \
|
||||
--build=i686-unknown-linux-gnu
|
||||
ENV SCRIPT python2.7 ../x.py dist --build $HOSTS --host $HOSTS --target $HOSTS
|
||||
ENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=clang
|
||||
|
||||
# This is the only builder which will create source tarballs
|
||||
ENV DIST_SRC 1
|
||||
# This was added when we switched from gcc to clang. It's not clear why this is
|
||||
# needed unfortunately, but without this the stage1 bootstrap segfaults
|
||||
# somewhere inside of a build script. The build ends up just hanging instead of
|
||||
# actually killing the process that segfaulted, but if the process is run
|
||||
# manually in a debugger the segfault is immediately seen as well as the
|
||||
# misaligned stack access.
|
||||
#
|
||||
# Added in #50200 there's some more logs there
|
||||
ENV CFLAGS -mstackrealign
|
||||
|
||||
# When we build cargo in this container, we don't want it to use the system
|
||||
# libcurl, instead it should compile its own.
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
|
||||
source shared.sh
|
||||
|
||||
curl https://ftp.gnu.org/gnu/binutils/binutils-2.25.1.tar.bz2 | tar xfj -
|
||||
|
||||
mkdir binutils-build
|
||||
cd binutils-build
|
||||
hide_output ../binutils-2.25.1/configure --prefix=/rustroot
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ..
|
||||
rm -rf binutils-build
|
||||
rm -rf binutils-2.25.1
|
||||
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
curl https://cmake.org/files/v3.6/cmake-3.6.3.tar.gz | tar xzf -
|
||||
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
hide_output ../cmake-3.6.3/configure --prefix=/rustroot
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ..
|
||||
rm -rf cmake-build
|
||||
rm -rf cmake-3.6.3
|
||||
@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
VERSION=7.51.0
|
||||
|
||||
curl http://cool.haxx.se/download/curl-$VERSION.tar.bz2 | tar xjf -
|
||||
|
||||
mkdir curl-build
|
||||
cd curl-build
|
||||
hide_output ../curl-$VERSION/configure \
|
||||
--prefix=/rustroot \
|
||||
--with-ssl=/rustroot \
|
||||
--disable-sspi \
|
||||
--disable-gopher \
|
||||
--disable-smtp \
|
||||
--disable-smb \
|
||||
--disable-imap \
|
||||
--disable-pop3 \
|
||||
--disable-tftp \
|
||||
--disable-telnet \
|
||||
--disable-manual \
|
||||
--disable-dict \
|
||||
--disable-rtsp \
|
||||
--disable-ldaps \
|
||||
--disable-ldap
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ..
|
||||
rm -rf curl-build
|
||||
rm -rf curl-$VERSION
|
||||
yum erase -y curl
|
||||
@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
|
||||
source shared.sh
|
||||
|
||||
GCC=4.8.5
|
||||
|
||||
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.bz2 | tar xjf -
|
||||
cd gcc-$GCC
|
||||
|
||||
# FIXME(#49246): Remove the `sed` below.
|
||||
#
|
||||
# On 2018 March 21st, two Travis builders' cache for Docker are suddenly invalidated. Normally this
|
||||
# is fine, because we just need to rebuild the Docker image. However, it reveals a network issue:
|
||||
# downloading from `ftp://gcc.gnu.org/` from Travis (using passive mode) often leads to "Connection
|
||||
# timed out" error, and even when the download completed, the file is usually corrupted. This causes
|
||||
# nothing to be landed that day.
|
||||
#
|
||||
# We observed that the `gcc-4.8.5.tar.bz2` above can be downloaded successfully, so as a stability
|
||||
# improvement we try to download from the HTTPS mirror instead. Turns out this uncovered the third
|
||||
# bug: the host `gcc.gnu.org` and `cygwin.com` share the same IP, and the TLS certificate of the
|
||||
# latter host is presented to `wget`! Therefore, we choose to download from the insecure HTTP server
|
||||
# instead here.
|
||||
#
|
||||
sed -i'' 's|ftp://gcc\.gnu\.org/|http://gcc.gnu.org/|g' ./contrib/download_prerequisites
|
||||
|
||||
./contrib/download_prerequisites
|
||||
mkdir ../gcc-build
|
||||
cd ../gcc-build
|
||||
hide_output ../gcc-$GCC/configure \
|
||||
--prefix=/rustroot \
|
||||
--enable-languages=c,c++
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
ln -nsf gcc /rustroot/bin/cc
|
||||
|
||||
cd ..
|
||||
rm -rf gcc-build
|
||||
rm -rf gcc-$GCC
|
||||
yum erase -y gcc gcc-c++ binutils
|
||||
@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
curl -L https://www.kernel.org/pub/software/scm/git/git-2.10.0.tar.gz | tar xzf -
|
||||
|
||||
cd git-2.10.0
|
||||
make configure
|
||||
hide_output ./configure --prefix=/rustroot
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ..
|
||||
rm -rf git-2.10.0
|
||||
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
curl https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.2.84.tar.xz | unxz | tar x
|
||||
|
||||
cd linux-3.2.84
|
||||
hide_output make mrproper
|
||||
hide_output make INSTALL_HDR_PATH=dest headers_install
|
||||
|
||||
find dest/include \( -name .install -o -name ..install.cmd \) -delete
|
||||
yes | cp -fr dest/include/* /usr/include
|
||||
|
||||
cd ..
|
||||
rm -rf linux-3.2.84
|
||||
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
VERSION=1.0.2k
|
||||
URL=https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/openssl-$VERSION.tar.gz
|
||||
|
||||
curl $URL | tar xzf -
|
||||
|
||||
cd openssl-$VERSION
|
||||
hide_output ./config --prefix=/rustroot shared -fPIC
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
cd ..
|
||||
rm -rf openssl-$VERSION
|
||||
|
||||
# Make the system cert collection available to the new install.
|
||||
ln -nsf /etc/pki/tls/cert.pem /rustroot/ssl/
|
||||
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
source shared.sh
|
||||
|
||||
curl https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz | \
|
||||
tar xzf -
|
||||
|
||||
mkdir python-build
|
||||
cd python-build
|
||||
|
||||
# Gotta do some hackery to tell python about our custom OpenSSL build, but other
|
||||
# than that fairly normal.
|
||||
CFLAGS='-I /rustroot/include' LDFLAGS='-L /rustroot/lib -L /rustroot/lib64' \
|
||||
hide_output ../Python-2.7.12/configure --prefix=/rustroot
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ..
|
||||
rm -rf python-build
|
||||
rm -rf Python-2.7.12
|
||||
@ -1,25 +0,0 @@
|
||||
# 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.
|
||||
|
||||
hide_output() {
|
||||
set +x
|
||||
on_err="
|
||||
echo ERROR: An error was encountered with the build.
|
||||
cat /tmp/build.log
|
||||
exit 1
|
||||
"
|
||||
trap "$on_err" ERR
|
||||
bash -c "while true; do sleep 30; echo \$(date) - building ...; done" &
|
||||
PING_LOOP_PID=$!
|
||||
$@ &> /tmp/build.log
|
||||
trap - ERR
|
||||
kill $PING_LOOP_PID
|
||||
set -x
|
||||
}
|
||||
@ -29,7 +29,7 @@ ENV PATH=/rustroot/bin:$PATH
|
||||
ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib
|
||||
ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig
|
||||
WORKDIR /tmp
|
||||
COPY dist-x86_64-linux/shared.sh dist-x86_64-linux/build-binutils.sh /tmp/
|
||||
COPY dist-x86_64-linux/shared.sh /tmp/
|
||||
|
||||
# We need a build of openssl which supports SNI to download artifacts from
|
||||
# static.rust-lang.org. This'll be used to link into libcurl below (and used
|
||||
@ -51,9 +51,15 @@ RUN ./build-curl.sh
|
||||
# immediately segfault in Rust, so we need to install our own binutils.
|
||||
#
|
||||
# See https://github.com/rust-lang/rust/issues/20440 for more info
|
||||
COPY dist-x86_64-linux/build-binutils.sh /tmp/
|
||||
RUN ./build-binutils.sh
|
||||
|
||||
# Need a newer version of gcc than centos has to compile LLVM nowadays
|
||||
# libssh2 (a dependency of Cargo) requires cmake 2.8.11 or higher but CentOS
|
||||
# only has 2.6.4, so build our own
|
||||
COPY dist-x86_64-linux/build-cmake.sh /tmp/
|
||||
RUN ./build-cmake.sh
|
||||
|
||||
# Build a version of gcc capable of building LLVM 6
|
||||
COPY dist-x86_64-linux/build-gcc.sh /tmp/
|
||||
RUN ./build-gcc.sh
|
||||
|
||||
@ -61,16 +67,17 @@ RUN ./build-gcc.sh
|
||||
COPY dist-x86_64-linux/build-python.sh /tmp/
|
||||
RUN ./build-python.sh
|
||||
|
||||
# Now build LLVM+Clang 6, afterwards configuring further compilations to use the
|
||||
# clang/clang++ compilers.
|
||||
COPY dist-x86_64-linux/build-clang.sh /tmp/
|
||||
RUN ./build-clang.sh
|
||||
ENV CC=clang CXX=clang++
|
||||
|
||||
# Apparently CentOS 5.5 desn't have `git` in yum, but we're gonna need it for
|
||||
# cloning, so download and build it here.
|
||||
COPY dist-x86_64-linux/build-git.sh /tmp/
|
||||
RUN ./build-git.sh
|
||||
|
||||
# libssh2 (a dependency of Cargo) requires cmake 2.8.11 or higher but CentOS
|
||||
# only has 2.6.4, so build our own
|
||||
COPY dist-x86_64-linux/build-cmake.sh /tmp/
|
||||
RUN ./build-cmake.sh
|
||||
|
||||
# for sanitizers, we need kernel headers files newer than the ones CentOS ships
|
||||
# with so we install newer ones here
|
||||
COPY dist-x86_64-linux/build-headers.sh /tmp/
|
||||
@ -85,8 +92,10 @@ ENV RUST_CONFIGURE_ARGS \
|
||||
--enable-full-tools \
|
||||
--enable-sanitizers \
|
||||
--enable-profiler \
|
||||
--enable-compiler-docs
|
||||
--enable-compiler-docs \
|
||||
--set target.x86_64-unknown-linux-gnu.linker=clang
|
||||
ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
|
||||
ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
|
||||
|
||||
# This is the only builder which will create source tarballs
|
||||
ENV DIST_SRC 1
|
||||
|
||||
64
src/ci/docker/dist-x86_64-linux/build-clang.sh
Executable file
64
src/ci/docker/dist-x86_64-linux/build-clang.sh
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env 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 -ex
|
||||
|
||||
source shared.sh
|
||||
|
||||
LLVM=6.0.0
|
||||
|
||||
mkdir clang
|
||||
cd clang
|
||||
|
||||
curl https://releases.llvm.org/$LLVM/llvm-$LLVM.src.tar.xz | \
|
||||
xz -d | \
|
||||
tar xf -
|
||||
|
||||
cd llvm-$LLVM.src
|
||||
|
||||
mkdir -p tools/clang
|
||||
|
||||
curl https://releases.llvm.org/$LLVM/cfe-$LLVM.src.tar.xz | \
|
||||
xz -d | \
|
||||
tar xf - -C tools/clang --strip-components=1
|
||||
|
||||
mkdir ../clang-build
|
||||
cd ../clang-build
|
||||
|
||||
# For whatever reason the default set of include paths for clang is different
|
||||
# than that of gcc. As a result we need to manually include our sysroot's
|
||||
# include path, /rustroot/include, to clang's default include path.
|
||||
#
|
||||
# Alsow there's this weird oddity with gcc where there's an 'include-fixed'
|
||||
# directory that it generates. It turns out [1] that Centos 5's headers are so
|
||||
# old that they're incompatible with modern C semantics. While gcc automatically
|
||||
# fixes that clang doesn't account for this. Tell clang to manually include the
|
||||
# fixed headers so we can successfully compile code later on.
|
||||
#
|
||||
# [1]: https://sourceware.org/ml/crossgcc/2008-11/msg00028.html
|
||||
INC="/rustroot/include"
|
||||
INC="$INC:/rustroot/lib/gcc/x86_64-unknown-linux-gnu/4.8.5/include-fixed"
|
||||
INC="$INC:/usr/include"
|
||||
|
||||
hide_output \
|
||||
cmake ../llvm-$LLVM.src \
|
||||
-DCMAKE_C_COMPILER=/rustroot/bin/gcc \
|
||||
-DCMAKE_CXX_COMPILER=/rustroot/bin/g++ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/rustroot \
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 \
|
||||
-DC_INCLUDE_DIRS="$INC"
|
||||
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ../..
|
||||
rm -rf clang
|
||||
@ -42,7 +42,6 @@ hide_output ../gcc-$GCC/configure \
|
||||
--enable-languages=c,c++
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
ln -nsf gcc /rustroot/bin/cc
|
||||
|
||||
cd ..
|
||||
rm -rf gcc-build
|
||||
|
||||
@ -36,8 +36,10 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
|
||||
s3url="s3://$SCCACHE_BUCKET/docker/$cksum"
|
||||
url="https://s3-us-west-1.amazonaws.com/$SCCACHE_BUCKET/docker/$cksum"
|
||||
echo "Attempting to download $s3url"
|
||||
rm -f /tmp/rustci_docker_cache
|
||||
set +e
|
||||
loaded_images=$(curl $url | docker load | sed 's/.* sha/sha/')
|
||||
retry curl -f -L -C - -o /tmp/rustci_docker_cache "$url"
|
||||
loaded_images=$(docker load -i /tmp/rustci_docker_cache | sed 's/.* sha/sha/')
|
||||
set -e
|
||||
echo "Downloaded containers:\n$loaded_images"
|
||||
fi
|
||||
@ -116,6 +118,10 @@ fi
|
||||
# goes ahead and sets it for all builders.
|
||||
args="$args --privileged"
|
||||
|
||||
if [ "$CI" != "" ]; then
|
||||
args="$args --dns 8.8.8.8 --dns 8.8.4.4 --dns 1.1.1.1 --dns 1.0.0.1"
|
||||
fi
|
||||
|
||||
exec docker \
|
||||
run \
|
||||
--volume "$root_dir:/checkout:ro" \
|
||||
|
||||
@ -18,6 +18,7 @@ if __name__ == '__main__':
|
||||
os_name = sys.argv[1]
|
||||
toolstate_file = sys.argv[2]
|
||||
current_state = sys.argv[3]
|
||||
verb = sys.argv[4] # 'regressed' or 'changed'
|
||||
|
||||
with open(toolstate_file, 'r') as f:
|
||||
toolstate = json.load(f)
|
||||
@ -29,10 +30,17 @@ if __name__ == '__main__':
|
||||
tool = cur['tool']
|
||||
state = cur[os_name]
|
||||
new_state = toolstate.get(tool, '')
|
||||
if new_state < state:
|
||||
if verb == 'regressed':
|
||||
updated = new_state < state
|
||||
elif verb == 'changed':
|
||||
updated = new_state != state
|
||||
else:
|
||||
print('Unknown verb {}'.format(updated))
|
||||
sys.exit(2)
|
||||
if updated:
|
||||
print(
|
||||
'Error: The state of "{}" has regressed from "{}" to "{}"'
|
||||
.format(tool, state, new_state)
|
||||
'The state of "{}" has {} from "{}" to "{}"'
|
||||
.format(tool, verb, state, new_state)
|
||||
)
|
||||
regressed = True
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@ SIX_WEEK_CYCLE="$(( ($(date +%s) / 604800 - 3) % 6 ))"
|
||||
|
||||
touch "$TOOLSTATE_FILE"
|
||||
|
||||
# Try to test all the tools and store the build/test success in the TOOLSTATE_FILE
|
||||
|
||||
set +e
|
||||
python2.7 "$X_PY" test --no-fail-fast \
|
||||
src/doc/book \
|
||||
@ -30,12 +32,15 @@ python2.7 "$X_PY" test --no-fail-fast \
|
||||
src/doc/reference \
|
||||
src/doc/rust-by-example \
|
||||
src/tools/rls \
|
||||
src/tools/rustfmt
|
||||
src/tools/rustfmt \
|
||||
src/tools/miri \
|
||||
src/tools/clippy
|
||||
set -e
|
||||
|
||||
cat "$TOOLSTATE_FILE"
|
||||
echo
|
||||
|
||||
# This function checks that if a tool's submodule changed, the tool's state must improve
|
||||
verify_status() {
|
||||
echo "Verifying status of $1..."
|
||||
if echo "$CHANGED_FILES" | grep -q "^M[[:blank:]]$2$"; then
|
||||
@ -55,33 +60,61 @@ verify_status() {
|
||||
fi
|
||||
}
|
||||
|
||||
# deduplicates the submodule check and the assertion that on beta some tools MUST be passing
|
||||
check_dispatch() {
|
||||
if [ "$1" = submodule_changed ]; then
|
||||
# ignore $2 (branch id)
|
||||
verify_status $3 $4
|
||||
elif [ "$2" = beta ]; then
|
||||
echo "Requiring test passing for $3..."
|
||||
if grep -q '"'"$3"'":"\(test\|build\)-fail"' "$TOOLSTATE_FILE"; then
|
||||
exit 4
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# list all tools here
|
||||
status_check() {
|
||||
check_dispatch $1 beta book src/doc/book
|
||||
check_dispatch $1 beta nomicon src/doc/nomicon
|
||||
check_dispatch $1 beta reference src/doc/reference
|
||||
check_dispatch $1 beta rust-by-example src/doc/rust-by-example
|
||||
check_dispatch $1 beta rls src/tool/rls
|
||||
check_dispatch $1 beta rustfmt src/tool/rustfmt
|
||||
# these tools are not required for beta to successfully branch
|
||||
check_dispatch $1 nightly clippy-driver src/tool/clippy
|
||||
check_dispatch $1 nightly miri src/tool/miri
|
||||
}
|
||||
|
||||
# If this PR is intended to update one of these tools, do not let the build pass
|
||||
# when they do not test-pass.
|
||||
|
||||
verify_status book src/doc/book
|
||||
verify_status nomicon src/doc/nomicon
|
||||
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
|
||||
status_check "submodule_changed"
|
||||
|
||||
CHECK_NOT="$(readlink -f "$(dirname $0)/checkregression.py")"
|
||||
change_toolstate() {
|
||||
# only update the history
|
||||
if python2.7 "$CHECK_NOT" "$OS" "$TOOLSTATE_FILE" "_data/latest.json" changed; then
|
||||
echo 'Toolstate is not changed. Not updating.'
|
||||
else
|
||||
if [ $SIX_WEEK_CYCLE -eq 5 ]; then
|
||||
python2.7 "$CHECK_NOT" "$OS" "$TOOLSTATE_FILE" "_data/latest.json" regressed
|
||||
fi
|
||||
sed -i "1 a\\
|
||||
$COMMIT\t$(cat "$TOOLSTATE_FILE")
|
||||
" "history/$OS.tsv"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$RUST_RELEASE_CHANNEL" = nightly -a -n "${TOOLSTATE_REPO_ACCESS_TOKEN+is_set}" ]; then
|
||||
. "$(dirname $0)/repo.sh"
|
||||
MESSAGE_FILE=$(mktemp -t msg.XXXXXX)
|
||||
echo "($OS CI update)" > "$MESSAGE_FILE"
|
||||
commit_toolstate_change "$MESSAGE_FILE" \
|
||||
sed -i "1 a\\
|
||||
$COMMIT\t$(cat "$TOOLSTATE_FILE")
|
||||
" "history/$OS.tsv"
|
||||
# if we are at the last week in the 6-week release cycle, reject any kind of regression.
|
||||
if [ $SIX_WEEK_CYCLE -eq 5 ]; then
|
||||
python2.7 "$(dirname $0)/checkregression.py" \
|
||||
"$OS" "$TOOLSTATE_FILE" "rust-toolstate/_data/latest.json"
|
||||
fi
|
||||
commit_toolstate_change "$MESSAGE_FILE" change_toolstate
|
||||
rm -f "$MESSAGE_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if grep -q fail "$TOOLSTATE_FILE"; then
|
||||
exit 4
|
||||
fi
|
||||
# abort compilation if an important tool doesn't build
|
||||
# (this code is reachable if not on the nightly channel)
|
||||
status_check "beta_required"
|
||||
|
||||
@ -60,7 +60,7 @@ commit_toolstate_change() {
|
||||
OLDFLAGS="$-"
|
||||
set -eu
|
||||
|
||||
git config --global user.email '34210020+rust-toolstate-update@users.noreply.github.com'
|
||||
git config --global user.email '7378925+rust-toolstate-update@users.noreply.github.com'
|
||||
git config --global user.name 'Rust Toolstate Update'
|
||||
git config --global credential.helper store
|
||||
printf 'https://%s:x-oauth-basic@github.com\n' "$TOOLSTATE_REPO_ACCESS_TOKEN" \
|
||||
|
||||
@ -27,10 +27,8 @@ fi
|
||||
ci_dir=`cd $(dirname $0) && pwd`
|
||||
source "$ci_dir/shared.sh"
|
||||
|
||||
if [ "$TRAVIS" == "true" ] && [ "$TRAVIS_BRANCH" != "auto" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-quiet-tests"
|
||||
else
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings"
|
||||
if [ "$TRAVIS" != "true" ] || [ "$TRAVIS_BRANCH" == "auto" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
|
||||
fi
|
||||
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache"
|
||||
@ -48,7 +46,7 @@ fi
|
||||
#
|
||||
# FIXME: need a scheme for changing this `nightly` value to `beta` and `stable`
|
||||
# either automatically or manually.
|
||||
export RUST_RELEASE_CHANNEL=stable
|
||||
export RUST_RELEASE_CHANNEL=beta
|
||||
if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL"
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp"
|
||||
|
||||
@ -21,11 +21,12 @@ function retry {
|
||||
while true; do
|
||||
"$@" && break || {
|
||||
if [[ $n -lt $max ]]; then
|
||||
sleep $n # don't retry immediately
|
||||
((n++))
|
||||
echo "Command failed. Attempt $n/$max:"
|
||||
else
|
||||
echo "The command has failed after $n attempts."
|
||||
exit 1
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
done
|
||||
|
||||
15
src/doc/book/.github/ISSUE_TEMPLATE.md
vendored
15
src/doc/book/.github/ISSUE_TEMPLATE.md
vendored
@ -9,18 +9,9 @@ This version of the book is under development, please file issues liberally!
|
||||
|
||||
### Second edition
|
||||
|
||||
For the second edition, we are currently working with No Starch Press to bring
|
||||
it to print. Chapters go through a number of stages in the editing process, and
|
||||
once they've gotten to the layout stage, they're effectively frozen.
|
||||
|
||||
For chapters that have gotten to the layout stage, we will likely only be
|
||||
accepting changes that correct factual errors or major problems and not, for
|
||||
example, minor wording changes.
|
||||
|
||||
Scroll all the way to the right on https://github.com/rust-lang/book/projects/1
|
||||
to see which chapters have been frozen.
|
||||
|
||||
Please see CONTRIBUTING.md for more details.
|
||||
No Starch Press has brought the second edition to print. Bugs containing
|
||||
factual errors will be documented as errata; bugs for wording changes or
|
||||
other small corrections should be filed against the 2018 edition.
|
||||
|
||||
### First edition
|
||||
|
||||
|
||||
15
src/doc/book/.github/PULL_REQUEST_TEMPLATE.md
vendored
15
src/doc/book/.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -11,18 +11,9 @@ ultimately wouldn't accept.
|
||||
|
||||
### Second edition
|
||||
|
||||
For the second edition, we are currently working with No Starch Press to bring
|
||||
it to print. Chapters go through a number of stages in the editing process, and
|
||||
once they've gotten to the layout stage, they're effectively frozen.
|
||||
|
||||
For chapters that have gotten to the layout stage, we will likely only be
|
||||
accepting changes that correct factual errors or major problems and not, for
|
||||
example, minor wording changes.
|
||||
|
||||
Scroll all the way to the right on https://github.com/rust-lang/book/projects/1
|
||||
to see which chapters have been frozen.
|
||||
|
||||
Please see CONTRIBUTING.md for more details.
|
||||
No Starch Press has brought the second edition to print. Pull requests fixing
|
||||
factual errors will be accepted and documented as errata; pull requests changing
|
||||
wording or other small corrections should be made against the 2018 edition instead.
|
||||
|
||||
### First edition
|
||||
|
||||
|
||||
@ -1,502 +0,0 @@
|
||||
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
|
||||
@ -128,5 +128,4 @@
|
||||
- [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)
|
||||
- [F - How Rust is Made and “Nightly Rust”](appendix-06-nightly-rust.md)
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
## 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
|
||||
The following list contains keywords that are reserved for current or future
|
||||
use by the Rust language. As such, they cannot 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
|
||||
The following keywords currently have the functionality described.
|
||||
|
||||
* `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
|
||||
* `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
|
||||
@ -46,20 +48,10 @@ constants, macros, static values, attributes, types, traits, or lifetimes.
|
||||
* `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.
|
||||
The following keywords do not have any functionality but are reserved by Rust
|
||||
for potential future use.
|
||||
|
||||
* `abstract`
|
||||
* `alignof`
|
||||
|
||||
@ -1,172 +1,204 @@
|
||||
## 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.
|
||||
This appendix contains 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
|
||||
Table B-1 contains 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 -->
|
||||
<span class="caption">Table B-1: Operators</span>
|
||||
|
||||
* `!` (`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.
|
||||
| Operator | Example | Explanation | Overloadable? |
|
||||
|----------|---------|-------------|---------------|
|
||||
| `!` | `ident!(...)`, `ident!{...}`, `ident![...]` | Macro expansion | |
|
||||
| `!` | `!expr` | Bitwise or logical complement | `Not` |
|
||||
| `!=` | `var != expr` | Nonequality comparison | `PartialEq` |
|
||||
| `%` | `expr % expr` | Arithmetic remainder | `Rem` |
|
||||
| `%=` | `var %= expr` | Arithmetic remainder and assignment | `RemAssign` |
|
||||
| `&` | `&expr`, `&mut expr` | Borrow | |
|
||||
| `&` | `&type`, `&mut type`, `&'a type`, `&'a mut type` | Borrowed pointer type | |
|
||||
| `&` | `expr & expr` | Bitwise AND | `BitAnd` |
|
||||
| `&=` | `var &= expr` | Bitwise AND and assignment | `BitAndAssign` |
|
||||
| `&&` | `expr && expr` | Logical AND | |
|
||||
| `*` | `expr * expr` | Arithmetic multiplication | `Mul` |
|
||||
| `*=` | `var *= expr` | Arithmetic multiplication and assignment | `MulAssign` |
|
||||
| `*` | `*expr` | Dereference | |
|
||||
| `*` | `*const type`, `*mut type` | Raw pointer | |
|
||||
| `+` | `trait + trait`, `'a + trait` | Compound type constraint | |
|
||||
| `+` | `expr + expr` | Arithmetic addition | `Add` |
|
||||
| `+=` | `var += expr` | Arithmetic addition and assignment | `AddAssign` |
|
||||
| `,` | `expr, expr` | Argument and element separator | |
|
||||
| `-` | `- expr` | Arithmetic negation | `Neg` |
|
||||
| `-` | `expr - expr` | Arithmetic subtraction | `Sub` |
|
||||
| `-=` | `var -= expr` | Arithmetic subtraction and assignment | `SubAssign` |
|
||||
| `->` | `fn(...) -> type`, <code>\|...\| -> type</code> | 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 | `Div` |
|
||||
| `/=` | `var /= expr` | Arithmetic division and assignment | `DivAssign` |
|
||||
| `:` | `pat: type`, `ident: type` | Constraints | |
|
||||
| `:` | `ident: expr` | Struct field initializer | |
|
||||
| `:` | `'a: loop {...}` | Loop label | |
|
||||
| `;` | `expr;` | Statement and item terminator | |
|
||||
| `;` | `[...; len]` | Part of fixed-size array syntax | |
|
||||
| `<<` | `expr << expr` | Left-shift | `Shl` |
|
||||
| `<<=` | `var <<= expr` | Left-shift and assignment | `ShlAssign` |
|
||||
| `<` | `expr < expr` | Less than comparison | `PartialOrd` |
|
||||
| `<=` | `expr <= expr` | Less than or equal to comparison | `PartialOrd` |
|
||||
| `=` | `var = expr`, `ident = type` | Assignment/equivalence | |
|
||||
| `==` | `expr == expr` | Equality comparison | `PartialEq` |
|
||||
| `=>` | `pat => expr` | Part of match arm syntax | |
|
||||
| `>` | `expr > expr` | Greater than comparison | `PartialOrd` |
|
||||
| `>=` | `expr >= expr` | Greater than or equal to comparison | `PartialOrd` |
|
||||
| `>>` | `expr >> expr` | Right-shift | `Shr` |
|
||||
| `>>=` | `var >>= expr` | Right-shift and assignment | `ShrAssign` |
|
||||
| `@` | `ident @ pat` | Pattern binding | |
|
||||
| `^` | `expr ^ expr` | Bitwise exclusive OR | `BitXor` |
|
||||
| `^=` | `var ^= expr` | Bitwise exclusive OR and assignment | `BitXorAssign` |
|
||||
| <code>\|</code> | <code>pat \| pat</code> | Pattern alternatives | |
|
||||
| <code>\|</code> | <code>expr \| expr</code> | Bitwise OR | `BitOr` |
|
||||
| <code>\|=</code> | <code>var \|= expr</code> | Bitwise OR and assignment | `BitOrAssign` |
|
||||
| <code>\|\|</code> | <code>expr \|\| expr</code> | Logical OR | |
|
||||
| `?` | `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 list contains all non-letters that don’t function as operators;
|
||||
that is, they don’t behave like a function or method call.
|
||||
|
||||
The following lists all non-letters that don’t function as operators; that is,
|
||||
they don’t behave like a function or method call.
|
||||
Table B-2 shows symbols that appear on their own and are valid in a variety of
|
||||
locations.
|
||||
|
||||
#### Standalone Syntax
|
||||
<span class="caption">Table B-2: Stand-Alone Syntax</span>
|
||||
|
||||
* `'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.
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `'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 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 |
|
||||
| <code>\|...\| expr</code> | Closure |
|
||||
| `!` | Always empty bottom type for diverging functions |
|
||||
| `_` | “Ignored” pattern binding; also used to make integer literals readable |
|
||||
|
||||
#### Path-related Syntax
|
||||
Table B-3 shows symbols that appear in the context of a path through the module
|
||||
hierarchy to an item.
|
||||
|
||||
* `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.
|
||||
<span class="caption">Table B-3: Path-Related Syntax</span>
|
||||
|
||||
#### Generics
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `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 that cannot be directly named (e.g., `<&T>::...`, `<[T]>::...`, etc.) |
|
||||
| `trait::method(...)` | Disambiguating a method call by naming the trait that 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 |
|
||||
|
||||
* `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.
|
||||
Table B-4 shows symbols that appear in the context of using generic type
|
||||
parameters.
|
||||
|
||||
#### Trait Bound Constraints
|
||||
<span class="caption">Table B-4: Generics</span>
|
||||
|
||||
* `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.
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `path<...>` | Specifies parameters to generic type in a type (e.g., `Vec<u8>`) |
|
||||
| `path::<...>`, `method::<...>` | Specifies parameters to generic type, function, or method in an expression; often referred to as turbofish (e.g., `"42".parse::<i32>()`) |
|
||||
| `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>` | A generic type where one or more associated types have specific assignments (e.g., `Iterator<Item=T>`) |
|
||||
|
||||
#### Macros and Attributes
|
||||
Table B-5 shows symbols that appear in the context of constraining generic type
|
||||
parameters with trait bounds.
|
||||
|
||||
* `#[meta]`: outer attribute.
|
||||
* `#![meta]`: inner attribute.
|
||||
* `$ident`: macro substitution.
|
||||
* `$ident:kind`: macro capture.
|
||||
* `$(…)…`: macro repetition.
|
||||
<span class="caption">Table B-5: Trait Bound Constraints</span>
|
||||
|
||||
#### Comments
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `T: U` | Generic parameter `T` constrained to types that implement `U` |
|
||||
| `T: 'a` | Generic type `T` must outlive lifetime `'a` (meaning the type cannot transitively contain any references with lifetimes shorter than `'a`) |
|
||||
| `T : 'static` | 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 |
|
||||
|
||||
* `//`: line comment.
|
||||
* `//!`: inner line doc comment.
|
||||
* `///`: outer line doc comment.
|
||||
* `/*…*/`: block comment.
|
||||
* `/*!…*/`: inner block doc comment.
|
||||
* `/**…*/`: outer block doc comment.
|
||||
Table B-6 shows symbols that appear in the context of calling or defining
|
||||
macros and specifying attributes on an item.
|
||||
|
||||
#### Tuples
|
||||
<span class="caption">Table B-6: Macros and Attributes</span>
|
||||
|
||||
* `()`: 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.
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `#[meta]` | Outer attribute |
|
||||
| `#![meta]` | Inner attribute |
|
||||
| `$ident` | Macro substitution |
|
||||
| `$ident:kind` | Macro capture |
|
||||
| `$(…)…` | Macro repetition |
|
||||
|
||||
#### Curly Brackets
|
||||
Table B-7 shows symbols that create comments.
|
||||
|
||||
* `{…}`: block expression.
|
||||
* `Type {…}`: `struct` literal.
|
||||
<span class="caption">Table B-7: Comments</span>
|
||||
|
||||
#### Square Brackets
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `//` | Line comment |
|
||||
| `//!` | Inner line doc comment |
|
||||
| `///` | Outer line doc comment |
|
||||
| `/*...*/` | Block comment |
|
||||
| `/*!...*/` | Inner block doc comment |
|
||||
| `/**...*/` | Outer block doc comment |
|
||||
|
||||
* `[…]`: 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”.
|
||||
Table B-8 shows symbols that appear in the context of using tuples.
|
||||
|
||||
<span class="caption">Table B-8: Tuples</span>
|
||||
|
||||
| Symbol | Explanation |
|
||||
|--------|-------------|
|
||||
| `()` | Empty tuple (aka 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`, etc. | Tuple indexing |
|
||||
|
||||
Table B-9 shows the contexts in which curly braces are used.
|
||||
|
||||
<span class="caption">Table B-9: Curly Brackets</span>
|
||||
|
||||
| Context | Explanation |
|
||||
|---------|-------------|
|
||||
| `{...}` | Block expression |
|
||||
| `Type {...}` | `struct` literal |
|
||||
|
||||
Table B-10 shows the contexts in which square brackets are used.
|
||||
|
||||
<span class="caption">Table B-10: Square Brackets</span>
|
||||
|
||||
| Context | Explanation |
|
||||
|---------|-------------|
|
||||
| `[...]` | 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`, or `RangeFull` as the “index” |
|
||||
|
||||
@ -1,140 +1,104 @@
|
||||
## C - Derivable Traits
|
||||
## Appendix 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.
|
||||
In various places in the book, we’ve discussed the `derive` attribute, which
|
||||
you can apply to a struct or enum definition. The `derive` attribute generates
|
||||
code that will implement a trait with its own default implementation on the
|
||||
type you’ve annotated with the `derive` syntax.
|
||||
|
||||
<!-- 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 -->
|
||||
In this appendix, we provide a reference of all the traits in the standard
|
||||
library that you can use with `derive`. Each section covers:
|
||||
|
||||
<!-- 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
|
||||
* 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
|
||||
consult the standard library documentation for each trait for details on 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.
|
||||
formatting for end users. You should always consider 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, so
|
||||
it 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.”
|
||||
libraries can implement `derive` for their own traits, making the list of
|
||||
traits you can use `derive` with truly open-ended. Implementing `derive`
|
||||
involves using a procedural macro, which is covered in Appendix D.
|
||||
|
||||
### `Debug` for Programmer Output
|
||||
|
||||
The `Debug` trait enables debug formatting in format strings, indicated by
|
||||
adding `:?` within `{}` placeholders.
|
||||
The `Debug` trait enables debug formatting in format strings, which you
|
||||
indicate 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.
|
||||
The `Debug` trait is required, for example, in use of the `assert_eq!` macro.
|
||||
This macro 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.
|
||||
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.
|
||||
structs, two instances are equal only if *all* fields are equal, and the
|
||||
instances are 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 `PartialEq` trait 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
|
||||
applied to types that also implement `PartialEq`, although 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.
|
||||
number types: the implementation of floating point numbers states that two
|
||||
instances of the not-a-number (`NaN`) value 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.
|
||||
An example of when `Eq` is required is for keys in a `HashMap<K, V>` so the
|
||||
`HashMap<K, V>` 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
|
||||
purposes. A type that implements `PartialOrd` can be used with the `<`, `>`,
|
||||
`<=`, and `>=` operators. You can only apply the `PartialOrd` trait 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
|
||||
`Option<Ordering>` that will be `None` when the values given don’t 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.
|
||||
enum definition are considered less 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 `PartialOrd` trait 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
|
||||
ordering will always be possible. You can only apply the `Ord` trait 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`.
|
||||
@ -144,22 +108,14 @@ 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`.
|
||||
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 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
|
||||
@ -170,61 +126,49 @@ 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
|
||||
The `Copy` trait doesn’t define any methods to prevent programmers from
|
||||
overloading those methods and 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`. You can
|
||||
only apply the `Copy` trait to types that also implement `Clone`, because a
|
||||
type that implements `Copy` has a trivial implementation of `Clone` that
|
||||
performs the same task as `Copy`.
|
||||
|
||||
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`.
|
||||
The `Copy` trait is rarely required; types that implement `Copy` have
|
||||
optimizations available, meaning you don’t have to call `clone`, which makes
|
||||
the code more concise.
|
||||
|
||||
`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
|
||||
Everything possible with `Copy` you can also accomplish 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
|
||||
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.
|
||||
An example of when `Hash` is required is in storing keys in a `HashMap<K, V>`
|
||||
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` implements the `default` function. The derived implementation of the
|
||||
`default` function calls the `default` function 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
|
||||
The `Default::default` function 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>`.
|
||||
The `Default` trait is required when you use the method `unwrap_or_default` on
|
||||
`Option<T>` instances, for example. If the `Option<T>` is `None`, the method
|
||||
`unwrap_or_default` will return the result of `Default::default` for the type
|
||||
`T` stored in the `Option<T>`.
|
||||
|
||||
@ -1,53 +1,55 @@
|
||||
## 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:
|
||||
We’ve used macros like `println!` throughout this book but haven’t fully
|
||||
explored what a macro is and how it works. This appendix explains macros as
|
||||
follows:
|
||||
|
||||
- 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
|
||||
* 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
|
||||
so this section is more likely to become out-of-date than the rest of the book.
|
||||
Due to Rust’s stability guarantees, the code shown here will continue to work
|
||||
with future versions, 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.
|
||||
that in mind when 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.
|
||||
Fundamentally, macros are a way of writing code that writes other code, which
|
||||
is 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 manually.
|
||||
|
||||
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
|
||||
A function signature must 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
|
||||
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.
|
||||
The downside to implementing a macro instead of 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:
|
||||
aren’t namespaced within modules like function definitions are. 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 you bring
|
||||
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]
|
||||
@ -56,12 +58,12 @@ 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.
|
||||
to define macros with the same name. In practice, this conflict doesn’t occur
|
||||
often, 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.
|
||||
There is one last important difference between macros and functions: you must
|
||||
define or bring macros into scope *before* you call them in a file, whereas you
|
||||
can define functions anywhere and call them anywhere.
|
||||
|
||||
### Declarative Macros with `macro_rules!` for General Metaprogramming
|
||||
|
||||
@ -72,15 +74,15 @@ 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
|
||||
code associated with them; in this situation, 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
|
||||
use `macro_rules!` by looking 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, the following macro creates a new vector with three
|
||||
integers inside:
|
||||
|
||||
```rust
|
||||
@ -88,11 +90,11 @@ 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
|
||||
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:
|
||||
Let’s look at a slightly simplified definition of the `vec!` macro in Listing
|
||||
D-1.
|
||||
|
||||
```rust
|
||||
#[macro_export]
|
||||
@ -109,33 +111,33 @@ macro_rules! vec {
|
||||
}
|
||||
```
|
||||
|
||||
Listing AD-1: A simplified version of the `vec!` macro definition
|
||||
<span class="caption">Listing D-1: A simplified version of the `vec!` macro
|
||||
definition</span>
|
||||
|
||||
> 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.
|
||||
> Note: The actual definition of the `vec!` macro in the standard library
|
||||
> includes code to preallocate the correct amount of memory up front. That code
|
||||
> is an optimization that we don’t 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.
|
||||
`#[macro_use]` annotation, the macro wouldn’t 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.
|
||||
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
|
||||
is the only pattern in this macro, there is 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].
|
||||
in Listing D-1 mean; for the full macro pattern syntax, see [the reference].
|
||||
|
||||
[the reference]: ../../reference/macros.html
|
||||
|
||||
@ -154,16 +156,11 @@ 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
|
||||
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 -->
|
||||
this macro call will be the following:
|
||||
|
||||
```rust,ignore
|
||||
let mut temp_vec = Vec::new();
|
||||
@ -176,10 +173,10 @@ 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].
|
||||
Given that most Rust programmers will *use* macros more than *write* macros, we
|
||||
won’t discuss `macro_rules!` any further. 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
|
||||
|
||||
@ -188,22 +185,20 @@ as [The Little Book of Rust Macros][tlborm].
|
||||
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
|
||||
output rather than matching against patterns and replacing the code with other
|
||||
code as declarative macros do. At the time of this writing, you can only 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
|
||||
We’ll 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
|
||||
making our crate users 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:
|
||||
been defined. In other words, we’ll write a crate that enables another
|
||||
programmer to write code like Listing D-2 using our crate.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -222,17 +217,17 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<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>
|
||||
<span class="caption">Listing D-2: The code a user of our crate will be able to
|
||||
write when using 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:
|
||||
This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The
|
||||
first step is to make a new library crate, like this:
|
||||
|
||||
```text
|
||||
$ cargo new hello_macro --lib
|
||||
```
|
||||
|
||||
Now we’ll define the `HelloMacro` trait and its associated function:
|
||||
Next, we’ll define the `HelloMacro` trait and its associated function:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -242,9 +237,8 @@ pub trait HelloMacro {
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
We have a trait and its function. At this point, our crate user could implement
|
||||
the trait to achieve the desired functionality, like so:
|
||||
|
||||
```rust,ignore
|
||||
extern crate hello_macro;
|
||||
@ -264,24 +258,21 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
However, they would need to write the implementation block for each type they
|
||||
wanted to use with `hello_macro`; we want to spare them from having to do 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.
|
||||
`hello_macro` function that will print the name of the type the trait is
|
||||
implemented on: Rust doesn’t have reflection capabilities, so it 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,
|
||||
The next step is to define the procedural macro. At the time of this 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:
|
||||
might be lifted. The convention for structuring crates and macro crates is as
|
||||
follows: 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
|
||||
@ -291,15 +282,15 @@ 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.
|
||||
be published separately, 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 reexport 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
|
||||
We’ll also need functionality from the `syn` and `quote` crates, as you’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`:
|
||||
|
||||
@ -314,16 +305,10 @@ 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
|
||||
To start defining the procedural macro, place the code in Listing D-3 into your
|
||||
*src/lib.rs* file for the `hello_macro_derive` crate. Note that this code 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
|
||||
@ -350,10 +335,16 @@ pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing AD-3: Code that most procedural macro crates will
|
||||
<span class="caption">Listing D-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
|
||||
Notice the way we’ve split the functions in D-3; this will be the same for
|
||||
almost every procedural macro crate you see or create, because 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 your
|
||||
procedural macro’s purpose.
|
||||
|
||||
We’ve 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
|
||||
@ -366,29 +357,24 @@ to handle: writing a full parser for Rust code is no simple task.
|
||||
[`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.
|
||||
specifies `#[derive(HelloMacro)]` on a type. The reason is that 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
|
||||
for which we are deriving `HelloMacro`. In the example in Listing D-2, `s` will
|
||||
have the `String` value `struct Pancakes;` because that is 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 this writing, you can only convert a `TokenStream` to a
|
||||
> string. A richer API will exist in the future.
|
||||
|
||||
> 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
|
||||
Now we need to parse the Rust code `String` into a data structure that we can
|
||||
then interpret and perform operations on. This is where `syn` comes into play.
|
||||
The `parse_derive_input` function in `syn` takes a `String` and returns a
|
||||
`DeriveInput` struct representing the parsed Rust code. The following code
|
||||
shows the relevant parts of the `DeriveInput` struct we get from parsing the
|
||||
string `struct Pancakes;`:
|
||||
|
||||
@ -408,29 +394,29 @@ DeriveInput {
|
||||
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.
|
||||
documentation 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`
|
||||
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. But before we do, note
|
||||
that the last part of this `hello_macro_derive` function uses 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.
|
||||
code that our crate users write, so when they compile their crate, they’ll get
|
||||
extra functionality that we provide.
|
||||
|
||||
You may have noticed that we’re calling `unwrap` to panic if the calls to the
|
||||
You might 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!`.
|
||||
return `TokenStream` rather than `Result` to conform to the procedural macro
|
||||
API. We’ve chosen to simplify this example by using `unwrap`; in production
|
||||
code, you should provide more specific error messages about what went wrong by
|
||||
using `panic!` or `expect`.
|
||||
|
||||
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:
|
||||
into a `String` and a `DeriveInput` instance, let’s generate the code that
|
||||
implements the `HelloMacro` trait on the annotated type:
|
||||
|
||||
<span class="filename">Filename: hello_macro_derive/src/lib.rs</span>
|
||||
|
||||
@ -448,12 +434,12 @@ fn impl_hello_macro(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
```
|
||||
|
||||
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")`.
|
||||
annotated type using `ast.ident`. The code in Listing D-2 specifies that the
|
||||
`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 `quote!` macro lets us write the Rust code that we want to return and
|
||||
convert it into `quote::Tokens`. This macro also provides some very 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.
|
||||
@ -470,18 +456,18 @@ 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
|
||||
`String`. There is a possibility that the `#name` input might be an expression
|
||||
to print 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:
|
||||
and `hello_macro_derive`. Let’s hook up these crates to the code in Listing D-2
|
||||
to see the procedural macro in action! Create a new binary project in your
|
||||
*projects* directory using `cargo new pancakes`. We need to add
|
||||
`hello_macro` and `hello_macro_derive` as dependencies in the `pancakes`
|
||||
crate’s *Cargo.toml*. If you’re publishing 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]
|
||||
@ -489,16 +475,16 @@ 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
|
||||
Put the code from Listing D-2 into *src/main.rs*, and 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.
|
||||
`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added 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;
|
||||
In the future, Rust will expand declarative and procedural macros. Rust will
|
||||
use a better declarative macro system with the `macro` keyword and will add
|
||||
more types of procedural macros for more powerful tasks than just `derive`.
|
||||
These systems are still under development at the time of this publication;
|
||||
please consult the online Rust documentation for the latest information.
|
||||
|
||||
@ -11,7 +11,7 @@ For resources in languages other than English. Most are still in progress; see
|
||||
- [简体中文](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)
|
||||
- [Italiano](https://github.com/AgeOfWar/rust-book-it)
|
||||
- [Русский](https://github.com/iDeBugger/rust-book-ru)
|
||||
- [한국어](https://github.com/rinthel/rust-lang-book-ko)
|
||||
- [日本語](https://github.com/hazama-yuinyan/book)
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
# 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.
|
||||
@ -1,10 +1,7 @@
|
||||
# Appendix G - How Rust is Made and “Nightly Rust”
|
||||
# Appendix F - 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!
|
||||
developer.
|
||||
|
||||
### Stability Without Stagnation
|
||||
|
||||
@ -150,7 +147,7 @@ $ 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:
|
||||
of your authors’ Windows computer:
|
||||
|
||||
```powershell
|
||||
> rustup toolchain list
|
||||
@ -1,7 +1,13 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to *The Rust Programming Language*, an introductory book about Rust.
|
||||
> Note: This edition of the book is the same as [The Rust Programming
|
||||
> Language][nsprust] available in print and ebook format from [No Starch
|
||||
> Press][nsp].
|
||||
|
||||
[nsprust]: https://nostarch.com/rust
|
||||
[nsp]: https://nostarch.com/
|
||||
|
||||
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
|
||||
@ -18,11 +24,11 @@ the most important groups.
|
||||
|
||||
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
|
||||
is prone to a variety of subtle bugs, which in most other languages can be
|
||||
caught only 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
|
||||
alongside the compiler, the team can spend their time focusing on the program’s
|
||||
logic rather than chasing down bugs.
|
||||
|
||||
Rust also brings contemporary developer tools to the systems programming world:
|
||||
@ -51,7 +57,7 @@ programming.
|
||||
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
|
||||
bioinformatics, search engines, Internet of Things applications, machine
|
||||
learning, and even major parts of the Firefox web browser.
|
||||
|
||||
### Open Source Developers
|
||||
@ -65,18 +71,17 @@ language.
|
||||
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.
|
||||
through feature additions and refactoring. This is in contrast to the brittle
|
||||
legacy code in languages without these checks, which developers are often
|
||||
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.
|
||||
The Rust language hopes to support many other users as well; those mentioned
|
||||
here are merely some of the biggest stakeholders. Overall, Rust’s greatest
|
||||
ambition is to eliminate the trade-offs that programmers have accepted for
|
||||
decades by providing safety *and* productivity, speed *and* ergonomics. Give
|
||||
Rust a try and see if its choices work for you.
|
||||
|
||||
## Who This Book Is For
|
||||
|
||||
@ -99,16 +104,17 @@ chapters. In concept chapters, you’ll learn about an aspect of Rust. In projec
|
||||
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 1 explains how to install Rust, how to write a Hello, world! program,
|
||||
and how to use Cargo, Rust’s package manager and build tool. Chapter 2 is a
|
||||
hands-on introduction to the Rust language. Here we 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 place for that. At first, you
|
||||
might even want to skip Chapter 3, which covers Rust features similar to those
|
||||
of other programming languages, 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 on to 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 the details you’ve learned.
|
||||
|
||||
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
|
||||
@ -118,15 +124,15 @@ In Chapter 7, you’ll learn about Rust’s module system and about privacy rule
|
||||
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.
|
||||
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.
|
||||
which even with Rust’s safety guarantees is necessary 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
|
||||
@ -136,7 +142,7 @@ 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
|
||||
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
|
||||
@ -148,9 +154,9 @@ 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.
|
||||
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, and 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
|
||||
@ -158,9 +164,9 @@ 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
|
||||
As such, we’ll provide many examples of code that doesn’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.
|
||||
@ -170,4 +176,4 @@ that doesn’t compile.
|
||||
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
|
||||
[book]: https://github.com/rust-lang/book/tree/master/second-edition/src
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
# 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 start your Rust journey! There’s a lot to learn, but every journey starts
|
||||
somewhere. In this chapter, we’ll discuss:
|
||||
|
||||
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
|
||||
* Installing Rust on Linux, macOS, and Windows
|
||||
* Writing a program that prints `Hello, world!`
|
||||
* Using `cargo`, Rust’s package manager and build system
|
||||
|
||||
@ -1,110 +1,96 @@
|
||||
## 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 first step is to install Rust. We’ll download Rust through `rustup`, a
|
||||
command line tool for managing Rust versions and associated tools. You’ll need
|
||||
an internet connection for the download.
|
||||
|
||||
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.
|
||||
> Note: If you prefer not to use `rustup` for some reason, please see [the Rust
|
||||
> installation page](https://www.rust-lang.org/install.html) for other options.
|
||||
|
||||
<!-- PROD: Start Box -->
|
||||
The following steps install the latest stable version of the Rust compiler.
|
||||
Rust’s stability guarantees ensure that all the examples in the book that
|
||||
compile will continue to compile with newer Rust versions. The output might
|
||||
differ slightly between versions, because Rust often improves error messages
|
||||
and warnings. In other words, any newer, stable version of Rust you install
|
||||
using these steps should work as expected with the content of this book.
|
||||
|
||||
> #### Command Line Notation
|
||||
> ### 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 `$`.
|
||||
> In this chapter and throughout the book, we’ll show some commands used in the
|
||||
> terminal. Lines that you should enter in a terminal all start with `$`. You
|
||||
> don’t need to type in the `$` character; it indicates the start of each
|
||||
> command. Lines that don’t start with `$` typically show the output of the
|
||||
> previous command. Additionally, PowerShell-specific examples will use `>`
|
||||
> rather than `$`.
|
||||
|
||||
<!-- PROD: End box -->
|
||||
### Installing `rustup` on Linux or macOS
|
||||
|
||||
### Installing Rustup on Linux or Mac
|
||||
|
||||
If you’re on Linux or a Mac, open a terminal and enter the following command:
|
||||
If you’re using Linux or macOS, 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:
|
||||
The command downloads a script and starts the installation of the `rustup`
|
||||
tool, which installs the latest stable version of Rust. You might be prompted
|
||||
for your password. If the install is successful, the following line will 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.
|
||||
If you prefer, feel free to download the script and inspect it before running
|
||||
it.
|
||||
|
||||
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`:
|
||||
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.
|
||||
Additionally, you’ll need a linker of some kind. It’s likely one is already
|
||||
installed, but when you try to compile a Rust program and get errors indicating
|
||||
that a linker could not execute, that means a linker isn’t installed on your
|
||||
system and you’ll need to install one manually. C compilers usually come with
|
||||
the correct linker. Check your platform’s documentation for how to install a C
|
||||
compiler. Also, some common Rust packages depend on C code and will need a C
|
||||
compiler. Therefore, it might be worth installing one now.
|
||||
|
||||
### Installing Rustup on Windows
|
||||
### 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
|
||||
On Windows, go to [https://www.rust-lang.org/install.html][install] and follow
|
||||
the instructions for installing Rust. At some point in the installation, you’ll
|
||||
receive a message explaining that 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 [Build Tools for Visual Studio 2017][visualstudio]. The tools are in
|
||||
the Other Tools and Frameworks section.
|
||||
|
||||
[install]: https://www.rust-lang.org/en-US/install.html
|
||||
[install]: https://www.rust-lang.org/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.
|
||||
The rest of this book uses commands that work in both *cmd.exe* and PowerShell.
|
||||
If there are specific differences, we’ll explain which to use.
|
||||
|
||||
### Updating and Uninstalling
|
||||
|
||||
Once you have Rust installed via `rustup`, updating to the latest version is
|
||||
easy. From your shell, run the update script:
|
||||
After you’ve installed Rust via `rustup`, updating to the latest version is
|
||||
easy. From your shell, run the following update script:
|
||||
|
||||
```text
|
||||
$ rustup update
|
||||
```
|
||||
|
||||
To uninstall Rust and `rustup`, from your shell, run the uninstall script:
|
||||
To uninstall Rust and `rustup`, run the following uninstall script from your
|
||||
shell:
|
||||
|
||||
```text
|
||||
$ rustup self uninstall
|
||||
@ -112,31 +98,28 @@ $ rustup self uninstall
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
To check whether you have Rust installed correctly, open up a shell and enter:
|
||||
To check whether you have Rust installed correctly, open a shell and enter this
|
||||
line:
|
||||
|
||||
```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:
|
||||
stable version that has been released 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
|
||||
If you see this information, you have installed Rust successfully! If you don’t
|
||||
see this information 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].
|
||||
[Mibbit][mibbit]. At that address you can chat 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
|
||||
@ -149,6 +132,6 @@ 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!
|
||||
Any time a type or function is provided by the standard library and you’re not
|
||||
sure what it does or how to use it, use the application programming interface
|
||||
(API) documentation to find out!
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
## 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!
|
||||
Now that you’ve installed Rust, let’s write your first Rust program. It’s
|
||||
traditional when learning a new language to write a little program that prints
|
||||
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
|
||||
> Note: This book assumes basic familiarity with the command line. Rust makes
|
||||
> no specific demands about your editing or tooling or where your code lives, so
|
||||
> if you prefer to use an integrated development environment (IDE) instead of
|
||||
> 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. Recently,
|
||||
> the Rust team has been focusing on enabling great IDE support, 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.
|
||||
You’ll start by making a directory to store your Rust code. It doesn’t matter
|
||||
to Rust where your code lives, but for the exercises and projects in this book,
|
||||
we 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:
|
||||
and a directory for the Hello, world! project within the *projects* directory.
|
||||
|
||||
Linux and Mac:
|
||||
For Linux and macOS, enter this:
|
||||
|
||||
```text
|
||||
$ mkdir ~/projects
|
||||
@ -31,7 +31,7 @@ $ mkdir hello_world
|
||||
$ cd hello_world
|
||||
```
|
||||
|
||||
Windows CMD:
|
||||
For Windows CMD, enter this:
|
||||
|
||||
```cmd
|
||||
> mkdir "%USERPROFILE%\projects"
|
||||
@ -40,7 +40,7 @@ Windows CMD:
|
||||
> cd hello_world
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
For Windows PowerShell, enter this:
|
||||
|
||||
```powershell
|
||||
> mkdir $env:USERPROFILE\projects
|
||||
@ -51,13 +51,12 @@ Windows PowerShell:
|
||||
|
||||
### Writing and Running a Rust Program
|
||||
|
||||
Next, make a new source file and call it *main.rs*---Rust files always end with
|
||||
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*.
|
||||
an underscore to separate them. For example, 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:
|
||||
Now open the *main.rs* file you just created and enter the code in Listing 1-1.
|
||||
|
||||
<span class="filename">Filename: main.rs</span>
|
||||
|
||||
@ -67,9 +66,9 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 1-1: A program that prints “Hello, world!”</span>
|
||||
<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
|
||||
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
|
||||
@ -78,7 +77,7 @@ $ ./main
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
On Windows, use `.\main.exe` instead of `./main`.
|
||||
On Windows, enter the command `.\main.exe` instead of `./main`:
|
||||
|
||||
```powershell
|
||||
> rustc main.rs
|
||||
@ -86,22 +85,17 @@ On Windows, use `.\main.exe` instead of `./main`.
|
||||
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.
|
||||
Regardless of your operating system, the string `Hello, world!` should print to
|
||||
the terminal. If you don’t see this output, refer back to the “Troubleshooting”
|
||||
part of the Intstallation section 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 -->
|
||||
If `Hello, world!` did print, congratulations! You’ve officially written a Rust
|
||||
program. That makes you a Rust programmer—welcome!
|
||||
|
||||
### 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:
|
||||
Let’s review in detail what just happened in your Hello, world! program.
|
||||
Here’s the first piece of the puzzle:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -109,81 +103,86 @@ 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
|
||||
These lines define a function in Rust. The `main` function is special: it is
|
||||
always the first code that runs in 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 `)`.
|
||||
nothing. If there were parameters, they would go inside the parentheses, `()`.
|
||||
|
||||
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.
|
||||
Also, note that the function body is wrapped in curly brackets, `{}`. Rust
|
||||
requires these around all function bodies. It’s good style to place the opening
|
||||
curly bracket on the same line as the function declaration, adding 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.
|
||||
At the time of this writing, an automatic formatter tool called `rustfmt` is
|
||||
under development. If you want to stick to a standard style across Rust
|
||||
projects, `rustfmt` will format your code in a particular style. The Rust team
|
||||
plans to eventually include this tool with the standard Rust distribution, like
|
||||
`rustc`. So depending on when you read this book, it might already be installed
|
||||
on your computer! Check the online documentation for more details.
|
||||
|
||||
Inside the `main` function, we have this code:
|
||||
Inside the `main` function is the following 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.
|
||||
This line does all the work in this little program: it prints text to the
|
||||
screen. There are four important details to notice here. First, 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
|
||||
Second, `println!` calls a Rust macro. If it called a function instead, it
|
||||
would be entered as `println` (without the `!`). We’ll discuss Rust macros in
|
||||
more detail in Appendix D. For now, you just need to know that using a `!`
|
||||
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 -->
|
||||
Third, you see the `"Hello, world!"` string. We pass this string as an argument
|
||||
to `println!`, and the string is printed to the screen.
|
||||
|
||||
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
|
||||
`;`.
|
||||
Fourth, 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 semicolon.
|
||||
|
||||
### 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.
|
||||
You’ve just run a newly created program, so let’s examine each step in the
|
||||
process.
|
||||
|
||||
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:
|
||||
Before running a Rust program, you must 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.
|
||||
If you have 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:
|
||||
On Linux and macOS 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:
|
||||
With PowerShell on Windows, you can use `ls` as well, but you'll see three files:
|
||||
|
||||
```text
|
||||
> ls
|
||||
|
||||
|
||||
Directory: Path:\to\the\project
|
||||
|
||||
|
||||
Mode LastWriteTime Length Name
|
||||
---- ------------- ------ ----
|
||||
-a---- 6/1/2018 7:31 AM 137728 main.exe
|
||||
-a---- 6/1/2018 7:31 AM 1454080 main.pdb
|
||||
-a---- 6/1/2018 7:31 AM 14 main.rs
|
||||
```
|
||||
|
||||
With CMD on Windows, you would enter the following:
|
||||
|
||||
```cmd
|
||||
> dir /B %= the /B option says to only show the file names =%
|
||||
@ -192,27 +191,28 @@ 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:
|
||||
This shows the source code file with the *.rs* extension, the executable file
|
||||
(*main.exe* on Windows, but *main* on all other platforms), and, when using
|
||||
CMD, a file containing debugging information with the *.pdb* extension. From
|
||||
here, you run the *main* or *main.exe* file, like this:
|
||||
|
||||
```text
|
||||
$ ./main # or .\main.exe on Windows
|
||||
$ ./main # or .\main.exe on Windows
|
||||
```
|
||||
|
||||
If *main.rs* were your “Hello, world!” program, this would print `Hello,
|
||||
If *main.rs* was your Hello, world! program, this line 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.
|
||||
If you’re more familiar with a dynamic language, such as Ruby, Python, or
|
||||
JavaScript, you might not be used to compiling and running a program as
|
||||
separate steps. Rust is an *ahead-of-time compiled* language, meaning you can
|
||||
compile a program and 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, they need to have a Ruby, Python, or JavaScript implementation
|
||||
installed (respectively). But in those languages, you only need one command to
|
||||
compile and run your program. Everything is a trade-off 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.
|
||||
grows, you’ll want to manage all the options and make it easy to share your
|
||||
code. Next, we’ll introduce you to the Cargo tool, which will help you write
|
||||
real-world Rust programs.
|
||||
|
||||
@ -1,71 +1,57 @@
|
||||
## 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*.)
|
||||
Cargo is Rust’s build system and package manager. Most Rustaceans use this tool
|
||||
to manage their Rust projects because Cargo handles 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.
|
||||
dependencies. So if we had built the Hello, world! project with Cargo, it would
|
||||
only use the part of Cargo that handles building your code. As you write more
|
||||
complex Rust programs, you’ll add dependencies, and if you start a project
|
||||
using Cargo, adding dependencies will be much 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:
|
||||
Because the vast majority of Rust projects use Cargo, the rest of this book
|
||||
assumes that you’re using Cargo too. Cargo comes installed with Rust if you
|
||||
used the official installers discussed in the “Installation” section. If you
|
||||
installed Rust through some other means, check whether Cargo is 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.
|
||||
If you see a version number, you have it! If you see an error, such as `command
|
||||
not found`, 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:
|
||||
original Hello, world! project. Navigate back to your *projects* directory (or
|
||||
wherever you decided to store your code). Then, on any operating system, run
|
||||
the following:
|
||||
|
||||
```text
|
||||
$ cargo new hello_cargo --bin
|
||||
$ cargo new hello_cargo
|
||||
$ 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 -->
|
||||
The first command creates a new directory called *hello_cargo*. We’ve named
|
||||
our project *hello_cargo*, and Cargo creates its files in a directory of the
|
||||
same name.
|
||||
|
||||
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.
|
||||
Go into the *hello_cargo* directory and list the files. You’ll see that Cargo
|
||||
has generated two files and one directory for us: a *Cargo.toml* file 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.
|
||||
> 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:
|
||||
Open *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>
|
||||
|
||||
@ -81,8 +67,8 @@ authors = ["Your Name <you@example.com>"]
|
||||
<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.
|
||||
This file is in the [*TOML*][toml]<!-- ignore --> (*Tom’s Obvious, Minimal
|
||||
Language*) format, which is Cargo’s configuration format.
|
||||
|
||||
[toml]: https://github.com/toml-lang/toml
|
||||
|
||||
@ -90,17 +76,17 @@ 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 next three lines set the configuration information Cargo needs to compile
|
||||
your program: the name, the version, and who wrote it. Cargo gets your name and
|
||||
email information from your environment, so if that information is not correct,
|
||||
fix the information now and then 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:
|
||||
Now open *src/main.rs* and take a look:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -110,27 +96,27 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
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 has generated a Hello, world! program for you, just like the one we wrote
|
||||
in Listing 1-1! So far, the differences between our previous project and the
|
||||
project Cargo generates are that Cargo placed the code 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.
|
||||
Cargo expects your source files to live inside the *src* directory. The
|
||||
top-level project directory is just for README files, license information,
|
||||
configuration files, and anything else not related to your code. Using Cargo
|
||||
helps you organize your projects. 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*.
|
||||
If you started a project that doesn’t use Cargo, as we did with the Hello,
|
||||
world! project, you can convert it to a project that does use Cargo. Move the
|
||||
project code into the *src* directory and create an appropriate *Cargo.toml*
|
||||
file.
|
||||
|
||||
### 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:
|
||||
Now let’s look at what’s different when we build and run the Hello, world!
|
||||
program with Cargo! From your *hello_cargo* directory, build your project by
|
||||
entering the following command:
|
||||
|
||||
```text
|
||||
$ cargo build
|
||||
@ -138,25 +124,25 @@ $ cargo build
|
||||
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:
|
||||
This command creates an executable file in *target/debug/hello_cargo* (or
|
||||
*target\debug\hello_cargo.exe* on Windows) rather than in your current
|
||||
directory. You can run the executable 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.
|
||||
If all goes well, `Hello, world!` should print to the terminal. Running `cargo
|
||||
build` for the first time also causes Cargo to create a new file at the top
|
||||
level: *Cargo.lock*. This file keeps 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 change this file manually; Cargo
|
||||
manages 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:
|
||||
`./target/debug/hello_cargo`, but we can also use `cargo run` to compile the
|
||||
code and then run the resulting executable all in one command:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
@ -165,10 +151,10 @@ $ cargo run
|
||||
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:
|
||||
Notice that this time we didn’t see output indicating that Cargo was compiling
|
||||
`hello_cargo`. Cargo figured out that the files hadn’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 this output:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
@ -178,8 +164,8 @@ $ cargo run
|
||||
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:
|
||||
Cargo also provides a command called `cargo check`. This command quickly checks
|
||||
your code to make sure it compiles but doesn’t produce an executable:
|
||||
|
||||
```text
|
||||
$ cargo check
|
||||
@ -187,48 +173,48 @@ $ cargo check
|
||||
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.
|
||||
Why would you not want an executable? Often, `cargo check` is much faster than
|
||||
`cargo build`, because it skips the step of producing an executable. If you’re
|
||||
continually checking your work while writing the code, using `cargo check` will
|
||||
speed up the process! As such, many Rustaceans run `cargo check` periodically
|
||||
as they write their program to make sure it compiles. Then they run `cargo
|
||||
build` when they’re ready to use the executable.
|
||||
|
||||
So to recap, using Cargo:
|
||||
Let’s recap what we’ve learned so far about 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.
|
||||
* We can build a project using `cargo build` or `cargo check`.
|
||||
* We can build and run a project in one step using `cargo run`.
|
||||
* Instead of saving the result of the build in the same directory as our code,
|
||||
Cargo stores 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.
|
||||
An additional advantage of using Cargo is that the commands are the same no
|
||||
matter which operating system you’re working on. So, at this point, we’ll no
|
||||
longer provide specific instructions for Linux and macOS 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
|
||||
--release` to compile it with optimizations. This command will create an
|
||||
executable in *target/release* instead of *target/debug*. The optimizations
|
||||
make your Rust code run faster, but turning them on lengthens the time it takes
|
||||
for your program to compile. This is why there are two different profiles: one
|
||||
for development, when you want to rebuild quickly and often, and another 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*.
|
||||
repeatedly and that will run as fast as possible. If you’re benchmarking your
|
||||
code’s running time, 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.
|
||||
With simple projects, Cargo doesn’t provide a lot of value over just using
|
||||
`rustc`, but it will prove its worth as your programs become more intricate.
|
||||
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:
|
||||
tooling you’ll use in 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 to that project’s directory, and build:
|
||||
|
||||
```text
|
||||
$ git clone someurl.com/someproject
|
||||
@ -236,31 +222,22 @@ $ cd someproject
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
If you want to look at Cargo in more detail, check out [its documentation].
|
||||
For more information about Cargo, 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:
|
||||
you’ve learned how to:
|
||||
|
||||
* Installed the latest stable version of Rust
|
||||
* Written a “Hello, world!” program using both `rustc` directly and using
|
||||
the conventions of `cargo`
|
||||
* Install the latest stable version of Rust using `rustup`
|
||||
* Update to a newer Rust version
|
||||
* Open locally installed documentation
|
||||
* Write and run a Hello, world! program using `rustc` directly
|
||||
* Create and run a new project 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.
|
||||
This is a great time to build a more substantial program to get used to reading
|
||||
and writing Rust code. So, in Chapter 2, we’ll build a guessing game program.
|
||||
If you would rather start by learning how common programming concepts work in
|
||||
Rust, see Chapter 3 and then return to Chapter 2.
|
||||
|
||||
@ -18,13 +18,12 @@ To set up a new project, go to the *projects* directory that you created in
|
||||
Chapter 1 and make a new project using Cargo, like so:
|
||||
|
||||
```text
|
||||
$ cargo new guessing_game --bin
|
||||
$ cargo new guessing_game
|
||||
$ cd guessing_game
|
||||
```
|
||||
|
||||
The first command, `cargo new`, takes the name of the project (`guessing_game`)
|
||||
as the first argument. The `--bin` flag tells Cargo to make a binary project,
|
||||
like the one in Chapter 1. The second command changes to the new project’s
|
||||
as the first argument. The second command changes to the new project’s
|
||||
directory.
|
||||
|
||||
Look at the generated *Cargo.toml* file:
|
||||
@ -155,11 +154,11 @@ line. Notice that this is a `let` statement, which is used to create a
|
||||
let foo = bar;
|
||||
```
|
||||
|
||||
This line creates a new variable named `foo` and bind it to the value `bar`. In
|
||||
Rust, variables are immutable by default. We’ll be discussing this concept in
|
||||
detail in the “Variables and Mutability” section in Chapter 3. The following
|
||||
example shows how to use `mut` before the variable name to make a variable
|
||||
mutable:
|
||||
This line creates a new variable named `foo` and binds it to the value of the
|
||||
`bar` variable. In Rust, variables are immutable by default. We’ll be
|
||||
discussing this concept in detail in the “Variables and Mutability” section in
|
||||
Chapter 3. The following example shows how to use `mut` before the variable
|
||||
name to make a variable mutable:
|
||||
|
||||
```rust,ignore
|
||||
let foo = 5; // immutable
|
||||
@ -685,8 +684,8 @@ Could not compile `guessing_game`.
|
||||
|
||||
The core of the error states that there are *mismatched types*. Rust has a
|
||||
strong, static type system. However, it also has type inference. When we wrote
|
||||
`let guess = String::new()`, Rust was able to infer that `guess` should be a
|
||||
`String` and didn’t make us write the type. The `secret_number`, on the other
|
||||
`let mut guess = String::new();`, Rust was able to infer that `guess` should be
|
||||
a `String` and didn’t make us write the type. The `secret_number`, on the other
|
||||
hand, is a number type. A few number types can have a value between 1 and 100:
|
||||
`i32`, a 32-bit number; `u32`, an unsigned 32-bit number; `i64`, a 64-bit
|
||||
number; as well as others. Rust defaults to an `i32`, which is the type of
|
||||
|
||||
@ -9,7 +9,7 @@ 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`.
|
||||
in your *projects* directory by using `cargo new 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:
|
||||
|
||||
@ -50,13 +50,14 @@ 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` |
|
||||
| Length | Signed | Unsigned |
|
||||
|---------|---------|----------|
|
||||
| 8-bit | `i8` | `u8` |
|
||||
| 16-bit | `i16` | `u16` |
|
||||
| 32-bit | `i32` | `u32` |
|
||||
| 64-bit | `i64` | `u64` |
|
||||
| 128-bit | `i128` | `u128` |
|
||||
| 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
|
||||
@ -98,6 +99,21 @@ 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.
|
||||
|
||||
##### Integer Overflow
|
||||
|
||||
Let's say that you have a `u8`, which can hold values between zero and `255`.
|
||||
What happens if you try to change it to `256`? This is called "integer
|
||||
overflow", and Rust has some interesting rules around this behavior. When
|
||||
compiling in debug mode, Rust checks for this kind of issue and will cause
|
||||
your program to *panic*, which is the term Rust uses when a program exits
|
||||
with an error. We'll discuss panics more in Chapter 9.
|
||||
|
||||
In release builds, Rust does not check for overflow, and instead will
|
||||
do something called "two's compliment wrapping." In short, `256` becomes
|
||||
`0`, `257` becomes `1`, etc. Relying on overflow is considered an error,
|
||||
even if this behavior happens. If you want this behavior explicitly, the
|
||||
standard library has a type, `Wrapping`, that provides it explicitly.
|
||||
|
||||
#### Floating-Point Types
|
||||
|
||||
Rust also has two primitive types for *floating-point numbers*, which are
|
||||
@ -172,12 +188,14 @@ 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.
|
||||
|
||||
Booleans are one byte in size.
|
||||
|
||||
#### 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.)
|
||||
code shows one way to use it. (Note that the `char` literal is specified with
|
||||
single quotes, as opposed to string literals, which use double quotes.)
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -204,8 +222,9 @@ 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.
|
||||
A tuple is a general way of grouping together some number of other values
|
||||
with a variety of types into one compound type. Tuples have a fixed length:
|
||||
once declared, they cannot grow or shrink in size.
|
||||
|
||||
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
|
||||
@ -269,7 +288,7 @@ index in a tuple is 0.
|
||||
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.
|
||||
fixed length, like tuples.
|
||||
|
||||
In Rust, the values going into an array are written as a comma-separated list
|
||||
inside square brackets:
|
||||
@ -300,6 +319,21 @@ let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
```
|
||||
|
||||
Arrays have an interesting type; it looks like this: `[type; number]`. For
|
||||
example:
|
||||
|
||||
```rust
|
||||
let a: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
```
|
||||
|
||||
First, there's square brackets; they look like the syntax for creating an
|
||||
array. Inside, there's two pieces of information, separated by a semicolon.
|
||||
The first is the type of each element of the array. Since all elements have
|
||||
the same type, we only need to list it once. After the semicolon, there's
|
||||
a number that indicates the length of the array. Since an array has a fixed size,
|
||||
this number is always the same, even if the array's elements are modified, it
|
||||
cannot grow or shrink.
|
||||
|
||||
##### Accessing Array Elements
|
||||
|
||||
An array is a single chunk of memory allocated on the stack. You can access
|
||||
@ -355,7 +389,7 @@ 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.
|
||||
panic.
|
||||
|
||||
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
|
||||
|
||||
@ -320,7 +320,7 @@ fn plus_one(x: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
Running this code produces an error, as follows:
|
||||
Compiling this code produces an error, as follows:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
|
||||
@ -226,7 +226,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
When we try to run this code, we’ll get an error. The `if` and `else` arms have
|
||||
When we try to compile 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:
|
||||
|
||||
@ -309,6 +309,30 @@ 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.
|
||||
|
||||
|
||||
#### 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);
|
||||
}
|
||||
```
|
||||
|
||||
#### Conditional Loops with `while`
|
||||
|
||||
It’s often useful for a program to evaluate a condition within a loop. While
|
||||
|
||||
@ -150,7 +150,18 @@ 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`.
|
||||
`start` and continues up to, but not including, `end`. If we wanted to include
|
||||
`end`, we can use `..=` instead of `..`:
|
||||
|
||||
```rust
|
||||
let s = String::from("hello world");
|
||||
|
||||
let hello = &s[0..=4];
|
||||
let world = &s[6..=10];
|
||||
```
|
||||
|
||||
The `=` means that we're including the last number, if that helps you remember
|
||||
the difference between `..` and `..=`.
|
||||
|
||||
We can create slices using a range within brackets by specifying
|
||||
`[starting_index..ending_index]`, where `starting_index` is the first position
|
||||
@ -158,7 +169,7 @@ 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.
|
||||
a slice that contains a pointer to the 7th byte of `s` and a length value of 5.
|
||||
|
||||
Figure 4-6 shows this in a diagram.
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ 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`:
|
||||
option:
|
||||
|
||||
```text
|
||||
$ cargo new communicator --lib
|
||||
@ -32,8 +32,7 @@ mod tests {
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
Cargo creates an example test to help us get our library started. 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*.
|
||||
|
||||
@ -104,6 +104,39 @@ fn main() {
|
||||
We’re still specifying the `TrafficLight` namespace for the `Green` variant
|
||||
because we didn’t include `Green` in the `use` statement.
|
||||
|
||||
#### 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() {}
|
||||
```
|
||||
|
||||
### Bringing All Names into Scope with a Glob
|
||||
|
||||
To bring all the items in a namespace into scope at once, we can use the `*`
|
||||
|
||||
@ -96,17 +96,32 @@ read their contents is a good next step. There are two ways to reference a
|
||||
value stored in a vector. In the examples, we’ve annotated the types of the
|
||||
values that are returned from these functions for extra clarity.
|
||||
|
||||
Listing 8-5 shows both methods of accessing a value in a vector, either with
|
||||
indexing syntax or the `get` method:
|
||||
Listing 8-5 shows the method of accessing a value in a vector with
|
||||
indexing syntax:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
let third: &i32 = &v[2];
|
||||
let third: Option<&i32> = v.get(2);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-5: Using indexing syntax or the `get` method to
|
||||
<span class="caption">Listing 8-5: Using indexing syntax to
|
||||
access an item in a vector</span>
|
||||
|
||||
Listing 8-6 shows the method of accessing a value in a vector, with
|
||||
the `get` method:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
let v_index = 2;
|
||||
|
||||
match v.get(v_index) {
|
||||
Some(_) => { println!("Reachable element at index: {}", v_index); },
|
||||
None => { println!("Unreachable element at index: {}", v_index); }
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-6: Using the `get` method to
|
||||
access an item in a vector</span>
|
||||
|
||||
Note two details here. First, we use the index value of `2` to get the third
|
||||
@ -119,7 +134,7 @@ Rust has two ways to reference an element so you can choose how the program
|
||||
behaves when you try to use an index value that the vector doesn’t have an
|
||||
element for. As an example, let’s see what a program will do if it has a vector
|
||||
that holds five elements and then tries to access an element at index 100, as
|
||||
shown in Listing 8-6:
|
||||
shown in Listing 8-7:
|
||||
|
||||
```rust,should_panic
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
@ -128,7 +143,7 @@ let does_not_exist = &v[100];
|
||||
let does_not_exist = v.get(100);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-6: Attempting to access the element at index
|
||||
<span class="caption">Listing 8-7: Attempting to access the element at index
|
||||
100 in a vector containing five elements</span>
|
||||
|
||||
When we run this code, the first `[]` method will cause the program to panic
|
||||
@ -150,7 +165,7 @@ When the program has a valid reference, the borrow checker enforces the
|
||||
ownership and borrowing rules (covered in Chapter 4) to ensure this reference
|
||||
and any other references to the contents of the vector remain valid. Recall the
|
||||
rule that states you can’t have mutable and immutable references in the same
|
||||
scope. That rule applies in Listing 8-7, where we hold an immutable reference to
|
||||
scope. That rule applies in Listing 8-8, where we hold an immutable reference to
|
||||
the first element in a vector and try to add an element to the end, which won’t
|
||||
work:
|
||||
|
||||
@ -162,7 +177,7 @@ let first = &v[0];
|
||||
v.push(6);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-7: Attempting to add an element to a vector
|
||||
<span class="caption">Listing 8-8: Attempting to add an element to a vector
|
||||
while holding a reference to an item</span>
|
||||
|
||||
Compiling this code will result in this error:
|
||||
@ -181,7 +196,7 @@ error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immuta
|
||||
| - immutable borrow ends here
|
||||
```
|
||||
|
||||
The code in Listing 8-7 might look like it should work: why should a reference
|
||||
The code in Listing 8-8 might look like it should work: why should a reference
|
||||
to the first element care about what changes at the end of the vector? This
|
||||
error is due to the way vectors work: adding a new element onto the end of the
|
||||
vector might require allocating new memory and copying the old elements to the
|
||||
@ -197,7 +212,7 @@ programs from ending up in that situation.
|
||||
|
||||
If we want to access each element in a vector in turn, we can iterate through
|
||||
all of the elements rather than use indexes to access one at a time. Listing
|
||||
8-8 shows how to use a `for` loop to get immutable references to each element
|
||||
8-9 shows how to use a `for` loop to get immutable references to each element
|
||||
in a vector of `i32` values and print them:
|
||||
|
||||
```rust
|
||||
@ -207,11 +222,11 @@ for i in &v {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-8: Printing each element in a vector by
|
||||
<span class="caption">Listing 8-9: Printing each element in a vector by
|
||||
iterating over the elements using a `for` loop</span>
|
||||
|
||||
We can also iterate over mutable references to each element in a mutable vector
|
||||
in order to make changes to all the elements. The `for` loop in Listing 8-9
|
||||
in order to make changes to all the elements. The `for` loop in Listing 8-10
|
||||
will add `50` to each element:
|
||||
|
||||
```rust
|
||||
@ -221,12 +236,12 @@ for i in &mut v {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-9: Iterating over mutable references to
|
||||
<span class="caption">Listing 8-10: Iterating over mutable references to
|
||||
elements in a vector</span>
|
||||
|
||||
To change the value that the mutable reference refers to, we have to use the
|
||||
dereference operator (`*`) to get to the value in `i` before we can use the
|
||||
`+=` operator .
|
||||
`+=` operator . We'll talk more about `*` in Chapter 15.
|
||||
|
||||
### Using an Enum to Store Multiple Types
|
||||
|
||||
@ -241,7 +256,7 @@ some of the columns in the row contain integers, some floating-point numbers,
|
||||
and some strings. We can define an enum whose variants will hold the different
|
||||
value types, and then all the enum variants will be considered the same type:
|
||||
that of the enum. Then we can create a vector that holds that enum and so,
|
||||
ultimately, holds different types. We’ve demonstrated this in Listing 8-10:
|
||||
ultimately, holds different types. We’ve demonstrated this in Listing 8-11:
|
||||
|
||||
```rust
|
||||
enum SpreadsheetCell {
|
||||
@ -257,7 +272,7 @@ let row = vec![
|
||||
];
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-10: Defining an `enum` to store values of
|
||||
<span class="caption">Listing 8-11: Defining an `enum` to store values of
|
||||
different types in one vector</span>
|
||||
|
||||
Rust needs to know what types will be in the vector at compile time so it knows
|
||||
|
||||
@ -307,19 +307,22 @@ fn read_username_from_file() -> Result<String, io::Error> {
|
||||
<span class="caption">Listing 9-6: A function that returns errors to the
|
||||
calling code using `match`</span>
|
||||
|
||||
Let’s look at the return type of the function first: `Result<String,
|
||||
io::Error>`. This means the function is returning a value of the type
|
||||
`Result<T, E>` where the generic parameter `T` has been filled in with the
|
||||
concrete type `String`, and the generic type `E` has been filled in with the
|
||||
concrete type `io::Error`. If this function succeeds without any problems, the
|
||||
code that calls this function will receive an `Ok` value that holds a
|
||||
`String`—the username that this function read from the file. If this function
|
||||
encounters any problems, the code that calls this function will receive an
|
||||
`Err` value that holds an instance of `io::Error` that contains more
|
||||
information about what the problems were. We chose `io::Error` as the return
|
||||
type of this function because that happens to be the type of the error value
|
||||
returned from both of the operations we’re calling in this function’s body that
|
||||
might fail: the `File::open` function and the `read_to_string` method.
|
||||
This function can be written in a much shorter way, but we're going to start by
|
||||
doing a lot of it manually in order to explore error handling; at the end,
|
||||
we'll show the easy way. Let’s look at the return type of the function first:
|
||||
`Result<String, io::Error>`. This means the function is returning a value of
|
||||
the type `Result<T, E>` where the generic parameter `T` has been filled in
|
||||
with the concrete type `String`, and the generic type `E` has been filled in
|
||||
with the concrete type `io::Error`. If this function succeeds without any
|
||||
problems, the code that calls this function will receive an `Ok` value that
|
||||
holds a `String`—the username that this function read from the file. If this
|
||||
function encounters any problems, the code that calls this function will
|
||||
receive an `Err` value that holds an instance of `io::Error` that contains
|
||||
more information about what the problems were. We chose `io::Error` as the
|
||||
return type of this function because that happens to be the type of the error
|
||||
value returned from both of the operations we’re calling in this function’s
|
||||
body that might fail: the `File::open` function and the `read_to_string`
|
||||
method.
|
||||
|
||||
The body of the function starts by calling the `File::open` function. Then we
|
||||
handle the `Result` value returned with a `match` similar to the `match` in
|
||||
@ -379,12 +382,12 @@ The `?` placed after a `Result` value is defined to work in almost the same way
|
||||
as the `match` expressions we defined to handle the `Result` values in Listing
|
||||
9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will
|
||||
get returned from this expression, and the program will continue. If the value
|
||||
is an `Err`, the value inside the `Err` will be returned from the whole
|
||||
function as if we had used the `return` keyword so the error value gets
|
||||
propagated to the calling code.
|
||||
is an `Err`, the `Err` will be returned from the whole function as if we had
|
||||
used the `return` keyword so the error value gets propagated to the calling
|
||||
code.
|
||||
|
||||
There is a difference between what the `match` expression from Listing 9-6 and
|
||||
`?` do: error values used with `?` go through the `from` function, defined in
|
||||
`?` do: error values taken by `?` go through the `from` function, defined in
|
||||
the `From` trait in the standard library, which is used to convert errors from
|
||||
one type into another. When `?` calls the `from` function, the error type
|
||||
received is converted into the error type defined in the return type of the
|
||||
@ -431,6 +434,30 @@ username in `s` when both `File::open` and `read_to_string` succeed rather than
|
||||
returning errors. The functionality is again the same as in Listing 9-6 and
|
||||
Listing 9-7; this is just a different, more ergonomic way to write it.
|
||||
|
||||
Speaking of different ways to write this function, there's a way to make this even
|
||||
shorter:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
|
||||
fn read_username_from_file() -> Result<String, io::Error> {
|
||||
fs::read_to_string("hello.txt")
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-9: Using `fs::read_to_string`</span>
|
||||
|
||||
Reading a file into a string is a fairly common operation, and so Rust
|
||||
provides a convenience function called `fs::read_to_string` that will
|
||||
open the file, create a new `String`, read the contents of the file,
|
||||
and put the contents into that `String`, and then return it. Of course,
|
||||
this doesn't give us the opportunity to show off all of this error handling,
|
||||
so we did it the hard way at first.
|
||||
|
||||
#### The `?` Operator Can Only Be Used in Functions That Return `Result`
|
||||
|
||||
The `?` operator can only be used in functions that have a return type of
|
||||
|
||||
@ -192,7 +192,7 @@ impl Guess {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-9: A `Guess` type that will only continue with
|
||||
<span class="caption">Listing 9-10: A `Guess` type that will only continue with
|
||||
values between 1 and 100</span>
|
||||
|
||||
First, we define a struct named `Guess` that has a field named `value` that
|
||||
|
||||
@ -15,11 +15,11 @@ explore how to define your own types, functions, and methods with generics!
|
||||
|
||||
First, we’ll review how to extract a function to reduce code duplication. Next,
|
||||
we’ll use the same technique to make a generic function from two functions that
|
||||
only differ in the types of their parameters. We’ll also explain how to use
|
||||
differ only in the types of their parameters. We’ll also explain how to use
|
||||
generic types in struct and enum definitions.
|
||||
|
||||
Then you’ll learn how to use *traits* to define behavior in a generic way. You
|
||||
can then combine traits with generic types to constrain a generic type to only
|
||||
can combine traits with generic types to constrain a generic type to only
|
||||
those types that have a particular behavior, as opposed to just any type.
|
||||
|
||||
Finally, we’ll discuss *lifetimes*, a variety of generics that give the
|
||||
@ -36,7 +36,7 @@ you recognize duplicated code to extract into a function, you’ll start to
|
||||
recognize duplicated code that can use generics.
|
||||
|
||||
Consider a short program that finds the largest number in a list, as shown in
|
||||
Listing 10-1:
|
||||
Listing 10-1.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -65,13 +65,13 @@ the first number in the list in a variable named `largest`. Then it iterates
|
||||
through all the numbers in the list, and if the current number is greater than
|
||||
the number stored in `largest`, it replaces the number in that variable.
|
||||
However, if the current number is less than the largest number seen so far, the
|
||||
variable doesn’t change and the code moves on to the next number in the list.
|
||||
variable doesn’t change, and the code moves on to the next number in the list.
|
||||
After considering all the numbers in the list, `largest` should hold the
|
||||
largest number, which in this case is 100.
|
||||
|
||||
To find the largest number in two different lists of numbers, we can duplicate
|
||||
the code in Listing 10-1 and use the same logic at two different places in the
|
||||
program, as shown in Listing 10-2:
|
||||
program, as shown in Listing 10-2.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -107,7 +107,7 @@ fn main() {
|
||||
lists of numbers</span>
|
||||
|
||||
Although this code works, duplicating code is tedious and error prone. We also
|
||||
have to update the code in multiple places to change it.
|
||||
have to update the code in multiple places when we want to change it.
|
||||
|
||||
To eliminate this duplication, we can create an abstraction by defining a
|
||||
function that operates on any list of integers given to it in a parameter. This
|
||||
@ -117,7 +117,7 @@ largest number in a list abstractly.
|
||||
In Listing 10-3, we extracted the code that finds the largest number into a
|
||||
function named `largest`. Unlike the code in Listing 10-1, which can find the
|
||||
largest number in only one particular list, this program can find the largest
|
||||
number in two different lists:
|
||||
number in two different lists.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -161,7 +161,7 @@ In sum, here are the steps we took to change the code from Listing 10-2 to
|
||||
Listing 10-3:
|
||||
|
||||
1. Identify duplicate code.
|
||||
2. Extract the duplicate code into the body of the function, and specify the
|
||||
2. Extract the duplicate code into the body of the function and specify the
|
||||
inputs and return values of that code in the function signature.
|
||||
3. Update the two instances of duplicated code to call the function instead.
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ parameters and return value. Doing so makes our code more flexible and provides
|
||||
more functionality to callers of our function while preventing code duplication.
|
||||
|
||||
Continuing with our `largest` function, Listing 10-4 shows two functions that
|
||||
both find the largest value in a slice:
|
||||
both find the largest value in a slice.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -66,17 +66,17 @@ the largest `i32` in a slice. The `largest_char` function finds the largest
|
||||
the duplication by introducing a generic type parameter in a single function.
|
||||
|
||||
To parameterize the types in the new function we’ll define, we need to name the
|
||||
type parameter, just like we do for the value parameters to a function. You can
|
||||
type parameter, just as we do for the value parameters to a function. You can
|
||||
use any identifier as a type parameter name. But we’ll use `T` because, by
|
||||
convention, parameter names in Rust are short, often just a letter, and Rust’s
|
||||
type naming convention is CamelCase. Short for “type,” `T` is the default
|
||||
type-naming convention is CamelCase. Short for “type,” `T` is the default
|
||||
choice of most Rust programmers.
|
||||
|
||||
When we use a parameter in the body of the function, we have to declare the
|
||||
parameter name in the signature so that the compiler knows what that name
|
||||
means. Similarly, when we use a type parameter name in a function signature, we
|
||||
have to declare the type parameter name before we use it. To define the generic
|
||||
`largest` function, place type name declarations inside angle brackets (`<>`)
|
||||
parameter name in the signature so the compiler knows what that name means.
|
||||
Similarly, when we use a type parameter name in a function signature, we have
|
||||
to declare the type parameter name before we use it. To define the generic
|
||||
`largest` function, place type name declarations inside angle brackets, `<>`,
|
||||
between the name of the function and the parameter list, like this:
|
||||
|
||||
```rust,ignore
|
||||
@ -140,16 +140,16 @@ traits in the next section. For now, this error states that the body of
|
||||
`largest` won’t work for all possible types that `T` could be. Because we want
|
||||
to compare values of type `T` in the body, we can only use types whose values
|
||||
can be ordered. To enable comparisons, the standard library has the
|
||||
`std::cmp::PartialOrd` trait that you can implement on types (see Appendix C,
|
||||
“Derivable Traits,” for more on this trait). You’ll learn how to specify that a
|
||||
generic type has a particular trait in the “Trait Bounds” section, but let’s
|
||||
first explore other ways of using generic type parameters.
|
||||
`std::cmp::PartialOrd` trait that you can implement on types (see Appendix C
|
||||
for more on this trait). You’ll learn how to specify that a generic type has a
|
||||
particular trait in the “Trait Bounds” section, but let’s first explore other
|
||||
ways of using generic type parameters.
|
||||
|
||||
### In Struct Definitions
|
||||
|
||||
We can also define structs to use a generic type parameter in one or more
|
||||
fields using the `<>` syntax. Listing 10-6 shows how to define a `Point<T>`
|
||||
struct to hold `x` and `y` coordinate values of any type:
|
||||
struct to hold `x` and `y` coordinate values of any type.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -174,11 +174,11 @@ angle brackets just after the name of the struct. Then we can use the generic
|
||||
type in the struct definition where we would otherwise specify concrete data
|
||||
types.
|
||||
|
||||
Note that because we’ve only used one generic type to define `Point<T>`, this
|
||||
Note that because we’ve used only one generic type to define `Point<T>`, this
|
||||
definition says that the `Point<T>` struct is generic over some type `T`, and
|
||||
the fields `x` and `y` are *both* that same type, whatever that type may be. If
|
||||
we create an instance of a `Point<T>` that has values of different types, as in
|
||||
Listing 10-7, our code won’t compile:
|
||||
Listing 10-7, our code won’t compile.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -194,11 +194,11 @@ fn main() {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 10-7: The fields `x` and `y` must be the same
|
||||
type because both have the same generic data type `T`</span>
|
||||
type because both have the same generic data type `T`.</span>
|
||||
|
||||
In this example, when we assign the integer value `5` to `x`, we let the
|
||||
In this example, when we assign the integer value 5 to `x`, we let the
|
||||
compiler know that the generic type `T` will be an integer for this instance of
|
||||
`Point<T>`. Then when we specify `4.0` for `y`, which we’ve defined to have the
|
||||
`Point<T>`. Then when we specify 4.0 for `y`, which we’ve defined to have the
|
||||
same type as `x`, we’ll get a type mismatch error like this:
|
||||
|
||||
```text
|
||||
@ -216,7 +216,7 @@ floating-point variable
|
||||
To define a `Point` struct where `x` and `y` are both generics but could have
|
||||
different types, we can use multiple generic type parameters. For example, in
|
||||
Listing 10-8, we can change the definition of `Point` to be generic over types
|
||||
`T` and `U` where `x` is of type `T` and `y` is of type `U`:
|
||||
`T` and `U` where `x` is of type `T` and `y` is of type `U`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -245,7 +245,7 @@ could indicate that your code needs restructuring into smaller pieces.
|
||||
|
||||
As we did with structs, we can define enums to hold generic data types in their
|
||||
variants. Let’s take another look at the `Option<T>` enum that the standard
|
||||
library provides that we used in Chapter 6:
|
||||
library provides, which we used in Chapter 6:
|
||||
|
||||
```rust
|
||||
enum Option<T> {
|
||||
@ -276,7 +276,7 @@ The `Result` enum is generic over two types, `T` and `E`, and has two variants:
|
||||
`E`. This definition makes it convenient to use the `Result` enum anywhere we
|
||||
have an operation that might succeed (return a value of some type `T`) or fail
|
||||
(return an error of some type `E`). In fact, this is what we used to open a
|
||||
file in Listing 9-3 where `T` was filled in with the type `std::fs::File` when
|
||||
file in Listing 9-3, where `T` was filled in with the type `std::fs::File` when
|
||||
the file was opened successfully and `E` was filled in with the type
|
||||
`std::io::Error` when there were problems opening the file.
|
||||
|
||||
@ -286,9 +286,9 @@ avoid duplication by using generic types instead.
|
||||
|
||||
### In Method Definitions
|
||||
|
||||
As we did in Chapter 5, we can implement methods on structs and enums that have
|
||||
generic types in their definitions. Listing 10-9 shows the `Point<T>` struct we
|
||||
defined in Listing 10-6 with a method named `x` implemented on it:
|
||||
We can implement methods on structs and enums (as we did in Chapter 5) and use
|
||||
generic types in their definitions, too. Listing 10-9 shows the `Point<T>`
|
||||
struct we defined in Listing 10-6 with a method named `x` implemented on it.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -325,7 +325,7 @@ brackets in `Point` is a generic type rather than a concrete type.
|
||||
|
||||
We could, for example, implement methods only on `Point<f32>` instances rather
|
||||
than on `Point<T>` instances with any generic type. In Listing 10-10 we use the
|
||||
concrete type `f32`, meaning we don’t declare any types after `impl`:
|
||||
concrete type `f32`, meaning we don’t declare any types after `impl`.
|
||||
|
||||
```rust
|
||||
# struct Point<T> {
|
||||
@ -344,10 +344,10 @@ impl Point<f32> {
|
||||
struct with a particular concrete type for the generic type parameter `T`</span>
|
||||
|
||||
This code means the type `Point<f32>` will have a method named
|
||||
`distance_from_origin`, and other instances of `Point<T>` where `T` is not of
|
||||
`distance_from_origin` and other instances of `Point<T>` where `T` is not of
|
||||
type `f32` will not have this method defined. The method measures how far our
|
||||
point is from the point at coordinates (0.0, 0.0) and uses mathematical
|
||||
operations that are only available for floating point types.
|
||||
operations that are available only for floating point types.
|
||||
|
||||
Generic type parameters in a struct definition aren’t always the same as those
|
||||
you use in that struct’s method signatures. For example, Listing 10-11 defines
|
||||
@ -355,7 +355,7 @@ the method `mixup` on the `Point<T, U>` struct from Listing 10-8. The method
|
||||
takes another `Point` as a parameter, which might have different types than the
|
||||
`self` `Point` we’re calling `mixup` on. The method creates a new `Point`
|
||||
instance with the `x` value from the `self` `Point` (of type `T`) and the `y`
|
||||
value from the passed-in `Point` (of type `W`):
|
||||
value from the passed-in `Point` (of type `W`).
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -427,8 +427,8 @@ let float = Some(5.0);
|
||||
```
|
||||
|
||||
When Rust compiles this code, it performs monomorphization. During that
|
||||
process, the compiler reads the values that have been used in the instances of
|
||||
`Option<T>` and identifies two kinds of `Option<T>`: one is `i32` and the other
|
||||
process, the compiler reads the values that have been used in `Option<T>`
|
||||
instances and identifies two kinds of `Option<T>`: one is `i32` and the other
|
||||
is `f64`. As such, it expands the generic definition of `Option<T>` into
|
||||
`Option_i32` and `Option_f64`, thereby replacing the generic definition with
|
||||
the specific ones.
|
||||
@ -457,6 +457,6 @@ fn main() {
|
||||
|
||||
Because Rust compiles generic code into code that specifies the type in each
|
||||
instance, we pay no runtime cost for using generics. When the code runs, it
|
||||
performs just like it would if we had duplicated each definition by hand. The
|
||||
performs just as it would if we had duplicated each definition by hand. The
|
||||
process of monomorphization makes Rust’s generics extremely efficient at
|
||||
runtime.
|
||||
|
||||
@ -25,7 +25,7 @@ We want to make a media aggregator library that can display summaries of data
|
||||
that might be stored in a `NewsArticle` or `Tweet` instance. To do this, we
|
||||
need a summary from each type, and we need to request that summary by calling a
|
||||
`summarize` method on an instance. Listing 10-12 shows the definition of a
|
||||
`Summary` trait that expresses this behavior:
|
||||
`Summary` trait that expresses this behavior.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -39,7 +39,7 @@ pub trait Summary {
|
||||
behavior provided by a `summarize` method</span>
|
||||
|
||||
Here, we declare a trait using the `trait` keyword and then the trait’s name,
|
||||
which is `Summary` in this case. Inside the curly brackets we declare the
|
||||
which is `Summary` in this case. Inside the curly brackets, we declare the
|
||||
method signatures that describe the behaviors of the types that implement this
|
||||
trait, which in this case is `fn summarize(&self) -> String`.
|
||||
|
||||
@ -127,14 +127,14 @@ know, people`.
|
||||
|
||||
Note that because we defined the `Summary` trait and the `NewsArticle` and
|
||||
`Tweet` types in the same *lib.rs* in Listing 10-13, they’re all in the same
|
||||
scope. Let’s say this *lib.rs* is for a crate we’ve called `aggregator`, and
|
||||
scope. Let’s say this *lib.rs* is for a crate we’ve called `aggregator` and
|
||||
someone else wants to use our crate’s functionality to implement the `Summary`
|
||||
trait on a struct defined within their library’s scope. They would need to
|
||||
import the trait into their scope first. They would do so by specifying `use
|
||||
aggregator::Summary;`, which then enables them to implement `Summary` for their
|
||||
type. The `Summary` trait would also need to be a public trait for another
|
||||
crate to implement it, which it is because we put the `pub` keyword before
|
||||
`trait` in Listing 10-12.
|
||||
aggregator::Summary;`, which then would enable them to implement `Summary` for
|
||||
their type. The `Summary` trait would also need to be a public trait for
|
||||
another crate to implement it, which it is because we put the `pub` keyword
|
||||
before `trait` in Listing 10-12.
|
||||
|
||||
One restriction to note with trait implementations is that we can implement a
|
||||
trait on a type only if either the trait or the type is local to our crate.
|
||||
@ -162,8 +162,8 @@ Then, as we implement the trait on a particular type, we can keep or override
|
||||
each method’s default behavior.
|
||||
|
||||
Listing 10-14 shows how to specify a default string for the `summarize` method
|
||||
of the `Summary` trait instead of only defining the method signature, like we
|
||||
did in Listing 10-12:
|
||||
of the `Summary` trait instead of only defining the method signature, as we did
|
||||
in Listing 10-12.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -258,20 +258,29 @@ This code prints `1 new tweet: (Read more from @horse_ebooks...)`.
|
||||
Note that it isn’t possible to call the default implementation from an
|
||||
overriding implementation of that same method.
|
||||
|
||||
### Trait Bounds
|
||||
### Traits as arguments
|
||||
|
||||
Now that you know how to define traits and implement those traits on types, we
|
||||
can explore how to use traits with generic type parameters. We can use *trait
|
||||
bounds* to constrain generic types to ensure the type will be limited to those
|
||||
that implement a particular trait and behavior.
|
||||
can explore how to use traits to accept arguments of many different types.
|
||||
|
||||
For example, in Listing 10-13, we implemented the `Summary` trait on the types
|
||||
`NewsArticle` and `Tweet`. We can define a function `notify` that calls the
|
||||
`summarize` method on its parameter `item`, which is of the generic type `T`.
|
||||
To be able to call `summarize` on `item` without getting an error telling us
|
||||
that the generic type `T` doesn’t implement the method `summarize`, we can use
|
||||
trait bounds on `T` to specify that `item` must be of a type that implements
|
||||
the `Summary` trait:
|
||||
`summarize` method on its parameter `item`, which is of some type that implements
|
||||
the `Summary` trait. To do this, we can use the '`impl Trait`' syntax, like this:
|
||||
|
||||
```rust,ignore
|
||||
pub fn notify(item: impl Summary) {
|
||||
println!("Breaking news! {}", item.summarize());
|
||||
}
|
||||
```
|
||||
|
||||
In the body of `notify`, we can call any methods on `item` that come from
|
||||
the `Summary` trait, like `summarize`.
|
||||
|
||||
#### Trait Bounds
|
||||
|
||||
The `impl Trait` syntax works for short examples, but is syntax sugar for a
|
||||
longer form. This is called a 'trait bound', and it looks like this:
|
||||
|
||||
```rust,ignore
|
||||
pub fn notify<T: Summary>(item: T) {
|
||||
@ -279,28 +288,45 @@ pub fn notify<T: Summary>(item: T) {
|
||||
}
|
||||
```
|
||||
|
||||
We place trait bounds with the declaration of the generic type parameter, after
|
||||
a colon and inside angle brackets. Because of the trait bound on `T`, we can
|
||||
This is equivalent to the example above, but is a bit more verbose. We place
|
||||
trait bounds with the declaration of the generic type parameter, after a
|
||||
colon and inside angle brackets. Because of the trait bound on `T`, we can
|
||||
call `notify` and pass in any instance of `NewsArticle` or `Tweet`. Code that
|
||||
calls the function with any other type, like a `String` or an `i32`, won’t
|
||||
compile, because those types don’t implement `Summary`.
|
||||
|
||||
When should you use this form over `impl Trait`? While `impl Trait` is nice for
|
||||
shorter examples, trait bounds are nice for more complex ones. For example,
|
||||
say we wanted to take two things that implement `Summary`:
|
||||
|
||||
```rust,ignore
|
||||
pub fn notify(item1: impl Summary, item2: impl Summary) {
|
||||
pub fn notify<T: Summary>(item1: T, item2: T) {
|
||||
```
|
||||
|
||||
The version with the bound is a bit easier. In general, you should use whatever
|
||||
form makes your code the most understandable.
|
||||
|
||||
##### Multiple trait bounds with `+`
|
||||
|
||||
We can specify multiple trait bounds on a generic type using the `+` syntax.
|
||||
For example, to use display formatting on the type `T` in a function as well as
|
||||
the `summarize` method, we can use `T: Summary + Display` to say `T` can be any
|
||||
type that implements `Summary` and `Display`.
|
||||
|
||||
However, there are downsides to using too many trait bounds. Each generic has
|
||||
its own trait bounds; so functions with multiple generic type parameters can
|
||||
have lots of trait bound information between a function’s name and its
|
||||
parameter list, making the function signature hard to read. For this reason,
|
||||
Rust has alternate syntax for specifying trait bounds inside a `where` clause
|
||||
after the function signature. So instead of writing this:
|
||||
type that implements `Summary` and `Display`. This can grow quite complex!
|
||||
|
||||
```rust,ignore
|
||||
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {
|
||||
```
|
||||
|
||||
#### `where` clauses for clearer code
|
||||
|
||||
However, there are downsides to using too many trait bounds. Each generic has
|
||||
its own trait bounds, so functions with multiple generic type parameters can
|
||||
have lots of trait bound information between a function’s name and its
|
||||
parameter list, making the function signature hard to read. For this reason,
|
||||
Rust has alternate syntax for specifying trait bounds inside a `where` clause
|
||||
after the function signature. So instead of writing this:
|
||||
|
||||
we can use a `where` clause, like this:
|
||||
|
||||
```rust,ignore
|
||||
@ -314,6 +340,60 @@ This function’s signature is less cluttered in that the function name,
|
||||
parameter list, and return type are close together, similar to a function
|
||||
without lots of trait bounds.
|
||||
|
||||
### Returning Traits
|
||||
|
||||
We can use the `impl Trait` syntax in return position as well, to return
|
||||
something that implements a trait:
|
||||
|
||||
```rust,ignore
|
||||
fn returns_summarizable() -> impl Summary {
|
||||
Tweet {
|
||||
username: String::from("horse_ebooks"),
|
||||
content: String::from("of course, as you probably already know, people"),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This signature says, "I'm going to return something that implements the
|
||||
`Summary` trait, but I'm not going to tell you the exact type. In our case,
|
||||
we're returning a `Tweet`, but the caller doesn't know that.
|
||||
|
||||
Why is this useful? In chapter 13, we're going to learn about two features
|
||||
that rely heavily on traits: closures, and iterators. These features create
|
||||
types that only the compiler knows, or types that are very, very long.
|
||||
`impl Trait` lets you simply say "this returns an `Iterator`" without
|
||||
needing to write out a really long type.
|
||||
|
||||
This only works if you have a single type that you're returning, however.
|
||||
For example, this would *not* work:
|
||||
|
||||
```rust,ignore
|
||||
fn returns_summarizable(switch: bool) -> impl Summary {
|
||||
if switch {
|
||||
NewsArticle {
|
||||
headline: String::from("Penguins win the Stanley Cup Championship!"),
|
||||
location: String::from("Pittsburgh, PA, USA"),
|
||||
author: String::from("Iceburgh"),
|
||||
content: String::from("The Pittsburgh Penguins once again are the best
|
||||
hockey team in the NHL."),
|
||||
}
|
||||
} else {
|
||||
Tweet {
|
||||
username: String::from("horse_ebooks"),
|
||||
content: String::from("of course, as you probably already know, people"),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, we try to return either a `NewsArticle` or a `Tweet`. This cannot work,
|
||||
due to restrictions around how `impl Trait` works. To write this code, you'll
|
||||
have to wait until Chapter 17, "trait objects".
|
||||
|
||||
### Fixing the `largest` Function with Trait Bounds
|
||||
|
||||
Now that you know how to specify the behavior you want to use using the generic
|
||||
@ -332,7 +412,7 @@ error[E0369]: binary operation `>` cannot be applied to type `T`
|
||||
```
|
||||
|
||||
In the body of `largest` we wanted to compare two values of type `T` using the
|
||||
greater-than (`>`) operator. Because that operator is defined as a default
|
||||
greater than (`>`) operator. Because that operator is defined as a default
|
||||
method on the standard library trait `std::cmp::PartialOrd`, we need to specify
|
||||
`PartialOrd` in the trait bounds for `T` so the `largest` function can work on
|
||||
slices of any type that we can compare. We don’t need to bring `PartialOrd`
|
||||
@ -379,7 +459,7 @@ To call this code with only those types that implement the `Copy` trait, we can
|
||||
add `Copy` to the trait bounds of `T`! Listing 10-15 shows the complete code of
|
||||
a generic `largest` function that will compile as long as the types of the
|
||||
values in the slice that we pass into the function implement the `PartialOrd`
|
||||
*and* `Copy` traits, like `i32` and `char` do:
|
||||
*and* `Copy` traits, like `i32` and `char` do.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -434,7 +514,7 @@ we can implement methods conditionally for types that implement the specified
|
||||
traits. For example, the type `Pair<T>` in Listing 10-16 always implements the
|
||||
`new` function. But `Pair<T>` only implements the `cmp_display` method if its
|
||||
inner type `T` implements the `PartialOrd` trait that enables comparison *and*
|
||||
the `Display` trait that enables printing:
|
||||
the `Display` trait that enables printing.
|
||||
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
One detail we didn’t discuss in the “References and Borrowing” section in
|
||||
Chapter 4 is that every reference in Rust has a *lifetime*, which is the scope
|
||||
for which that reference is valid. Most of the time lifetimes are implicit and
|
||||
inferred, just like most of the time types are inferred. We must annotate types
|
||||
for which that reference is valid. Most of the time, lifetimes are implicit and
|
||||
inferred, just like most of the time, types are inferred. We must annotate types
|
||||
when multiple types are possible. In a similar way, we must annotate lifetimes
|
||||
when the lifetimes of references could be related in a few different ways. Rust
|
||||
requires us to annotate the relationships using generic lifetime parameters to
|
||||
@ -16,12 +16,12 @@ common ways you might encounter lifetime syntax so you can become familiar with
|
||||
the concepts. See the “Advanced Lifetimes” section in Chapter 19 for more
|
||||
detailed information.
|
||||
|
||||
### Lifetimes Prevent Dangling References
|
||||
### Preventing Dangling References with Lifetimes
|
||||
|
||||
The main aim of lifetimes is to prevent dangling references, which cause a
|
||||
program to reference data other than the data it’s intended to reference.
|
||||
Consider the program in Listing 10-17, which has an outer scope and an inner
|
||||
scope:
|
||||
scope.
|
||||
|
||||
```rust,ignore
|
||||
{
|
||||
@ -39,15 +39,15 @@ scope:
|
||||
<span class="caption">Listing 10-17: An attempt to use a reference whose value
|
||||
has gone out of scope</span>
|
||||
|
||||
> Note: The example in Listing 10-17 and the next few examples declare
|
||||
> variables without giving them an initial value, so the variable name exists
|
||||
> in the outer scope. At first glance, this might appear to be in conflict with
|
||||
> Rust having no null values. However, if we try to use a variable before
|
||||
> giving it a value, we’ll get a compile time error, which shows that Rust
|
||||
> indeed does not allow null values.
|
||||
> Note: The examples in Listings 10-17, 10-18, and 10-24 declare variables
|
||||
> without giving them an initial value, so the variable name exists in the
|
||||
> outer scope. At first glance, this might appear to be in conflict with Rust’s
|
||||
> having no null values. However, if we try to use a variable before giving it
|
||||
> a value, we’ll get a compile-time error, which shows that Rust indeed does
|
||||
> not allow null values.
|
||||
|
||||
The outer scope declares a variable named `r` with no initial value, and the
|
||||
inner scope declares a variable named `x` with the initial value of `5`. Inside
|
||||
inner scope declares a variable named `x` with the initial value of 5. Inside
|
||||
the inner scope, we attempt to set the value of `r` as a reference to `x`. Then
|
||||
the inner scope ends, and we attempt to print the value in `r`. This code won’t
|
||||
compile because the value `r` is referring to has gone out of scope before we
|
||||
@ -76,9 +76,9 @@ It uses a borrow checker.
|
||||
|
||||
### The Borrow Checker
|
||||
|
||||
The Rust compiler has a *borrow checker* that compares scopes to determine that
|
||||
all borrows are valid. Listing 10-18 shows the same code as Listing 10-17 but
|
||||
with annotations showing the lifetimes of the variables:
|
||||
The Rust compiler has a *borrow checker* that compares scopes to determine
|
||||
whether all borrows are valid. Listing 10-18 shows the same code as Listing
|
||||
10-17 but with annotations showing the lifetimes of the variables.
|
||||
|
||||
```rust,ignore
|
||||
{
|
||||
@ -104,7 +104,7 @@ with a lifetime of `'b`. The program is rejected because `'b` is shorter than
|
||||
`'a`: the subject of the reference doesn’t live as long as the reference.
|
||||
|
||||
Listing 10-19 fixes the code so it doesn’t have a dangling reference and
|
||||
compiles without any errors:
|
||||
compiles without any errors.
|
||||
|
||||
```rust
|
||||
{
|
||||
@ -133,7 +133,7 @@ lifetimes of parameters and return values in the context of functions.
|
||||
Let’s write a function that returns the longer of two string slices. This
|
||||
function will take two string slices and return a string slice. After we’ve
|
||||
implemented the `longest` function, the code in Listing 10-20 should print `The
|
||||
longest string is abcd`:
|
||||
longest string is abcd`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -148,7 +148,7 @@ fn main() {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 10-20: A `main` function that calls the `longest`
|
||||
function to find the longest of two string slices</span>
|
||||
function to find the longer of two string slices</span>
|
||||
|
||||
Note that we want the function to take string slices, which are references,
|
||||
because we don’t want the `longest` function to take ownership of its
|
||||
@ -161,7 +161,7 @@ discussion about why the parameters we use in Listing 10-20 are the ones we
|
||||
want.
|
||||
|
||||
If we try to implement the `longest` function as shown in Listing 10-21, it
|
||||
won’t compile:
|
||||
won’t compile.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -201,8 +201,8 @@ reference to `y`!
|
||||
When we’re defining this function, we don’t know the concrete values that will
|
||||
be passed into this function, so we don’t know whether the `if` case or the
|
||||
`else` case will execute. We also don’t know the concrete lifetimes of the
|
||||
references that will be passed in, so we can’t look at the scopes like we did
|
||||
in Listings 10-18 and 10-19 to determine that the reference we return will
|
||||
references that will be passed in, so we can’t look at the scopes as we did in
|
||||
Listings 10-18 and 10-19 to determine whether the reference we return will
|
||||
always be valid. The borrow checker can’t determine this either, because it
|
||||
doesn’t know how the lifetimes of `x` and `y` relate to the lifetime of the
|
||||
return value. To fix this error, we’ll add generic lifetime parameters that
|
||||
@ -212,14 +212,14 @@ perform its analysis.
|
||||
### Lifetime Annotation Syntax
|
||||
|
||||
Lifetime annotations don’t change how long any of the references live. Just
|
||||
like functions can accept any type when the signature specifies a generic type
|
||||
as functions can accept any type when the signature specifies a generic type
|
||||
parameter, functions can accept references with any lifetime by specifying a
|
||||
generic lifetime parameter. Lifetime annotations describe the relationships of
|
||||
the lifetimes of multiple references to each other without affecting the
|
||||
lifetimes.
|
||||
|
||||
Lifetime annotations have a slightly unusual syntax: the names of lifetime
|
||||
parameters must start with an apostrophe `'` and are usually all lowercase and
|
||||
parameters must start with an apostrophe (`'`) and are usually all lowercase and
|
||||
very short, like generic types. Most people use the name `'a`. We place
|
||||
lifetime parameter annotations after the `&` of a reference, using a space to
|
||||
separate the annotation from the reference’s type.
|
||||
@ -234,7 +234,7 @@ reference to an `i32` that also has the lifetime `'a`.
|
||||
&'a mut i32 // a mutable reference with an explicit lifetime
|
||||
```
|
||||
|
||||
One lifetime annotation by itself doesn’t have much meaning because the
|
||||
One lifetime annotation by itself doesn’t have much meaning, because the
|
||||
annotations are meant to tell Rust how generic lifetime parameters of multiple
|
||||
references relate to each other. For example, let’s say we have a function with
|
||||
the parameter `first` that is a reference to an `i32` with lifetime `'a`. The
|
||||
@ -250,8 +250,8 @@ function. As with generic type parameters, we need to declare generic lifetime
|
||||
parameters inside angle brackets between the function name and the parameter
|
||||
list. The constraint we want to express in this signature is that all the
|
||||
references in the parameters and the return value must have the same lifetime.
|
||||
We’ll name the lifetime `'a`, and then add it to each reference, as shown in
|
||||
Listing 10-22:
|
||||
We’ll name the lifetime `'a` and then add it to each reference, as shown in
|
||||
Listing 10-22.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -302,7 +302,7 @@ lifetimes of `x` and `y`.
|
||||
|
||||
Let’s look at how the lifetime annotations restrict the `longest` function by
|
||||
passing in references that have different concrete lifetimes. Listing 10-23 is
|
||||
a straightforward example:
|
||||
a straightforward example.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -341,7 +341,7 @@ declaration of the `result` variable outside the inner scope but leave the
|
||||
assignment of the value to the `result` variable inside the scope with
|
||||
`string2`. Then we’ll move the `println!` that uses `result` outside the inner
|
||||
scope, after the inner scope has ended. The code in Listing 10-24 will not
|
||||
compile:
|
||||
compile.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -358,7 +358,7 @@ fn main() {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 10-24: Attempting to use `result` after `string2`
|
||||
has gone out of scope; the code won’t compile</span>
|
||||
has gone out of scope</span>
|
||||
|
||||
When we try to compile this code, we’ll get this error:
|
||||
|
||||
@ -381,7 +381,7 @@ this because we annotated the lifetimes of the function parameters and return
|
||||
values using the same lifetime parameter `'a`.
|
||||
|
||||
As humans, we can look at this code and see that `string1` is longer than
|
||||
`string2`, and therefore `result` will contain a reference to `string1`.
|
||||
`string2` and therefore `result` will contain a reference to `string1`.
|
||||
Because `string1` has not gone out of scope yet, a reference to `string1` will
|
||||
still be valid for the `println!` statement. However, the compiler can’t see
|
||||
that the reference is valid in this case. We’ve told Rust that the lifetime of
|
||||
@ -473,7 +473,7 @@ would create dangling pointers or otherwise violate memory safety.
|
||||
So far, we’ve only defined structs to hold owned types. It’s possible for
|
||||
structs to hold references, but in that case we would need to add a lifetime
|
||||
annotation on every reference in the struct’s definition. Listing 10-25 has a
|
||||
struct named `ImportantExcerpt` that holds a string slice:
|
||||
struct named `ImportantExcerpt` that holds a string slice.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -512,8 +512,8 @@ the `ImportantExcerpt` goes out of scope, so the reference in the
|
||||
|
||||
You’ve learned that every reference has a lifetime and that you need to specify
|
||||
lifetime parameters for functions or structs that use references. However, in
|
||||
Chapter 4 we had a function in the “String Slices” section, which is shown again
|
||||
in Listing 10-26, that compiled without lifetime annotations:
|
||||
Chapter 4 we had a function in Listing 4-9, which is shown again in Listing
|
||||
10-26, that compiled without lifetime annotations.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -531,7 +531,7 @@ fn first_word(s: &str) -> &str {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 10-26: A function we defined in Chapter 4 that
|
||||
<span class="caption">Listing 10-26: A function we defined in Listing 4-9 that
|
||||
compiled without lifetime annotations, even though the parameter and return
|
||||
type are references</span>
|
||||
|
||||
@ -548,8 +548,8 @@ After writing a lot of Rust code, the Rust team found that Rust programmers
|
||||
were entering the same lifetime annotations over and over in particular
|
||||
situations. These situations were predictable and followed a few deterministic
|
||||
patterns. The developers programmed these patterns into the compiler’s code so
|
||||
the borrow checker could infer the lifetimes in these situations and not need
|
||||
explicit annotations.
|
||||
the borrow checker could infer the lifetimes in these situations and wouldn’t
|
||||
need explicit annotations.
|
||||
|
||||
This piece of Rust history is relevant because it’s possible that more
|
||||
deterministic patterns will emerge and be added to the compiler. In the future,
|
||||
@ -601,7 +601,8 @@ fn first_word(s: &str) -> &str {
|
||||
```
|
||||
|
||||
Then the compiler applies the first rule, which specifies that each parameter
|
||||
gets its own lifetime. We’ll call it `'a` as usual, so now the signature is:
|
||||
gets its own lifetime. We’ll call it `'a` as usual, so now the signature is
|
||||
this:
|
||||
|
||||
```rust,ignore
|
||||
fn first_word<'a>(s: &'a str) -> &str {
|
||||
@ -705,8 +706,11 @@ and all lifetimes have been accounted for.
|
||||
|
||||
One special lifetime we need to discuss is `'static`, which denotes the entire
|
||||
duration of the program. All string literals have the `'static` lifetime, which
|
||||
we can annotate as follows: `let s: &'static str = "I have a static
|
||||
lifetime.";`.
|
||||
we can annotate as follows:
|
||||
|
||||
```rust
|
||||
let s: &'static str = "I have a static lifetime.";
|
||||
```
|
||||
|
||||
The text of this string is stored directly in the binary of your program, which
|
||||
is always available. Therefore, the lifetime of all string literals is
|
||||
@ -763,6 +767,5 @@ analysis happens at compile time, which doesn’t affect runtime performance!
|
||||
Believe it or not, there is much more to learn on the topics we discussed in
|
||||
this chapter: Chapter 17 discusses trait objects, which are another way to use
|
||||
traits. Chapter 19 covers more complex scenarios involving lifetime annotations
|
||||
as well as some advanced type system features. But in the next chapter, you’ll
|
||||
learn how to write tests in Rust so you can make sure your code is working the
|
||||
way it should.
|
||||
as well as some advanced type system features. But next, you’ll learn how to
|
||||
write tests in Rust so you can make sure your code is working the way it should.
|
||||
|
||||
@ -5,7 +5,7 @@ Let’s create a new project with, as always, `cargo new`. We’ll call our proj
|
||||
on your system.
|
||||
|
||||
```text
|
||||
$ cargo new --bin minigrep
|
||||
$ cargo new minigrep
|
||||
Created binary (application) `minigrep` project
|
||||
$ cd minigrep
|
||||
```
|
||||
|
||||
@ -32,7 +32,7 @@ shown in Listing 12-4:
|
||||
|
||||
```rust,should_panic
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::fs;
|
||||
use std::io::prelude::*;
|
||||
|
||||
fn main() {
|
||||
@ -45,11 +45,8 @@ fn main() {
|
||||
// --snip--
|
||||
println!("In file {}", filename);
|
||||
|
||||
let mut f = File::open(filename).expect("file not found");
|
||||
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)
|
||||
.expect("something went wrong reading the file");
|
||||
let contents = fs::read_to_string(filename)
|
||||
.expect("Something went wrong reading the file");
|
||||
|
||||
println!("With text:\n{}", contents);
|
||||
}
|
||||
@ -59,22 +56,18 @@ fn main() {
|
||||
by the second argument</span>
|
||||
|
||||
First, we add some more `use` statements to bring in relevant parts of the
|
||||
standard library: we need `std::fs::File` to handle files, and
|
||||
standard library: we need `std::fs` to handle files, and
|
||||
`std::io::prelude::*` contains various useful traits for doing I/O, including
|
||||
file I/O. In the same way that Rust has a general prelude that brings certain
|
||||
types and functions into scope automatically, the `std::io` module has its own
|
||||
prelude of common types and functions you’ll need when working with I/O. Unlike
|
||||
with the default prelude, we must explicitly add a `use` statement for the
|
||||
prelude from `std::io`.
|
||||
types and functions into scope automatically, the `std::io` module has its
|
||||
own prelude of common types and functions you’ll need when working with I/O.
|
||||
Unlike with the default prelude, we must explicitly add a `use` statement for
|
||||
the prelude from `std::io`.
|
||||
|
||||
In `main`, we’ve added three statements: first, we get a mutable handle to the
|
||||
file by calling the `File::open` function and passing it the value of the
|
||||
`filename` variable. Second, we create a variable called `contents` and set it
|
||||
to a mutable, empty `String`. This will hold the content of the file after we
|
||||
read it in. Third, we call `read_to_string` on our file handle and pass a
|
||||
mutable reference to `contents` as an argument.
|
||||
In `main`, we’ve added a new statement: `fs::read_to_string` will take the
|
||||
`filename`, open that file, and then produce a new `String` with its contents.
|
||||
|
||||
After those lines, we’ve again added a temporary `println!` statement that
|
||||
After that lines, we’ve again added a temporary `println!` statement that
|
||||
prints the value of `contents` after the file is read, so we can check that the
|
||||
program is working so far.
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ the struct fields rather than having separate variables:
|
||||
|
||||
```rust,should_panic
|
||||
# use std::env;
|
||||
# use std::fs::File;
|
||||
# use std::fs;
|
||||
#
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
@ -146,7 +146,8 @@ fn main() {
|
||||
println!("Searching for {}", config.query);
|
||||
println!("In file {}", config.filename);
|
||||
|
||||
let mut f = File::open(config.filename).expect("file not found");
|
||||
let contents = fs::read_to_string(config.filename)
|
||||
.expect("Something went wrong reading the file");
|
||||
|
||||
// --snip--
|
||||
}
|
||||
@ -466,10 +467,7 @@ fn main() {
|
||||
}
|
||||
|
||||
fn run(config: Config) {
|
||||
let mut f = File::open(config.filename).expect("file not found");
|
||||
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)
|
||||
let contents = fs::read_to_string(config.filename)
|
||||
.expect("something went wrong reading the file");
|
||||
|
||||
println!("With text:\n{}", contents);
|
||||
@ -502,11 +500,8 @@ use std::error::Error;
|
||||
|
||||
// --snip--
|
||||
|
||||
fn run(config: Config) -> Result<(), Box<Error>> {
|
||||
let mut f = File::open(config.filename)?;
|
||||
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
fn run(config: Config) -> Result<(), Box<dyn Error>> {
|
||||
let contents = fs::read_to_string(config.filename)?;
|
||||
|
||||
println!("With text:\n{}", contents);
|
||||
|
||||
@ -518,19 +513,20 @@ fn run(config: Config) -> Result<(), Box<Error>> {
|
||||
`Result`</span>
|
||||
|
||||
We’ve made three significant changes here. First, we changed the return type of
|
||||
the `run` function to `Result<(), Box<Error>>`. This function previously
|
||||
the `run` function to `Result<(), Box<dyn Error>>`. This function previously
|
||||
returned the unit type, `()`, and we keep that as the value returned in the
|
||||
`Ok` case.
|
||||
|
||||
For the error type, we used the *trait object* `Box<Error>` (and we’ve brought
|
||||
`std::error::Error` into scope with a `use` statement at the top). We’ll cover
|
||||
trait objects in Chapter 17. For now, just know that `Box<Error>` means the
|
||||
function will return a type that implements the `Error` trait, but we don’t
|
||||
have to specify what particular type the return value will be. This gives us
|
||||
flexibility to return error values that may be of different types in different
|
||||
error cases.
|
||||
For the error type, we used the *trait object* `Box<dyn Error>` (and we’ve
|
||||
brought `std::error::Error` into scope with a `use` statement at the top).
|
||||
We’ll cover trait objects in Chapter 17. For now, just know that `Box<dyn
|
||||
Error>` means the function will return a type that implements the `Error`
|
||||
trait, but we don’t have to specify what particular type the return value
|
||||
will be. This gives us flexibility to return error values that may be of
|
||||
different types in different error cases. This is what the `dyn` means, it's
|
||||
short for "dynamic."
|
||||
|
||||
Second, we’ve removed the calls to `expect` in favor of `?`, as we talked about
|
||||
Second, we’ve removed the call to `expect` in favor of `?`, as we talked about
|
||||
in Chapter 9. Rather than `panic!` on an error, `?` will return the error value
|
||||
from the current function for the caller to handle.
|
||||
|
||||
@ -625,7 +621,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(config: Config) -> Result<(), Box<Error>> {
|
||||
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -276,11 +276,10 @@ will print each line returned from `search`:
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
pub fn run(config: Config) -> Result<(), Box<Error>> {
|
||||
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
|
||||
let mut f = File::open(config.filename)?;
|
||||
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
let contents = fs::read_to_string(config.filename)?;
|
||||
|
||||
for line in search(&config.query, &contents) {
|
||||
println!("{}", line);
|
||||
|
||||
@ -155,7 +155,7 @@ won’t compile yet:
|
||||
|
||||
```rust
|
||||
# use std::error::Error;
|
||||
# use std::fs::File;
|
||||
# use std::fs::{self, File};
|
||||
# use std::io::prelude::*;
|
||||
#
|
||||
# fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
|
||||
@ -166,17 +166,14 @@ won’t compile yet:
|
||||
# vec![]
|
||||
# }
|
||||
#
|
||||
# struct Config {
|
||||
# pub struct Config {
|
||||
# query: String,
|
||||
# filename: String,
|
||||
# case_sensitive: bool,
|
||||
# }
|
||||
#
|
||||
pub fn run(config: Config) -> Result<(), Box<Error>> {
|
||||
let mut f = File::open(config.filename)?;
|
||||
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
|
||||
let contents = fs::read_to_string(config.filename)?;
|
||||
|
||||
let results = if config.case_sensitive {
|
||||
search(&config.query, &contents)
|
||||
|
||||
@ -469,10 +469,10 @@ closure we want to store in the `calculation` field must have one `u32`
|
||||
parameter (specified within the parentheses after `Fn`) and must return a
|
||||
`u32` (specified after the `->`).
|
||||
|
||||
> Note: Functions implement all three of the `Fn` traits too. If what we want
|
||||
> to do doesn’t require capturing a value from the environment, we can use a
|
||||
> function rather than a closure where we need something that implements an `Fn`
|
||||
> trait.
|
||||
> Note: Functions can implement all three of the `Fn` traits too. If what we
|
||||
> want to do doesn’t require capturing a value from the environment, we can use
|
||||
> a function rather than a closure where we need something that implements an
|
||||
> `Fn` trait.
|
||||
|
||||
The `value` field is of type `Option<u32>`. Before we execute the closure,
|
||||
`value` will be `None`. When code using a `Cacher` asks for the *result* of the
|
||||
|
||||
@ -44,7 +44,7 @@ Next, we’ll create the `adder` binary crate by running `cargo new` within the
|
||||
*add* directory:
|
||||
|
||||
```text
|
||||
$ cargo new --bin adder
|
||||
$ cargo new adder
|
||||
Created binary (application) `adder` project
|
||||
```
|
||||
|
||||
@ -92,7 +92,7 @@ members = [
|
||||
Then generate a new library crate named `add-one`:
|
||||
|
||||
```text
|
||||
$ cargo new add-one
|
||||
$ cargo new add-one --lib
|
||||
Created library `add-one` project
|
||||
```
|
||||
|
||||
|
||||
@ -14,6 +14,11 @@ smart pointers to work in a similar way as references. Then we’ll look at
|
||||
Rust’s *deref coercion* feature and how it lets us work with either references
|
||||
or smart pointers.
|
||||
|
||||
> There's one big difference between the `MyBox<T>` type we're about to build
|
||||
> and the real `Box<T>`: our version will not store its data on the heap. We
|
||||
> are focusing this example on `Deref`, and so where the data is actually stored
|
||||
> is less important than the pointer-like behavior.
|
||||
|
||||
### Following the Pointer to the Value with the Dereference Operator
|
||||
|
||||
A regular reference is a type of pointer, and one way to think of a pointer is
|
||||
|
||||
@ -43,12 +43,13 @@ To implement the behavior we want `gui` to have, we’ll define a trait named
|
||||
takes a *trait object*. A trait object points to an instance of a type that
|
||||
implements the trait we specify. We create a trait object by specifying some
|
||||
sort of pointer, such as a `&` reference or a `Box<T>` smart pointer, and then
|
||||
specifying the relevant trait. (We’ll talk about the reason trait objects must
|
||||
use a pointer in Chapter 19 in the section “Dynamically Sized Types & Sized”.)
|
||||
We can use trait objects in place of a generic or concrete type. Wherever we
|
||||
use a trait object, Rust’s type system will ensure at compile time that any
|
||||
value used in that context will implement the trait object’s trait.
|
||||
Consequently, we don’t need to know all the possible types at compile time.
|
||||
specifying the relevant trait, and add a `dyn` keyword. (We’ll talk about the
|
||||
reason trait objects must use a pointer in Chapter 19 in the section
|
||||
“Dynamically Sized Types & Sized”.) We can use trait objects in place of a
|
||||
generic or concrete type. Wherever we use a trait object, Rust’s type system
|
||||
will ensure at compile time that any value used in that context will
|
||||
implement the trait object’s trait. Consequently, we don’t need to know all
|
||||
the possible types at compile time.
|
||||
|
||||
We’ve mentioned that in Rust, we refrain from calling structs and enums
|
||||
“objects” to distinguish them from other languages’ objects. In a struct or
|
||||
@ -77,8 +78,8 @@ pub trait Draw {
|
||||
This syntax should look familiar from our discussions on how to define traits
|
||||
in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named
|
||||
`Screen` that holds a vector named `components`. This vector is of type
|
||||
`Box<Draw>`, which is a trait object; it’s a stand-in for any type inside a
|
||||
`Box` that implements the `Draw` trait.
|
||||
`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside
|
||||
a `Box` that implements the `Draw` trait.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -88,7 +89,7 @@ in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named
|
||||
# }
|
||||
#
|
||||
pub struct Screen {
|
||||
pub components: Vec<Box<Draw>>,
|
||||
pub components: Vec<Box<dyn Draw>>,
|
||||
}
|
||||
```
|
||||
|
||||
@ -107,7 +108,7 @@ On the `Screen` struct, we’ll define a method named `run` that will call the
|
||||
# }
|
||||
#
|
||||
# pub struct Screen {
|
||||
# pub components: Vec<Box<Draw>>,
|
||||
# pub components: Vec<Box<dyn Draw>>,
|
||||
# }
|
||||
#
|
||||
impl Screen {
|
||||
@ -280,8 +281,8 @@ then it must be a duck! In the implementation of `run` on `Screen` in Listing
|
||||
17-5, `run` doesn’t need to know what the concrete type of each component is.
|
||||
It doesn’t check whether a component is an instance of a `Button` or a
|
||||
`SelectBox`, it just calls the `draw` method on the component. By specifying
|
||||
`Box<Draw>` as the type of the values in the `components` vector, we’ve defined
|
||||
`Screen` to need values that we can call the `draw` method on.
|
||||
`Box<dyn Draw>` as the type of the values in the `components` vector, we’ve
|
||||
defined `Screen` to need values that we can call the `draw` method on.
|
||||
|
||||
The advantage of using trait objects and Rust’s type system to write code
|
||||
similar to code using duck typing is that we never have to check whether a
|
||||
@ -397,7 +398,7 @@ implement the `Clone` trait instead of the `Draw` trait, like this:
|
||||
|
||||
```rust,ignore
|
||||
pub struct Screen {
|
||||
pub components: Vec<Box<Clone>>,
|
||||
pub components: Vec<Box<dyn Clone>>,
|
||||
}
|
||||
```
|
||||
|
||||
@ -407,7 +408,7 @@ We would get this error:
|
||||
error[E0038]: the trait `std::clone::Clone` cannot be made into an object
|
||||
--> src/lib.rs:2:5
|
||||
|
|
||||
2 | pub components: Vec<Box<Clone>>,
|
||||
2 | pub components: Vec<Box<dyn Clone>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` cannot be
|
||||
made into an object
|
||||
|
|
||||
|
||||
@ -83,15 +83,15 @@ Let’s get started on the implementation of the library! We know we need a
|
||||
public `Post` struct that holds some content, so we’ll start with the
|
||||
definition of the struct and an associated public `new` function to create an
|
||||
instance of `Post`, as shown in Listing 17-12. We’ll also make a private
|
||||
`State` trait. Then `Post` will hold a trait object of `Box<State>` inside an
|
||||
`Option` in a private field named `state`. You’ll see why the `Option` is
|
||||
necessary in a bit.
|
||||
`State` trait. Then `Post` will hold a trait object of `Box<dyn State>`
|
||||
inside an `Option<T>` in a private field named `state`. You’ll see why the
|
||||
`Option<T>` is necessary in a bit.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
pub struct Post {
|
||||
state: Option<Box<State>>,
|
||||
state: Option<Box<dyn State>>,
|
||||
content: String,
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ change its state from `Draft` to `PendingReview`. Listing 17-15 shows this code:
|
||||
|
||||
```rust
|
||||
# pub struct Post {
|
||||
# state: Option<Box<State>>,
|
||||
# state: Option<Box<dyn State>>,
|
||||
# content: String,
|
||||
# }
|
||||
#
|
||||
@ -218,13 +218,13 @@ impl Post {
|
||||
}
|
||||
|
||||
trait State {
|
||||
fn request_review(self: Box<Self>) -> Box<State>;
|
||||
fn request_review(self: Box<Self>) -> Box<dyn State>;
|
||||
}
|
||||
|
||||
struct Draft {}
|
||||
|
||||
impl State for Draft {
|
||||
fn request_review(self: Box<Self>) -> Box<State> {
|
||||
fn request_review(self: Box<Self>) -> Box<dyn State> {
|
||||
Box::new(PendingReview {})
|
||||
}
|
||||
}
|
||||
@ -232,7 +232,7 @@ impl State for Draft {
|
||||
struct PendingReview {}
|
||||
|
||||
impl State for PendingReview {
|
||||
fn request_review(self: Box<Self>) -> Box<State> {
|
||||
fn request_review(self: Box<Self>) -> Box<dyn State> {
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -293,7 +293,7 @@ state is approved, as shown in Listing 17-16:
|
||||
|
||||
```rust
|
||||
# pub struct Post {
|
||||
# state: Option<Box<State>>,
|
||||
# state: Option<Box<dyn State>>,
|
||||
# content: String,
|
||||
# }
|
||||
#
|
||||
@ -307,19 +307,19 @@ impl Post {
|
||||
}
|
||||
|
||||
trait State {
|
||||
fn request_review(self: Box<Self>) -> Box<State>;
|
||||
fn approve(self: Box<Self>) -> Box<State>;
|
||||
fn request_review(self: Box<Self>) -> Box<dyn State>;
|
||||
fn approve(self: Box<Self>) -> Box<dyn State>;
|
||||
}
|
||||
|
||||
struct Draft {}
|
||||
|
||||
impl State for Draft {
|
||||
# fn request_review(self: Box<Self>) -> Box<State> {
|
||||
# fn request_review(self: Box<Self>) -> Box<dyn State> {
|
||||
# Box::new(PendingReview {})
|
||||
# }
|
||||
#
|
||||
// --snip--
|
||||
fn approve(self: Box<Self>) -> Box<State> {
|
||||
fn approve(self: Box<Self>) -> Box<dyn State> {
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -327,12 +327,12 @@ impl State for Draft {
|
||||
struct PendingReview {}
|
||||
|
||||
impl State for PendingReview {
|
||||
# fn request_review(self: Box<Self>) -> Box<State> {
|
||||
# fn request_review(self: Box<Self>) -> Box<dyn State> {
|
||||
# self
|
||||
# }
|
||||
#
|
||||
// --snip--
|
||||
fn approve(self: Box<Self>) -> Box<State> {
|
||||
fn approve(self: Box<Self>) -> Box<dyn State> {
|
||||
Box::new(Published {})
|
||||
}
|
||||
}
|
||||
@ -340,11 +340,11 @@ impl State for PendingReview {
|
||||
struct Published {}
|
||||
|
||||
impl State for Published {
|
||||
fn request_review(self: Box<Self>) -> Box<State> {
|
||||
fn request_review(self: Box<Self>) -> Box<dyn State> {
|
||||
self
|
||||
}
|
||||
|
||||
fn approve(self: Box<Self>) -> Box<State> {
|
||||
fn approve(self: Box<Self>) -> Box<dyn State> {
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -374,7 +374,7 @@ otherwise, we want to return an empty string slice, as shown in Listing 17-17:
|
||||
# fn content<'a>(&self, post: &'a Post) -> &'a str;
|
||||
# }
|
||||
# pub struct Post {
|
||||
# state: Option<Box<State>>,
|
||||
# state: Option<Box<dyn State>>,
|
||||
# content: String,
|
||||
# }
|
||||
#
|
||||
@ -397,7 +397,7 @@ returned from using the `content` method on the `state` value.
|
||||
|
||||
We call the `as_ref` method on the `Option` because we want a reference to the
|
||||
value inside the `Option` rather than ownership of the value. Because `state`
|
||||
is an `Option<Box<State>>`, when we call `as_ref`, an `Option<&Box<State>>` is
|
||||
is an `Option<Box<dyn State>>`, when we call `as_ref`, an `Option<&Box<dyn State>>` is
|
||||
returned. If we didn’t call `as_ref`, we would get an error because we can’t
|
||||
move `state` out of the borrowed `&self` of the function parameter.
|
||||
|
||||
@ -408,7 +408,7 @@ the “Cases When You Have More Information Than the Compiler” section of Chap
|
||||
9 when we know that a `None` value is never possible, even though the compiler
|
||||
isn’t able to understand that.
|
||||
|
||||
At this point, when we call `content` on the `&Box<State>`, deref coercion will
|
||||
At this point, when we call `content` on the `&Box<dyn State>`, deref coercion will
|
||||
take effect on the `&` and the `Box` so the `content` method will ultimately be
|
||||
called on the type that implements the `State` trait. That means we need to add
|
||||
`content` to the `State` trait definition, and that is where we’ll put the
|
||||
|
||||
@ -17,12 +17,12 @@ to continue running a particular piece of code.
|
||||
|
||||
To use a pattern, we compare it to some value. If the pattern matches the
|
||||
value, we use the value parts in our code. Recall the `match` expressions in
|
||||
Chapter 6 that used patterns, such as the coin sorting machine example. If the
|
||||
Chapter 6 that used patterns, such as the coin-sorting machine example. If the
|
||||
value fits the shape of the pattern, we can use the named pieces. If it
|
||||
doesn’t, the code associated with the pattern won’t run.
|
||||
|
||||
This chapter is a reference on all things related to patterns. We’ll cover the
|
||||
valid places to use patterns, the difference between *refutable* and
|
||||
*irrefutable* patterns, and the different kinds of pattern syntax that you
|
||||
might see. By the end of the chapter, you’ll know how to use patterns to
|
||||
express many concepts in a clear way.
|
||||
valid places to use patterns, the difference between refutable and irrefutable
|
||||
patterns, and the different kinds of pattern syntax that you might see. By the
|
||||
end of the chapter, you’ll know how to use patterns to express many concepts in
|
||||
a clear way.
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
## All the Places Patterns Can Be Used
|
||||
|
||||
Patterns pop up in a number of places in Rust, and you’ve been using them a lot
|
||||
without realizing it! This section provides you with a reference to all the
|
||||
places where patterns are valid.
|
||||
without realizing it! This section discusses all the places where patterns are
|
||||
valid.
|
||||
|
||||
### `match` Arms
|
||||
|
||||
@ -40,20 +40,14 @@ the pattern in the `if let` doesn’t match.
|
||||
|
||||
Listing 18-1 shows that it’s also possible to mix and match `if let`, `else
|
||||
if`, and `else if let` expressions. Doing so gives us more flexibility than a
|
||||
`match` expression in which we can only express one value to compare with the
|
||||
`match` expression in which we can express only one value to compare with the
|
||||
patterns. Also, the conditions in a series of `if let`, `else if`, `else if
|
||||
let` arms aren’t required to relate to each other.
|
||||
|
||||
The code in Listing 18-1 shows a series of checks for several different
|
||||
conditions that decide what the background color should be. For this example,
|
||||
we’ve created variables with hardcoded values that a real program might receive
|
||||
from user input.
|
||||
|
||||
If the user specifies a favorite color, that color is the background color. If
|
||||
today is Tuesday, the background color will be green. If the user specifies
|
||||
their age as a string and we can parse it as a number successfully, the color
|
||||
is either purple or orange depending on the value of the number. If none of
|
||||
these conditions apply, the background color will be blue:
|
||||
The code in Listing 18-1 shows a series of checks for several conditions that
|
||||
decide what the background color should be. For this example, we’ve created
|
||||
variables with hardcoded values that a real program might receive from user
|
||||
input.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -82,6 +76,12 @@ fn main() {
|
||||
<span class="caption">Listing 18-1: Mixing `if let`, `else if`, `else if let`,
|
||||
and `else`</span>
|
||||
|
||||
If the user specifies a favorite color, that color is the background color. If
|
||||
today is Tuesday, the background color is green. If the user specifies
|
||||
their age as a string and we can parse it as a number successfully, the color
|
||||
is either purple or orange depending on the value of the number. If none of
|
||||
these conditions apply, the background color is blue.
|
||||
|
||||
This conditional structure lets us support complex requirements. With the
|
||||
hardcoded values we have here, this example will print `Using purple as the
|
||||
background color`.
|
||||
@ -104,7 +104,7 @@ not alert us to the possible logic bug.
|
||||
Similar in construction to `if let`, the `while let` conditional loop allows a
|
||||
`while` loop to run for as long as a pattern continues to match. The example in
|
||||
Listing 18-2 shows a `while let` loop that uses a vector as a stack and prints
|
||||
out the values in the vector in the opposite order in which they were pushed:
|
||||
the values in the vector in the opposite order in which they were pushed.
|
||||
|
||||
```rust
|
||||
let mut stack = Vec::new();
|
||||
@ -118,8 +118,8 @@ while let Some(top) = stack.pop() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 18-2: Using a `while let` loop to print out
|
||||
values for as long as `stack.pop()` returns `Some`</span>
|
||||
<span class="caption">Listing 18-2: Using a `while let` loop to print values
|
||||
for as long as `stack.pop()` returns `Some`</span>
|
||||
|
||||
This example prints 3, 2, and then 1. The `pop` method takes the last element
|
||||
out of the vector and returns `Some(value)`. If the vector is empty, `pop`
|
||||
@ -129,13 +129,13 @@ use `while let` to pop every element off our stack.
|
||||
|
||||
### `for` Loops
|
||||
|
||||
In Chapter 3 we mentioned that the `for` loop is the most common loop
|
||||
In Chapter 3, we mentioned that the `for` loop is the most common loop
|
||||
construction in Rust code, but we haven’t yet discussed the pattern that `for`
|
||||
takes. In a `for` loop, the pattern is the value that directly follows the
|
||||
keyword `for`, so in `for x in y` the `x` is the pattern.
|
||||
|
||||
Listing 18-3 demonstrates how to use a pattern in a `for` loop to destructure,
|
||||
or break apart, a tuple as part of the `for` loop:
|
||||
or break apart, a tuple as part of the `for` loop.
|
||||
|
||||
```rust
|
||||
let v = vec!['a', 'b', 'c'];
|
||||
@ -189,7 +189,7 @@ the variable `x`.” Because the name `x` is the whole pattern, this pattern
|
||||
effectively means “bind everything to the variable `x`, whatever the value is.”
|
||||
|
||||
To see the pattern matching aspect of `let` more clearly, consider Listing
|
||||
18-4, which uses a pattern with `let` to destructure a tuple:
|
||||
18-4, which uses a pattern with `let` to destructure a tuple.
|
||||
|
||||
```rust
|
||||
let (x, y, z) = (1, 2, 3);
|
||||
@ -206,7 +206,7 @@ pattern as nesting three individual variable patterns inside it.
|
||||
If the number of elements in the pattern doesn’t match the number of elements
|
||||
in the tuple, the overall type won’t match and we’ll get a compiler error. For
|
||||
example, Listing 18-5 shows an attempt to destructure a tuple with three
|
||||
elements into two variables, which won’t work:
|
||||
elements into two variables, which won’t work.
|
||||
|
||||
```rust,ignore
|
||||
let (x, y) = (1, 2, 3);
|
||||
@ -229,7 +229,7 @@ error[E0308]: mismatched types
|
||||
```
|
||||
|
||||
If we wanted to ignore one or more of the values in the tuple, we could use `_`
|
||||
or `..` as you’ll see in the “Ignoring Values in a Pattern” section. If the
|
||||
or `..`, as you’ll see in the “Ignoring Values in a Pattern” section. If the
|
||||
problem is that we have too many variables in the pattern, the solution is to
|
||||
make the types match by removing variables so the number of variables equals
|
||||
the number of elements in the tuple.
|
||||
@ -238,7 +238,7 @@ the number of elements in the tuple.
|
||||
|
||||
Function parameters can also be patterns. The code in Listing 18-6, which
|
||||
declares a function named `foo` that takes one parameter named `x` of type
|
||||
`i32`, should by now look familiar:
|
||||
`i32`, should by now look familiar.
|
||||
|
||||
```rust
|
||||
fn foo(x: i32) {
|
||||
@ -251,7 +251,7 @@ parameters</span>
|
||||
|
||||
The `x` part is a pattern! As we did with `let`, we could match a tuple in a
|
||||
function’s arguments to the pattern. Listing 18-7 splits the values in a tuple
|
||||
as we pass it to a function:
|
||||
as we pass it to a function.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -278,5 +278,5 @@ discussed in Chapter 13.
|
||||
|
||||
At this point, you’ve seen several ways of using patterns, but patterns don’t
|
||||
work the same in every place we can use them. In some places, the patterns must
|
||||
be *irrefutable*, meaning they must match any value provided. In other
|
||||
circumstances, they can be refutable. Let’s discuss these two concepts next.
|
||||
be irrefutable; in other circumstances, they can be refutable. We’ll discuss
|
||||
these two concepts next.
|
||||
|
||||
@ -5,8 +5,8 @@ for any possible value passed are *irrefutable*. An example would be `x` in the
|
||||
statement `let x = 5;` because `x` matches anything and therefore cannot fail
|
||||
to match. Patterns that can fail to match for some possible value are
|
||||
*refutable*. An example would be `Some(x)` in the expression `if let Some(x) =
|
||||
a_value`; if the value in `a_value` variable is `None` rather than `Some`, the
|
||||
`Some(x)` pattern would not match.
|
||||
a_value` because if the value in the `a_value` variable is `None` rather than
|
||||
`Some`, the `Some(x)` pattern will not match.
|
||||
|
||||
Function parameters, `let` statements, and `for` loops can only accept
|
||||
irrefutable patterns, because the program cannot do anything meaningful when
|
||||
@ -24,7 +24,7 @@ using the pattern with, depending on the intended behavior of the code.
|
||||
Let’s look at an example of what happens when we try to use a refutable pattern
|
||||
where Rust requires an irrefutable pattern and vice versa. Listing 18-8 shows a
|
||||
`let` statement, but for the pattern we’ve specified `Some(x)`, a refutable
|
||||
pattern. As you might expect, this code will error:
|
||||
pattern. As you might expect, this code will not compile.
|
||||
|
||||
```rust,ignore
|
||||
let Some(x) = some_option_value;
|
||||
@ -54,7 +54,7 @@ To fix the problem where we have a refutable pattern where an irrefutable
|
||||
pattern is needed, we can change the code that uses the pattern: instead of
|
||||
using `let`, we can use `if let`. Then if the pattern doesn’t match, the code
|
||||
will just skip the code in the curly brackets, giving it a way to continue
|
||||
validly. Listing 18-9 shows how to fix the code in Listing 18-8:
|
||||
validly. Listing 18-9 shows how to fix the code in Listing 18-8.
|
||||
|
||||
```rust
|
||||
# let some_option_value: Option<i32> = None;
|
||||
@ -69,7 +69,7 @@ patterns instead of `let`</span>
|
||||
We’ve given the code an out! This code is perfectly valid, although it means we
|
||||
cannot use an irrefutable pattern without receiving an error. If we give `if
|
||||
let` a pattern that will always match, such as `x`, as shown in Listing 18-10,
|
||||
it will error:
|
||||
it will not compile.
|
||||
|
||||
```rust,ignore
|
||||
if let x = 5 {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
## All the Pattern Syntax
|
||||
## Pattern Syntax
|
||||
|
||||
Throughout the book, you’ve seen examples of many different kinds of patterns.
|
||||
In this section, we gather all the syntax valid in patterns and discuss why you
|
||||
might want to use each of them.
|
||||
Throughout the book, you’ve seen examples of many kinds of patterns. In this
|
||||
section, we gather all the syntax valid in patterns and discuss why you might
|
||||
want to use each one.
|
||||
|
||||
### Matching Literals
|
||||
|
||||
@ -35,7 +35,7 @@ with all variables. In Listing 18-11, we declare a variable named `x` with the
|
||||
value `Some(5)` and a variable `y` with the value `10`. We then create a
|
||||
`match` expression on the value `x`. Look at the patterns in the match arms and
|
||||
`println!` at the end, and try to figure out what the code will print before
|
||||
running this code or reading further:
|
||||
running this code or reading further.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -152,7 +152,7 @@ to use different parts of these values. Let’s walk through each value.
|
||||
#### Destructuring Structs
|
||||
|
||||
Listing 18-12 shows a `Point` struct with two fields, `x` and `y`, that we can
|
||||
break apart using a pattern with a `let` statement:
|
||||
break apart using a pattern with a `let` statement.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -186,7 +186,7 @@ shorthand for patterns that match struct fields: you only need to list the name
|
||||
of the struct field, and the variables created from the pattern will have the
|
||||
same names. Listing 18-13 shows code that behaves in the same way as the code
|
||||
in Listing 18-12, but the variables created in the `let` pattern are `x` and
|
||||
`y` instead of `a` and `b`:
|
||||
`y` instead of `a` and `b`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -219,7 +219,7 @@ destructure the other fields.
|
||||
|
||||
Listing 18-14 shows a `match` expression that separates `Point` values into
|
||||
three cases: points that lie directly on the `x` axis (which is true when `y =
|
||||
0`), on the `y` axis (`x = 0`), or neither:
|
||||
0`), on the `y` axis (`x = 0`), or neither.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -262,7 +262,7 @@ destructured `Option<i32>` in Listing 6-5 in Chapter 6. One detail we haven’t
|
||||
mentioned explicitly is that the pattern to destructure an enum should
|
||||
correspond to the way the data stored within the enum is defined. As an
|
||||
example, in Listing 18-15 we use the `Message` enum from Listing 6-2 and write
|
||||
a `match` with patterns that will destructure each inner value:
|
||||
a `match` with patterns that will destructure each inner value.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -323,6 +323,51 @@ pattern is similar to the pattern we specify to match tuples. The number of
|
||||
variables in the pattern must match the number of elements in the variant we’re
|
||||
matching.
|
||||
|
||||
#### Destructuring Nested Structs & Enums
|
||||
|
||||
Up until now, all of our examples have been matching structures that were one
|
||||
level deep. Matching can work on nested structures too!
|
||||
|
||||
We can refactor the example above to support both RGB and HSV colors:
|
||||
|
||||
```rust
|
||||
enum Color {
|
||||
Rgb(i32, i32, i32),
|
||||
Hsv(i32, i32, i32)
|
||||
}
|
||||
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(Color),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
|
||||
|
||||
match msg {
|
||||
Message::ChangeColor(Color::Rgb(r, g, b)) => {
|
||||
println!(
|
||||
"Change the color to red {}, green {}, and blue {}",
|
||||
r,
|
||||
g,
|
||||
b
|
||||
)
|
||||
},
|
||||
Message::ChangeColor(Color::Hsv(h, s, v)) => {
|
||||
println!(
|
||||
"Change the color to hue {}, saturation {}, and value {}",
|
||||
h,
|
||||
s,
|
||||
v
|
||||
)
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Destructuring References
|
||||
|
||||
When the value we’re matching to our pattern contains a reference, we need to
|
||||
@ -334,8 +379,8 @@ iterate over references, but we want to use the values in the closure rather
|
||||
than the references.
|
||||
|
||||
The example in Listing 18-16 iterates over references to `Point` instances in a
|
||||
vector, and destructures the reference and the struct so we can perform
|
||||
calculations on the `x` and `y` values easily:
|
||||
vector, destructuring the reference and the struct so we can perform
|
||||
calculations on the `x` and `y` values easily.
|
||||
|
||||
```rust
|
||||
# struct Point {
|
||||
@ -363,7 +408,7 @@ is the result of squaring the `x` value and the `y` value, adding those
|
||||
together, and then adding the result for each `Point` in the `points` vector to
|
||||
get one number.
|
||||
|
||||
If we had not included the `&` in `&Point { x, y }` we’d get a type mismatch
|
||||
If we had not included the `&` in `&Point { x, y }`, we’d get a type mismatch
|
||||
error, because `iter` would then iterate over references to the items in the
|
||||
vector rather than the actual values. The error would look like this:
|
||||
|
||||
@ -385,7 +430,7 @@ we tried to match directly to a `Point` value, not a reference to a `Point`.
|
||||
|
||||
We can mix, match, and nest destructuring patterns in even more complex ways.
|
||||
The following example shows a complicated destructure where we nest structs and
|
||||
tuples inside a tuple, and destructure all the primitive values out:
|
||||
tuples inside a tuple and destructure all the primitive values out:
|
||||
|
||||
```rust
|
||||
# struct Point {
|
||||
@ -414,10 +459,10 @@ parts of a value. Let’s explore how and why to use each of these patterns.
|
||||
|
||||
#### Ignoring an Entire Value with `_`
|
||||
|
||||
We’ve used the underscore `_` as a wildcard pattern that will match any value
|
||||
We’ve used the underscore (`_`) as a wildcard pattern that will match any value
|
||||
but not bind to the value. Although the underscore `_` pattern is especially
|
||||
useful as the last arm in a `match` expression, we can use it in any pattern,
|
||||
including function parameters, as shown in Listing 18-17:
|
||||
including function parameters, as shown in Listing 18-17.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -438,7 +483,7 @@ and will print `This code only uses the y parameter: 4`.
|
||||
|
||||
In most cases when you no longer need a particular function parameter, you
|
||||
would change the signature so it doesn’t include the unused parameter. Ignoring
|
||||
a function parameter can be especially useful in some cases: for example, when
|
||||
a function parameter can be especially useful in some cases, for example, when
|
||||
implementing a trait when you need a certain type signature but the function
|
||||
body in your implementation doesn’t need one of the parameters. The compiler
|
||||
will then not warn about unused function parameters, as it would if you used a
|
||||
@ -446,8 +491,8 @@ name instead.
|
||||
|
||||
#### Ignoring Parts of a Value with a Nested `_`
|
||||
|
||||
We can also use `_` inside another pattern to ignore just part of a value: for
|
||||
example, when we only want to test for part of a value but have no use for the
|
||||
We can also use `_` inside another pattern to ignore just part of a value, for
|
||||
example, when we want to test for only part of a value but have no use for the
|
||||
other parts in the corresponding code we want to run. Listing 18-18 shows code
|
||||
responsible for managing a setting’s value. The business requirements are that
|
||||
the user should not be allowed to overwrite an existing customization of a
|
||||
@ -487,7 +532,7 @@ In all other cases (if either `setting_value` or `new_setting_value` are
|
||||
|
||||
We can also use underscores in multiple places within one pattern to ignore
|
||||
particular values. Listing 18-19 shows an example of ignoring the second and
|
||||
fourth values in a tuple of five items:
|
||||
fourth values in a tuple of five items.
|
||||
|
||||
```rust
|
||||
let numbers = (2, 4, 8, 16, 32);
|
||||
@ -504,7 +549,7 @@ match numbers {
|
||||
This code will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be
|
||||
ignored.
|
||||
|
||||
#### Ignoring an Unused Variable by Starting Its Name with an Underscore
|
||||
#### Ignoring an Unused Variable by Starting Its Name with `_`
|
||||
|
||||
If you create a variable but don’t use it anywhere, Rust will usually issue a
|
||||
warning because that could be a bug. But sometimes it’s useful to create a
|
||||
@ -532,7 +577,7 @@ warning about not using the variable preceded by the underscore.
|
||||
Note that there is a subtle difference between using only `_` and using a name
|
||||
that starts with an underscore. The syntax `_x` still binds the value to the
|
||||
variable, whereas `_` doesn’t bind at all. To show a case where this
|
||||
distinction matters, Listing 18-21 will provide us with an error:
|
||||
distinction matters, Listing 18-21 will provide us with an error.
|
||||
|
||||
```rust,ignore
|
||||
let s = Some(String::from("Hello!"));
|
||||
@ -550,7 +595,7 @@ underscore still binds the value, which might take ownership of the value</span>
|
||||
We’ll receive an error because the `s` value will still be moved into `_s`,
|
||||
which prevents us from using `s` again. However, using the underscore by itself
|
||||
doesn’t ever bind to the value. Listing 18-22 will compile without any errors
|
||||
because `s` doesn’t get moved into `_`:
|
||||
because `s` doesn’t get moved into `_`.
|
||||
|
||||
```rust
|
||||
let s = Some(String::from("Hello!"));
|
||||
@ -570,12 +615,12 @@ This code works just fine because we never bind `s` to anything; it isn’t move
|
||||
#### Ignoring Remaining Parts of a Value with `..`
|
||||
|
||||
With values that have many parts, we can use the `..` syntax to use only a few
|
||||
parts and ignore the rest, and avoid having to list underscores for each
|
||||
parts and ignore the rest, avoiding the need to list underscores for each
|
||||
ignored value. The `..` pattern ignores any parts of a value that we haven’t
|
||||
explicitly matched in the rest of the pattern. In Listing 18-23, we have a
|
||||
`Point` struct that holds a coordinate in three-dimensional space. In the
|
||||
`match` expression, we want to operate only on the `x` coordinate and ignore
|
||||
the values in the `y` and `z` fields:
|
||||
the values in the `y` and `z` fields.
|
||||
|
||||
```rust
|
||||
struct Point {
|
||||
@ -594,13 +639,13 @@ match origin {
|
||||
<span class="caption">Listing 18-23: Ignoring all fields of a `Point` except
|
||||
for `x` by using `..`</span>
|
||||
|
||||
We list the `x` value, and then just include the `..` pattern. This is quicker
|
||||
We list the `x` value and then just include the `..` pattern. This is quicker
|
||||
than having to list `y: _` and `z: _`, particularly when we’re working with
|
||||
structs that have lots of fields in situations where only one or two fields are
|
||||
relevant.
|
||||
|
||||
The syntax `..` will expand to as many values as it needs to be. Listing 18-24
|
||||
shows how to use `..` with a tuple:
|
||||
shows how to use `..` with a tuple.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -623,8 +668,9 @@ In this code, the first and last value are matched with `first` and `last`. The
|
||||
`..` will match and ignore everything in the middle.
|
||||
|
||||
However, using `..` must be unambiguous. If it is unclear which values are
|
||||
intended for matching and which should be ignored, Rust will error. Listing
|
||||
18-25 shows an example of using `..` ambiguously, so it will not compile:
|
||||
intended for matching and which should be ignored, Rust will give us an error.
|
||||
Listing 18-25 shows an example of using `..` ambiguously, so it will not
|
||||
compile.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -654,14 +700,14 @@ error: `..` can only be used once per tuple or tuple struct pattern
|
||||
```
|
||||
|
||||
It’s impossible for Rust to determine how many values in the tuple to ignore
|
||||
before matching a value with `second`, and then how many further values to
|
||||
before matching a value with `second` and then how many further values to
|
||||
ignore thereafter. This code could mean that we want to ignore `2`, bind
|
||||
`second` to `4`, and then ignore `8`, `16`, and `32`; or that we want to ignore
|
||||
`2` and `4`, bind `second` to `8`, and then ignore `16` and `32`; and so forth.
|
||||
The variable name `second` doesn’t mean anything special to Rust, so we get a
|
||||
compiler error because using `..` in two places like this is ambiguous.
|
||||
|
||||
### `ref` and `ref mut` to Create References in Patterns
|
||||
### Creating References in Patterns with `ref` and `ref mut`
|
||||
|
||||
Let’s look at using `ref` to make references so ownership of the values isn’t
|
||||
moved to variables in the pattern. Usually, when you match against a pattern,
|
||||
@ -671,7 +717,7 @@ the pattern. Listing 18-26 shows an example of a `match` that has a pattern
|
||||
with a variable and then usage of the entire value in the `println!` statement
|
||||
later, after the `match`. This code will fail to compile because ownership of
|
||||
part of the `robot_name` value is transferred to the `name` variable in the
|
||||
pattern of the first `match` arm:
|
||||
pattern of the first `match` arm.
|
||||
|
||||
```rust,ignore
|
||||
let robot_name = Some(String::from("Bors"));
|
||||
@ -702,7 +748,7 @@ reference in the value. Because `&` already has that meaning in patterns, we
|
||||
can’t use `&` to create a reference in a pattern.
|
||||
|
||||
Instead, to create a reference in a pattern, we use the `ref` keyword before
|
||||
the new variable, as shown in Listing 18-27:
|
||||
the new variable, as shown in Listing 18-27.
|
||||
|
||||
```rust
|
||||
let robot_name = Some(String::from("Bors"));
|
||||
@ -726,7 +772,7 @@ To create a mutable reference so we’re able to mutate a value matched in a
|
||||
pattern, we use `ref mut` instead of `&mut`. The reason is, again, that in
|
||||
patterns, the latter is for matching existing mutable references, not creating
|
||||
new ones. Listing 18-28 shows an example of a pattern creating a mutable
|
||||
reference:
|
||||
reference.
|
||||
|
||||
```rust
|
||||
let mut robot_name = Some(String::from("Bors"));
|
||||
@ -755,7 +801,7 @@ than a pattern alone allows.
|
||||
|
||||
The condition can use variables created in the pattern. Listing 18-29 shows a
|
||||
`match` where the first arm has the pattern `Some(x)` and also has a match
|
||||
guard of `if x < 5`:
|
||||
guard of `if x < 5`.
|
||||
|
||||
```rust
|
||||
let num = Some(4);
|
||||
@ -783,11 +829,11 @@ There is no way to express the `if x < 5` condition within a pattern, so the
|
||||
match guard gives us the ability to express this logic.
|
||||
|
||||
In Listing 18-11, we mentioned that we could use match guards to solve our
|
||||
pattern shadowing problem. Recall that a new variable was created inside the
|
||||
pattern-shadowing problem. Recall that a new variable was created inside the
|
||||
pattern in the `match` expression instead of using the variable outside the
|
||||
`match`. That new variable meant we couldn’t test against the value of the
|
||||
outer variable. Listing 18-30 shows how we can use a match guard to fix this
|
||||
problem:
|
||||
problem.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -826,7 +872,7 @@ patterns; the match guard condition will apply to all the patterns. Listing
|
||||
18-31 shows the precedence of combining a match guard with a pattern that uses
|
||||
`|`. The important part of this example is that the `if y` match guard applies
|
||||
to `4`, `5`, *and* `6`, even though it might look like `if y` only applies to
|
||||
`6`:
|
||||
`6`.
|
||||
|
||||
```rust
|
||||
let x = 4;
|
||||
@ -861,19 +907,19 @@ rather than this:
|
||||
```
|
||||
|
||||
After running the code, the precedence behavior is evident: if the match guard
|
||||
was only applied to the final value in the list of values specified using the
|
||||
were applied only to the final value in the list of values specified using the
|
||||
`|` operator, the arm would have matched and the program would have printed
|
||||
`yes`.
|
||||
|
||||
### `@` Bindings
|
||||
|
||||
The *at* operator `@` lets us create a variable that holds a value at the same
|
||||
time we’re testing that value to see whether it matches a pattern. Listing
|
||||
The *at* operator (`@`) lets us create a variable that holds a value at the
|
||||
same time we’re testing that value to see whether it matches a pattern. Listing
|
||||
18-32 shows an example where we want to test that a `Message::Hello` `id` field
|
||||
is within the range `3...7`. But we also want to bind the value to the variable
|
||||
`id_variable` so we can use it in the code associated with the arm. We could
|
||||
name this variable `id`, the same as the field, but for this example we’ll use
|
||||
a different name:
|
||||
a different name.
|
||||
|
||||
```rust
|
||||
enum Message {
|
||||
@ -902,18 +948,18 @@ This example will print `Found an id in range: 5`. By specifying `id_variable
|
||||
@` before the range `3...7`, we’re capturing whatever value matched the range
|
||||
while also testing that the value matched the range pattern.
|
||||
|
||||
In the second arm where we only have a range specified in the pattern, the code
|
||||
In the second arm, where we only have a range specified in the pattern, the code
|
||||
associated with the arm doesn’t have a variable that contains the actual value
|
||||
of the `id` field. The `id` field’s value could have been 10, 11, or 12, but
|
||||
the code that goes with that pattern doesn’t know which it is. The pattern code
|
||||
isn’t able to use the value from the `id` field, because we haven’t saved the
|
||||
`id` value in a variable.
|
||||
|
||||
In the last arm where we’ve specified a variable without a range, we do have
|
||||
In the last arm, where we’ve specified a variable without a range, we do have
|
||||
the value available to use in the arm’s code in a variable named `id`. The
|
||||
reason is that we’ve used the struct field shorthand syntax. But we haven’t
|
||||
applied any test to the value in the `id` field in this arm, like we did with
|
||||
the first two arms: any value would match this pattern.
|
||||
applied any test to the value in the `id` field in this arm, as we did with the
|
||||
first two arms: any value would match this pattern.
|
||||
|
||||
Using `@` lets us test a value and save it in a variable within one pattern.
|
||||
|
||||
|
||||
@ -10,13 +10,13 @@ make sure you have a grasp of all the features Rust has to offer.
|
||||
|
||||
In this chapter, we’ll cover:
|
||||
|
||||
* Unsafe Rust: How to opt out of some of Rust’s guarantees and take
|
||||
* Unsafe Rust: how to opt out of some of Rust’s guarantees and take
|
||||
responsibility for manually upholding those guarantees
|
||||
* Advanced lifetimes: Syntax for complex lifetime situations
|
||||
* Advanced traits: Associated types, default type parameters, fully qualified
|
||||
* Advanced lifetimes: syntax for complex lifetime situations
|
||||
* Advanced traits: associated types, default type parameters, fully qualified
|
||||
syntax, supertraits, and the newtype pattern in relation to traits
|
||||
* Advanced types: More about the newtype pattern, type aliases, the *never*
|
||||
type, and dynamically sized types
|
||||
* Advanced functions and closures: Function pointers and returning closures
|
||||
* Advanced types: more about the newtype pattern, type aliases, the never type,
|
||||
and dynamically sized types
|
||||
* Advanced functions and closures: function pointers and returning closures
|
||||
|
||||
It’s a panoply of Rust features with something for everyone! Let’s dive in!
|
||||
|
||||
@ -7,27 +7,27 @@ and works just like regular Rust, but gives us extra superpowers.
|
||||
|
||||
Unsafe Rust exists because, by nature, static analysis is conservative. When
|
||||
the compiler tries to determine whether or not code upholds the guarantees,
|
||||
it’s better for it to reject some valid programs rather than accepting some
|
||||
it’s better for it to reject some valid programs rather than accept some
|
||||
invalid programs. Although the code might be okay, as far as Rust is able to
|
||||
tell, it’s not! In these cases, we can use unsafe code to tell the compiler,
|
||||
“trust me, I know what I’m doing.” The downside is that we use it at our own
|
||||
risk: if we use unsafe code incorrectly, problems due to memory unsafety, such
|
||||
tell, it’s not! In these cases, you can use unsafe code to tell the compiler,
|
||||
“Trust me, I know what I’m doing.” The downside is that you use it at your own
|
||||
risk: if you use unsafe code incorrectly, problems due to memory unsafety, such
|
||||
as null pointer dereferencing, can occur.
|
||||
|
||||
Another reason Rust has an unsafe alter ego is that the underlying computer
|
||||
hardware is inherently unsafe. If Rust didn’t let us do unsafe operations, we
|
||||
couldn’t do certain tasks. Rust needs to allow us to do low-level systems
|
||||
hardware is inherently unsafe. If Rust didn’t let you do unsafe operations, you
|
||||
couldn’t do certain tasks. Rust needs to allow you to do low-level systems
|
||||
programming, such as directly interacting with the operating system or even
|
||||
writing our own operating system. Working with low-level systems programming is
|
||||
one of the goals of the language. Let’s explore what we can do with unsafe Rust
|
||||
and how to do it.
|
||||
writing your own operating system. Working with low-level systems programming
|
||||
is one of the goals of the language. Let’s explore what we can do with unsafe
|
||||
Rust and how to do it.
|
||||
|
||||
### Unsafe Superpowers
|
||||
|
||||
To switch to unsafe Rust, we use the `unsafe` keyword, and then start a new
|
||||
block that holds the unsafe code. We can take four actions in unsafe Rust,
|
||||
which we call *unsafe superpowers*, that we can’t in safe Rust. Those
|
||||
superpowers include the ability to:
|
||||
To switch to unsafe Rust, use the `unsafe` keyword and then start a new block
|
||||
that holds the unsafe code. You can take four actions in unsafe Rust, called
|
||||
*unsafe superpowers*, that you can’t in safe Rust. Those superpowers include
|
||||
the ability to:
|
||||
|
||||
* Dereference a raw pointer
|
||||
* Call an unsafe function or method
|
||||
@ -36,17 +36,17 @@ superpowers include the ability to:
|
||||
|
||||
It’s important to understand that `unsafe` doesn’t turn off the borrow checker
|
||||
or disable any other of Rust’s safety checks: if you use a reference in unsafe
|
||||
code, it will still be checked. The `unsafe` keyword only gives us access to
|
||||
code, it will still be checked. The `unsafe` keyword only gives you access to
|
||||
these four features that are then not checked by the compiler for memory
|
||||
safety. We still get some degree of safety inside of an unsafe block.
|
||||
safety. You’ll still get some degree of safety inside of an unsafe block.
|
||||
|
||||
In addition, `unsafe` does not mean the code inside the block is necessarily
|
||||
dangerous or that it will definitely have memory safety problems: the intent is
|
||||
that as the programmer, we’ll ensure the code inside an `unsafe` block will
|
||||
that as the programmer, you’ll ensure the code inside an `unsafe` block will
|
||||
access memory in a valid way.
|
||||
|
||||
People are fallible, and mistakes will happen, but by requiring these four
|
||||
unsafe operations to be inside blocks annotated with `unsafe` we’ll know that
|
||||
unsafe operations to be inside blocks annotated with `unsafe` you’ll know that
|
||||
any errors related to memory safety must be within an `unsafe` block. Keep
|
||||
`unsafe` blocks small; you’ll be thankful later when you investigate memory
|
||||
bugs.
|
||||
@ -60,7 +60,7 @@ from leaking out into all the places that you or your users might want to use
|
||||
the functionality implemented with `unsafe` code, because using a safe
|
||||
abstraction is safe.
|
||||
|
||||
Let’s look at each of the four unsafe superpowers in turn: we’ll also look at
|
||||
Let’s look at each of the four unsafe superpowers in turn. We’ll also look at
|
||||
some abstractions that provide a safe interface to unsafe code.
|
||||
|
||||
### Dereferencing a Raw Pointer
|
||||
@ -70,10 +70,10 @@ compiler ensures references are always valid. Unsafe Rust has two new types
|
||||
called *raw pointers* that are similar to references. As with references, raw
|
||||
pointers can be immutable or mutable and are written as `*const T` and `*mut
|
||||
T`, respectively. The asterisk isn’t the dereference operator; it’s part of the
|
||||
type name. In the context of raw pointers, “immutable” means that the pointer
|
||||
type name. In the context of raw pointers, *immutable* means that the pointer
|
||||
can’t be directly assigned to after being dereferenced.
|
||||
|
||||
Different from references and smart pointers, keep in mind that raw pointers:
|
||||
Different from references and smart pointers, raw pointers:
|
||||
|
||||
* Are allowed to ignore the borrowing rules by having both immutable and
|
||||
mutable pointers or multiple mutable pointers to the same location
|
||||
@ -81,12 +81,12 @@ Different from references and smart pointers, keep in mind that raw pointers:
|
||||
* Are allowed to be null
|
||||
* Don’t implement any automatic cleanup
|
||||
|
||||
By opting out of having Rust enforce these guarantees, we can make the
|
||||
trade-off of giving up guaranteed safety to gain performance or the ability to
|
||||
By opting out of having Rust enforce these guarantees, you can give up
|
||||
guaranteed safety in exchange for greater performance or the ability to
|
||||
interface with another language or hardware where Rust’s guarantees don’t apply.
|
||||
|
||||
Listing 19-1 shows how to create an immutable and a mutable raw pointer from
|
||||
references:
|
||||
references.
|
||||
|
||||
```rust
|
||||
let mut num = 5;
|
||||
@ -112,7 +112,7 @@ Listing 19-2 shows how to create a raw pointer to an arbitrary location in
|
||||
memory. Trying to use arbitrary memory is undefined: there might be data at
|
||||
that address or there might not, the compiler might optimize the code so there
|
||||
is no memory access, or the program might error with a segmentation fault.
|
||||
Usually, there is no good reason to write code like this, but it is possible:
|
||||
Usually, there is no good reason to write code like this, but it is possible.
|
||||
|
||||
```rust
|
||||
let address = 0x012345usize;
|
||||
@ -124,7 +124,7 @@ memory address</span>
|
||||
|
||||
Recall that we can create raw pointers in safe code, but we can’t *dereference*
|
||||
raw pointers and read the data being pointed to. In Listing 19-3, we use the
|
||||
dereference operator `*` on a raw pointer that requires an `unsafe` block:
|
||||
dereference operator `*` on a raw pointer that requires an `unsafe` block.
|
||||
|
||||
```rust
|
||||
let mut num = 5;
|
||||
@ -144,16 +144,16 @@ unsafe {
|
||||
Creating a pointer does no harm; it’s only when we try to access the value that
|
||||
it points at that we might end up dealing with an invalid value.
|
||||
|
||||
Note also that in Listing 19-1 and 19-3 we created `*const i32` and `*mut i32`
|
||||
Note also that in Listing 19-1 and 19-3, we created `*const i32` and `*mut i32`
|
||||
raw pointers that both pointed to the same memory location, where `num` is
|
||||
stored. If we instead tried to create an immutable and a mutable reference to
|
||||
`num`, the code would not have compiled because Rust’s ownership rules don’t
|
||||
allow a mutable reference at the same time as any immutable references. With
|
||||
raw pointers, we can create a mutable pointer and an immutable pointer to the
|
||||
same location, and change data through the mutable pointer, potentially
|
||||
creating a data race. Be careful!
|
||||
same location and change data through the mutable pointer, potentially creating
|
||||
a data race. Be careful!
|
||||
|
||||
With all of these dangers, why would we ever use raw pointers? One major use
|
||||
With all of these dangers, why would you ever use raw pointers? One major use
|
||||
case is when interfacing with C code, as you’ll see in the next section,
|
||||
“Calling an Unsafe Function or Method.” Another case is when building up safe
|
||||
abstractions that the borrow checker doesn’t understand. We’ll introduce unsafe
|
||||
@ -210,7 +210,7 @@ a common abstraction. As an example, let’s study a function from the standard
|
||||
library, `split_at_mut`, that requires some unsafe code and explore how we
|
||||
might implement it. This safe method is defined on mutable slices: it takes one
|
||||
slice and makes it two by splitting the slice at the index given as an
|
||||
argument. Listing 19-4 shows how to use `split_at_mut`:
|
||||
argument. Listing 19-4 shows how to use `split_at_mut`.
|
||||
|
||||
```rust
|
||||
let mut v = vec![1, 2, 3, 4, 5, 6];
|
||||
@ -245,17 +245,17 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
|
||||
<span class="caption">Listing 19-5: An attempted implementation of
|
||||
`split_at_mut` using only safe Rust</span>
|
||||
|
||||
This function first gets the total length of the slice, then it asserts that
|
||||
the index given as a parameter is within the slice by checking that it’s less
|
||||
than or equal to the length. The assertion means that if we pass an index that
|
||||
is greater than the index to split the slice at, the function will panic before
|
||||
it attempts to use that index.
|
||||
This function first gets the total length of the slice. Then it asserts that
|
||||
the index given as a parameter is within the slice by checking whether it’s
|
||||
less than or equal to the length. The assertion means that if we pass an index
|
||||
that is greater than the index to split the slice at, the function will panic
|
||||
before it attempts to use that index.
|
||||
|
||||
Then we return two mutable slices in a tuple: one from the start of the
|
||||
original slice to the `mid` index and another from `mid` to the end of the
|
||||
slice.
|
||||
|
||||
When we try to compile the code in Listing 19-5, we’ll get an error:
|
||||
When we try to compile the code in Listing 19-5, we’ll get an error.
|
||||
|
||||
```text
|
||||
error[E0499]: cannot borrow `*slice` as mutable more than once at a time
|
||||
@ -276,7 +276,7 @@ slices aren’t overlapping, but Rust isn’t smart enough to know this. When we
|
||||
know code is okay, but Rust doesn’t, it’s time to reach for unsafe code.
|
||||
|
||||
Listing 19-6 shows how to use an `unsafe` block, a raw pointer, and some calls
|
||||
to unsafe functions to make the implementation of `split_at_mut` work:
|
||||
to unsafe functions to make the implementation of `split_at_mut` work.
|
||||
|
||||
```rust
|
||||
use std::slice;
|
||||
@ -297,20 +297,20 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
|
||||
<span class="caption">Listing 19-6: Using unsafe code in the implementation of
|
||||
the `split_at_mut` function</span>
|
||||
|
||||
Recall from the “Slices” section in Chapter 4 that slices are a pointer to some
|
||||
data and the length of the slice. We use the `len` method to get the length of
|
||||
a slice and the `as_mut_ptr` method to access the raw pointer of a slice. In
|
||||
this case, because we have a mutable slice to `i32` values, `as_mut_ptr`
|
||||
returns a raw pointer with the type `*mut i32`, which we’ve stored in the
|
||||
variable `ptr`.
|
||||
Recall from “The Slice Type” section in Chapter 4 that slices are a pointer to
|
||||
some data and the length of the slice. We use the `len` method to get the
|
||||
length of a slice and the `as_mut_ptr` method to access the raw pointer of a
|
||||
slice. In this case, because we have a mutable slice to `i32` values,
|
||||
`as_mut_ptr` returns a raw pointer with the type `*mut i32`, which we’ve stored
|
||||
in the variable `ptr`.
|
||||
|
||||
We keep the assertion that the `mid` index is within the slice. Then we get to
|
||||
the unsafe code: the `slice::from_raw_parts_mut` function takes a raw pointer
|
||||
and a length, and creates a slice. We use this function to create a slice that
|
||||
starts from `ptr` and is `mid` items long. Then we call the `offset` method on
|
||||
`ptr` with `mid` as an argument to get a raw pointer that starts at `mid`, and
|
||||
we create a slice using that pointer and the remaining number of items after
|
||||
`mid` as the length.
|
||||
and a length, and it creates a slice. We use this function to create a slice
|
||||
that starts from `ptr` and is `mid` items long. Then we call the `offset`
|
||||
method on `ptr` with `mid` as an argument to get a raw pointer that starts at
|
||||
`mid`, and we create a slice using that pointer and the remaining number of
|
||||
items after `mid` as the length.
|
||||
|
||||
The function `slice::from_raw_parts_mut` is unsafe because it takes a raw
|
||||
pointer and must trust that this pointer is valid. The `offset` method on raw
|
||||
@ -330,7 +330,7 @@ data this function has access to.
|
||||
|
||||
In contrast, the use of `slice::from_raw_parts_mut` in Listing 19-7 would
|
||||
likely crash when the slice is used. This code takes an arbitrary memory
|
||||
location and creates a slice ten thousand items long:
|
||||
location and creates a slice 10,000 items long.
|
||||
|
||||
```rust
|
||||
use std::slice;
|
||||
@ -360,7 +360,7 @@ programming language to call those functions.
|
||||
|
||||
Listing 19-8 demonstrates how to set up an integration with the `abs` function
|
||||
from the C standard library. Functions declared within `extern` blocks are
|
||||
always unsafe to call from Rust code. The reason is that other languages don`t
|
||||
always unsafe to call from Rust code. The reason is that other languages don’t
|
||||
enforce Rust’s rules and guarantees, and Rust can’t check them, so
|
||||
responsibility falls on the programmer to ensure safety.
|
||||
|
||||
@ -387,30 +387,30 @@ functions from another language we want to call. The `"C"` part defines which
|
||||
defines how to call the function at the assembly level. The `"C"` ABI is the
|
||||
most common and follows the C programming language’s ABI.
|
||||
|
||||
#### Calling Rust Functions from Other Languages
|
||||
|
||||
We can also use `extern` to create an interface that allows other languages to
|
||||
call Rust functions. Instead of an `extern` block, we add the `extern` keyword
|
||||
and specify the ABI to use just before the `fn` keyword. We also need to add a
|
||||
`#[no_mangle]` annotation to tell the Rust compiler not to mangle the name of
|
||||
this function. *Mangling* is when a compiler changes the name we’ve given a
|
||||
function to a different name that contains more information for other parts of
|
||||
the compilation process to consume but is less human readable. Every
|
||||
programming language compiler mangles names slightly differently, so for a Rust
|
||||
function to be nameable by other languages, we must disable the Rust compiler’s
|
||||
name mangling.
|
||||
|
||||
In the following example, we make the `call_from_c` function accessible from C
|
||||
code, after it’s compiled to a shared library and linked from C:
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern "C" fn call_from_c() {
|
||||
println!("Just called a Rust function from C!");
|
||||
}
|
||||
```
|
||||
|
||||
This usage of `extern` does not require `unsafe`.
|
||||
> #### Calling Rust Functions from Other Languages
|
||||
>
|
||||
> We can also use `extern` to create an interface that allows other languages
|
||||
> to call Rust functions. Instead of an `extern` block, we add the `extern`
|
||||
> keyword and specify the ABI to use just before the `fn` keyword. We also need
|
||||
> to add a `#[no_mangle]` annotation to tell the Rust compiler not to mangle
|
||||
> the name of this function. *Mangling* is when a compiler changes the name
|
||||
> we’ve given a function to a different name that contains more information for
|
||||
> other parts of the compilation process to consume but is less human readable.
|
||||
> Every programming language compiler mangles names slightly differently, so
|
||||
> for a Rust function to be nameable by other languages, we must disable the
|
||||
> Rust compiler’s name mangling.
|
||||
>
|
||||
> In the following example, we make the `call_from_c` function accessible from
|
||||
> C code, after it’s compiled to a shared library and linked from C:
|
||||
>
|
||||
> ```rust
|
||||
> #[no_mangle]
|
||||
> pub extern "C" fn call_from_c() {
|
||||
> println!("Just called a Rust function from C!");
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> This usage of `extern` does not require `unsafe`.
|
||||
|
||||
### Accessing or Modifying a Mutable Static Variable
|
||||
|
||||
@ -420,7 +420,7 @@ accessing the same mutable global variable, it can cause a data race.
|
||||
|
||||
In Rust, global variables are called *static* variables. Listing 19-9 shows an
|
||||
example declaration and use of a static variable with a string slice as a
|
||||
value:
|
||||
value.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -451,7 +451,7 @@ are allowed to duplicate their data whenever they’re used.
|
||||
Another difference between constants and static variables is that static
|
||||
variables can be mutable. Accessing and modifying mutable static variables is
|
||||
*unsafe*. Listing 19-10 shows how to declare, access, and modify a mutable
|
||||
static variable named `COUNTER`:
|
||||
static variable named `COUNTER`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -485,16 +485,16 @@ races.
|
||||
With mutable data that is globally accessible, it’s difficult to ensure there
|
||||
are no data races, which is why Rust considers mutable static variables to be
|
||||
unsafe. Where possible, it’s preferable to use the concurrency techniques and
|
||||
thread-safe smart pointers we discussed in Chapter 16, so the compiler checks
|
||||
thread-safe smart pointers we discussed in Chapter 16 so the compiler checks
|
||||
that data accessed from different threads is done safely.
|
||||
|
||||
### Implementing an Unsafe Trait
|
||||
|
||||
The final action that only works with `unsafe` is implementing an unsafe trait.
|
||||
The final action that works only with `unsafe` is implementing an unsafe trait.
|
||||
A trait is unsafe when at least one of its methods has some invariant that the
|
||||
compiler can’t verify. We can declare that a trait is `unsafe` by adding the
|
||||
`unsafe` keyword before `trait`; then implementation of the trait must be
|
||||
marked as `unsafe` too, as shown in Listing 19-11:
|
||||
`unsafe` keyword before `trait` and marking the implementation of the trait as
|
||||
`unsafe` too, as shown in Listing 19-11.
|
||||
|
||||
```rust
|
||||
unsafe trait Foo {
|
||||
|
||||
@ -6,12 +6,12 @@ lifetimes of different references relate. You saw how every reference has a
|
||||
lifetime, but most of the time, Rust will let you elide lifetimes. Now we’ll
|
||||
look at three advanced features of lifetimes that we haven’t covered yet:
|
||||
|
||||
* Lifetime subtyping: Ensures that one lifetime outlives another lifetime
|
||||
* Lifetime bounds: Specifies a lifetime for a reference to a generic type
|
||||
* Inference of trait object lifetimes: How the compiler infers trait object
|
||||
lifetimes and when they need to be specified
|
||||
* Lifetime subtyping: ensures that one lifetime outlives another lifetime
|
||||
* Lifetime bounds: specifies a lifetime for a reference to a generic type
|
||||
* Inference of trait object lifetimes: allows the compiler to infer trait
|
||||
object lifetimes and when they need to be specified
|
||||
|
||||
### Lifetime Subtyping Ensures One Lifetime Outlives Another
|
||||
### Ensuring One Lifetime Outlives Another with Lifetime Subtyping
|
||||
|
||||
*Lifetime subtyping* specifies that one lifetime should outlive another
|
||||
lifetime. To explore lifetime subtyping, imagine we want to write a parser.
|
||||
@ -19,7 +19,7 @@ We’ll use a structure called `Context` that holds a reference to the string
|
||||
we’re parsing. We’ll write a parser that will parse this string and return
|
||||
success or failure. The parser will need to borrow the `Context` to do the
|
||||
parsing. Listing 19-12 implements this parser code, except the code doesn’t
|
||||
have the required lifetime annotations, so it won’t compile:
|
||||
have the required lifetime annotations, so it won’t compile.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -37,13 +37,14 @@ impl Parser {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 19-12: Defining a parser without lifetime annotations
|
||||
<span class="caption">Listing 19-12: Defining a parser without lifetime
|
||||
annotations</span>
|
||||
|
||||
Compiling the code results in errors because Rust expects lifetime parameters
|
||||
on the string slice in `Context` and the reference to a `Context` in `Parser`.
|
||||
|
||||
For simplicity’s sake, the `parse` function returns `Result<(), &str>`. That
|
||||
is, the function will do nothing on success, and on failure will return the
|
||||
is, the function will do nothing on success and, on failure, will return the
|
||||
part of the string slice that didn’t parse correctly. A real implementation
|
||||
would provide more error information and would return a structured data type
|
||||
when parsing succeeds. We won’t be discussing those details because they aren’t
|
||||
@ -60,8 +61,12 @@ lifetimes involved.
|
||||
|
||||
To get this code to compile, we need to fill in the lifetime parameters for the
|
||||
string slice in `Context` and the reference to the `Context` in `Parser`. The
|
||||
most straightforward way to do this is to use the same lifetime everywhere, as
|
||||
shown in Listing 19-13:
|
||||
most straightforward way to do this is to use the same lifetime name
|
||||
everywhere, as shown in Listing 19-13. Recall from the “Lifetime Annotations in
|
||||
Struct Definitions” section in Chapter 10 that each of `struct Context<'a>`,
|
||||
`struct Parser<'a>`, and `impl<'a>` is declaring a new lifetime parameter.
|
||||
While their names happen to all be the same, the three lifetime parameters
|
||||
declared in this example aren’t related.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -80,17 +85,17 @@ impl<'a> Parser<'a> {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 19-13: Annotating all references in `Context` and
|
||||
`Parser` with the same lifetime parameter</span>
|
||||
`Parser` with lifetime parameters</span>
|
||||
|
||||
This code compiles just fine. It tells Rust that a `Parser` holds a reference
|
||||
to a `Context` with lifetime `'a`, and that `Context` holds a string slice that
|
||||
to a `Context` with lifetime `'a` and that `Context` holds a string slice that
|
||||
also lives as long as the reference to the `Context` in `Parser`. Rust’s
|
||||
compiler error message stated that lifetime parameters were required for these
|
||||
references, and we’ve now added lifetime parameters.
|
||||
|
||||
Next, in Listing 19-14, we’ll add a function that takes an instance of
|
||||
`Context`, uses a `Parser` to parse that context, and returns what `parse`
|
||||
returns. This code doesn’t quite work:
|
||||
returns. This code doesn’t quite work.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -186,18 +191,18 @@ string slice that `Context` holds is the same as that of the lifetime of the
|
||||
reference to `Context` that `Parser` holds.
|
||||
|
||||
The `parse_context` function can’t see that within the `parse` function, the
|
||||
string slice returned will outlive `Context` and `Parser`, and that the
|
||||
string slice returned will outlive `Context` and `Parser` and that the
|
||||
reference `parse_context` returns refers to the string slice, not to `Context`
|
||||
or `Parser`.
|
||||
|
||||
By knowing what the implementation of `parse` does, we know that the only
|
||||
reason the return value of `parse` is tied to the `Parser` is because it’s
|
||||
referencing the `Parser`’s `Context`, which is referencing the string slice.
|
||||
So, it’s really the lifetime of the string slice that `parse_context` needs to
|
||||
care about. We need a way to tell Rust that the string slice in `Context` and
|
||||
the reference to the `Context` in `Parser` have different lifetimes and that
|
||||
the return value of `parse_context` is tied to the lifetime of the string slice
|
||||
in `Context`.
|
||||
reason the return value of `parse` is tied to the `Parser` instance is that
|
||||
it’s referencing the `Parser` instance’s `Context`, which is referencing the
|
||||
string slice. So, it’s really the lifetime of the string slice that
|
||||
`parse_context` needs to care about. We need a way to tell Rust that the string
|
||||
slice in `Context` and the reference to the `Context` in `Parser` have
|
||||
different lifetimes and that the return value of `parse_context` is tied to the
|
||||
lifetime of the string slice in `Context`.
|
||||
|
||||
First, we’ll try giving `Parser` and `Context` different lifetime parameters,
|
||||
as shown in Listing 19-15. We’ll use `'s` and `'c` as lifetime parameter names
|
||||
@ -266,8 +271,8 @@ referenced data in `Context` with lifetime `'s` needs to be constrained to
|
||||
guarantee that it lives longer than the reference with lifetime `'c`. If `'s`
|
||||
is not longer than `'c`, the reference to `Context` might not be valid.
|
||||
|
||||
Now we get to the point of this section: the Rust feature *lifetime*
|
||||
*subtyping* specifies that one lifetime parameter lives at least as long as
|
||||
Now we get to the point of this section: the Rust feature *lifetime
|
||||
subtyping* specifies that one lifetime parameter lives at least as long as
|
||||
another one. In the angle brackets where we declare lifetime parameters, we can
|
||||
declare a lifetime `'a` as usual and declare a lifetime `'b` that lives at
|
||||
least as long as `'a` by declaring `'b` using the syntax `'b: 'a`.
|
||||
@ -293,7 +298,7 @@ lifetime of the string slice is longer than the reference to the `Context`.
|
||||
That was a very long-winded example, but as we mentioned at the start of this
|
||||
chapter, Rust’s advanced features are very specific. You won’t often need the
|
||||
syntax we described in this example, but in such situations, you’ll know how to
|
||||
refer to something you have a reference to.
|
||||
refer to something and give it the necessary lifetime.
|
||||
|
||||
### Lifetime Bounds on References to Generic Types
|
||||
|
||||
@ -307,7 +312,7 @@ As an example, consider a type that is a wrapper over references. Recall the
|
||||
section in Chapter 15: its `borrow` and `borrow_mut` methods return the types
|
||||
`Ref` and `RefMut`, respectively. These types are wrappers over references that
|
||||
keep track of the borrowing rules at runtime. The definition of the `Ref`
|
||||
struct is shown in Listing 19-16, without lifetime bounds for now:
|
||||
struct is shown in Listing 19-16, without lifetime bounds for now.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -316,7 +321,7 @@ struct Ref<'a, T>(&'a T);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 19-16: Defining a struct to wrap a reference to a
|
||||
generic type, without lifetime bounds to start</span>
|
||||
generic type, without lifetime bounds</span>
|
||||
|
||||
Without explicitly constraining the lifetime `'a` in relation to the generic
|
||||
parameter `T`, Rust will error because it doesn’t know how long the generic
|
||||
@ -350,7 +355,7 @@ consider adding an explicit lifetime bound `T: 'a` so that the reference type
|
||||
```
|
||||
|
||||
Listing 19-17 shows how to apply this advice by specifying the lifetime bound
|
||||
when we declare the generic type `T`:
|
||||
when we declare the generic type `T`.
|
||||
|
||||
```rust
|
||||
struct Ref<'a, T: 'a>(&'a T);
|
||||
@ -366,7 +371,7 @@ long as `'a`.
|
||||
We could solve this problem in a different way, as shown in the definition of a
|
||||
`StaticRef` struct in Listing 19-18, by adding the `'static` lifetime bound on
|
||||
`T`. This means if `T` contains any references, they must have the `'static`
|
||||
lifetime:
|
||||
lifetime.
|
||||
|
||||
```rust
|
||||
struct StaticRef<T: 'static>(&'static T);
|
||||
@ -393,7 +398,7 @@ happens if the type implementing the trait in the trait object has a lifetime
|
||||
of its own. Consider Listing 19-19 where we have a trait `Red` and a struct
|
||||
`Ball`. The `Ball` struct holds a reference (and thus has a lifetime parameter)
|
||||
and also implements trait `Red`. We want to use an instance of `Ball` as the
|
||||
trait object `Box<Red>`:
|
||||
trait object `Box<dyn Red>`.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -409,7 +414,7 @@ impl<'a> Red for Ball<'a> { }
|
||||
fn main() {
|
||||
let num = 5;
|
||||
|
||||
let obj = Box::new(Ball { diameter: &num }) as Box<Red>;
|
||||
let obj = Box::new(Ball { diameter: &num }) as Box<dyn Red>;
|
||||
}
|
||||
```
|
||||
|
||||
@ -425,14 +430,14 @@ rules for working with lifetimes and trait objects:
|
||||
is `'a`.
|
||||
* With a single `T: 'a` clause, the default lifetime of the trait object is
|
||||
`'a`.
|
||||
* With multiple `T: 'a`-like clauses, there is no default lifetime; we must be
|
||||
* With multiple clauses like `T: 'a`, there is no default lifetime; we must be
|
||||
explicit.
|
||||
|
||||
When we must be explicit, we can add a lifetime bound on a trait object like
|
||||
`Box<Red>` using the syntax `Box<Red + 'static>` or `Box<Red + 'a>`, depending
|
||||
on whether the reference lives for the entire program or not. As with the other
|
||||
bounds, the syntax adding a lifetime bound means that any implementor of the
|
||||
`Red` trait that has references inside the type must have the same lifetime
|
||||
specified in the trait object bounds as those references.
|
||||
`Box<dyn Red>` using the syntax `Box<dyn Red + 'static>` or `Box<dyn Red +
|
||||
'a>`, depending on whether the reference lives for the entire program or not.
|
||||
As with the other bounds, the syntax adding a lifetime bound means that any
|
||||
implementor of the `Red` trait that has references inside the type must have
|
||||
the same lifetime specified in the trait object bounds as those references.
|
||||
|
||||
Next, let’s look at some other advanced features that manage traits.
|
||||
|
||||
@ -4,7 +4,7 @@ We first covered traits in the “Traits: Defining Shared Behavior” section of
|
||||
Chapter 10, but as with lifetimes, we didn’t discuss the more advanced details.
|
||||
Now that you know more about Rust, we can get into the nitty-gritty.
|
||||
|
||||
### Associated Types Specify Placeholder Types in Trait Definitions
|
||||
### Specifying Placeholder Types in Trait Definitions with Associated Types
|
||||
|
||||
*Associated types* connect a type placeholder with a trait such that the trait
|
||||
method definitions can use these placeholder types in their signatures. The
|
||||
@ -15,7 +15,7 @@ trait is implemented.
|
||||
|
||||
We’ve described most of the advanced features in this chapter as being rarely
|
||||
needed. Associated types are somewhere in the middle: they’re used more rarely
|
||||
than features explained in the rest of the book, but more commonly than many of
|
||||
than features explained in the rest of the book but more commonly than many of
|
||||
the other features discussed in this chapter.
|
||||
|
||||
One example of a trait with an associated type is the `Iterator` trait that the
|
||||
@ -23,11 +23,12 @@ standard library provides. The associated type is named `Item` and stands in
|
||||
for the type of the values the type implementing the `Iterator` trait is
|
||||
iterating over. In “The `Iterator` Trait and the `next` Method” section of
|
||||
Chapter 13, we mentioned that the definition of the `Iterator` trait is as
|
||||
shown in Listing 19-20:
|
||||
shown in Listing 19-20.
|
||||
|
||||
```rust
|
||||
pub trait Iterator {
|
||||
type Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>;
|
||||
}
|
||||
```
|
||||
@ -40,11 +41,9 @@ that it will return values of type `Option<Self::Item>`. Implementors of the
|
||||
`Iterator` trait will specify the concrete type for `Item`, and the `next`
|
||||
method will return an `Option` containing a value of that concrete type.
|
||||
|
||||
#### Associated Types vs. Generics
|
||||
|
||||
Associated types might seem like a similar concept to generics, in that they
|
||||
allow us to define a function without specifying what types it can handle. So
|
||||
why use associated types?
|
||||
Associated types might seem like a similar concept to generics, in that the
|
||||
latter allow us to define a function without specifying what types it can
|
||||
handle. So why use associated types?
|
||||
|
||||
Let’s examine the difference between the two concepts with an example from
|
||||
Chapter 13 that implements the `Iterator` trait on the `Counter` struct. In
|
||||
@ -60,8 +59,8 @@ impl Iterator for Counter {
|
||||
// --snip--
|
||||
```
|
||||
|
||||
This syntax seems comparable to generics. So why not just define the `Iterator`
|
||||
trait with generics, as shown in Listing 19-21?
|
||||
This syntax seems comparable to that of generics. So why not just define the
|
||||
`Iterator` trait with generics, as shown in Listing 19-21?
|
||||
|
||||
```rust
|
||||
pub trait Iterator<T> {
|
||||
@ -73,13 +72,13 @@ pub trait Iterator<T> {
|
||||
`Iterator` trait using generics</span>
|
||||
|
||||
The difference is that when using generics, as in Listing 19-21, we must
|
||||
annotate the types in each implementation. The reason is that we can also
|
||||
implement `Iterator<String> for Counter` or any other type, which would give us
|
||||
multiple implementations of `Iterator` for `Counter`. In other words, when a
|
||||
trait has a generic parameter, it can be implemented for a type multiple times,
|
||||
changing the concrete types of the generic type parameters each time. When we
|
||||
use the `next` method on `Counter`, we would have to provide type annotations
|
||||
to indicate which implementation of `Iterator` we want to use.
|
||||
annotate the types in each implementation; because we can also implement
|
||||
`Iterator<String> for Counter` or any other type, we could have multiple
|
||||
implementations of `Iterator` for `Counter`. In other words, when a trait has a
|
||||
generic parameter, it can be implemented for a type multiple times, changing
|
||||
the concrete types of the generic type parameters each time. When we use the
|
||||
`next` method on `Counter`, we would have to provide type annotations to
|
||||
indicate which implementation of `Iterator` we want to use.
|
||||
|
||||
With associated types, we don’t need to annotate types because we can’t
|
||||
implement a trait on a type multiple times. In Listing 19-20 with the
|
||||
@ -112,7 +111,7 @@ struct:
|
||||
```rust
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
@ -155,22 +154,22 @@ trait Add<RHS=Self> {
|
||||
```
|
||||
|
||||
This code should look generally familiar: a trait with one method and an
|
||||
associated type. The new part is `RHS=Self` in the angle brackets: this syntax
|
||||
is called *default type parameters*. The `RHS` generic type parameter (short
|
||||
for “right hand side”) defines the type of the `rhs` parameter in the `add`
|
||||
method. If we don’t specify a concrete type for `RHS` when we implement the
|
||||
`Add` trait, the type of `RHS` will default to `Self`, which will be the type
|
||||
we’re implementing `Add` on.
|
||||
associated type. The new part is `RHS=Self`: this syntax is called *default
|
||||
type parameters*. The `RHS` generic type parameter (short for “right hand
|
||||
side”) defines the type of the `rhs` parameter in the `add` method. If we don’t
|
||||
specify a concrete type for `RHS` when we implement the `Add` trait, the type
|
||||
of `RHS` will default to `Self`, which will be the type we’re implementing
|
||||
`Add` on.
|
||||
|
||||
When we implemented `Add` for `Point`, we used the default for `RHS` because we
|
||||
wanted to add two `Point` instances. Let’s look at an example of implementing
|
||||
the `Add` trait where we want to customize the `RHS` type rather than using the
|
||||
default.
|
||||
|
||||
We have two structs holding values in different units, `Millimeters` and
|
||||
`Meters`. We want to add values in millimeters to values in meters and have the
|
||||
We have two structs, `Millimeters` and `Meters`, holding values in different
|
||||
units. We want to add values in millimeters to values in meters and have the
|
||||
implementation of `Add` do the conversion correctly. We can implement `Add` for
|
||||
`Millimeters` with `Meters` as the `RHS`, as shown in Listing 19-23:
|
||||
`Millimeters` with `Meters` as the `RHS`, as shown in Listing 19-23.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -195,35 +194,35 @@ impl Add<Meters> for Millimeters {
|
||||
To add `Millimeters` and `Meters`, we specify `impl Add<Meters>` to set the
|
||||
value of the `RHS` type parameter instead of using the default of `Self`.
|
||||
|
||||
We use default type parameters in two main ways:
|
||||
You’ll use default type parameters in two main ways:
|
||||
|
||||
* To extend a type without breaking existing code
|
||||
* To allow customization in specific cases most users won’t need
|
||||
|
||||
The standard library’s `Add` trait is an example of the second purpose:
|
||||
usually, you’ll add two like types, but the `Add` trait provides the ability
|
||||
for customizing beyond that. Using a default type parameter in the `Add` trait
|
||||
usually, you’ll add two like types, but the `Add` trait provides the ability to
|
||||
customize beyond that. Using a default type parameter in the `Add` trait
|
||||
definition means you don’t have to specify the extra parameter most of the
|
||||
time. In other words, a bit of implementation boilerplate isn’t needed, making
|
||||
it easier to use the trait.
|
||||
|
||||
The first purpose is similar to the second but in reverse: if we want to add a
|
||||
type parameter to an existing trait, we can give it a default to let us extend
|
||||
the functionality of the trait without breaking the existing implementation
|
||||
code.
|
||||
The first purpose is similar to the second but in reverse: if you want to add a
|
||||
type parameter to an existing trait, you can give it a default to allow
|
||||
extension of the functionality of the trait without breaking the existing
|
||||
implementation code.
|
||||
|
||||
### Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name
|
||||
|
||||
Nothing in Rust prevents a trait from having a method with the same name as
|
||||
another trait’s method, nor does Rust prevent us from implementing both traits
|
||||
another trait’s method, nor does Rust prevent you from implementing both traits
|
||||
on one type. It’s also possible to implement a method directly on the type with
|
||||
the same name as methods from traits.
|
||||
|
||||
When calling methods with the same name, we need to tell Rust which one we want
|
||||
to use. Consider the code in Listing 19-24 where we’ve defined two traits,
|
||||
When calling methods with the same name, you’ll need to tell Rust which one you
|
||||
want to use. Consider the code in Listing 19-24 where we’ve defined two traits,
|
||||
`Pilot` and `Wizard`, that both have a method called `fly`. We then implement
|
||||
both traits on a type `Human` that already has a method named `fly` implemented
|
||||
on it. Each `fly` method does something different:
|
||||
on it. Each `fly` method does something different.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -257,12 +256,12 @@ impl Human {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 19-24: Two traits defined to have a `fly` method
|
||||
and implementations of those traits on the `Human` type in addition to a `fly`
|
||||
method on `Human` directly</span>
|
||||
<span class="caption">Listing 19-24: Two traits are defined to have a `fly`
|
||||
method and are implemented on the `Human` type, and a `fly` method is
|
||||
implemented on `Human` directly</span>
|
||||
|
||||
When we call `fly` on an instance of `Human`, the compiler defaults to calling
|
||||
the method that is directly implemented on the type, as shown in Listing 19-25:
|
||||
the method that is directly implemented on the type, as shown in Listing 19-25.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -304,12 +303,12 @@ fn main() {
|
||||
<span class="caption">Listing 19-25: Calling `fly` on an instance of
|
||||
`Human`</span>
|
||||
|
||||
Running this code will print `*waving arms furiously*`, which shows that Rust
|
||||
Running this code will print `*waving arms furiously*`, showing that Rust
|
||||
called the `fly` method implemented on `Human` directly.
|
||||
|
||||
To call the `fly` methods from either the `Pilot` trait or the `Wizard` trait,
|
||||
we need to use more explicit syntax to specify which `fly` method we mean.
|
||||
Listing 19-26 demonstrates this syntax:
|
||||
Listing 19-26 demonstrates this syntax.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -355,8 +354,9 @@ want to call</span>
|
||||
|
||||
Specifying the trait name before the method name clarifies to Rust which
|
||||
implementation of `fly` we want to call. We could also write
|
||||
`Human::fly(&person)`, which is equivalent to `person.fly()` that we used in
|
||||
Listing 19-26 but is a bit longer to write if we don’t need to disambiguate.
|
||||
`Human::fly(&person)`, which is equivalent to the `person.fly()` that we used
|
||||
in Listing 19-26, but this is a bit longer to write if we don’t need to
|
||||
disambiguate.
|
||||
|
||||
Running this code prints the following:
|
||||
|
||||
@ -367,15 +367,15 @@ Up!
|
||||
```
|
||||
|
||||
Because the `fly` method takes a `self` parameter, if we had two *types* that
|
||||
both implement one *trait*, Rust can figure out which implementation of a trait
|
||||
to use based on the type of `self`.
|
||||
both implement one *trait*, Rust could figure out which implementation of a
|
||||
trait to use based on the type of `self`.
|
||||
|
||||
However, associated functions that are part of traits don’t have a `self`
|
||||
parameter. When two types in the same scope implement that trait, Rust can’t
|
||||
figure out which type we mean unless we use *fully qualified syntax*. For
|
||||
figure out which type you mean unless you use *fully qualified syntax*. For
|
||||
example, the `Animal` trait in Listing 19-27 has the associated function
|
||||
`baby_name`, the implementation of `Animal` for the struct `Dog`, and the
|
||||
associated function `baby_name` defined on `Dog` directly:
|
||||
associated function `baby_name` defined on `Dog` directly.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -404,8 +404,8 @@ fn main() {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 19-27: A trait with an associated function and a
|
||||
type that has an associated function with the same name that also implements
|
||||
the trait</span>
|
||||
type with an associated function of the same name that also implements the
|
||||
trait</span>
|
||||
|
||||
This code is for an animal shelter that wants to name all puppies Spot, which
|
||||
is implemented in the `baby_name` associated function that is defined on `Dog`.
|
||||
@ -425,7 +425,7 @@ This output isn’t what we wanted. We want to call the `baby_name` function tha
|
||||
is part of the `Animal` trait that we implemented on `Dog` so the code prints
|
||||
`A baby dog is called a puppy`. The technique of specifying the trait name that
|
||||
we used in Listing 19-26 doesn’t help here; if we change `main` to the code in
|
||||
Listing 19-28, we’ll get a compilation error:
|
||||
Listing 19-28, we’ll get a compilation error.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -454,9 +454,8 @@ error[E0283]: type annotations required: cannot resolve `_: Animal`
|
||||
```
|
||||
|
||||
To disambiguate and tell Rust that we want to use the implementation of
|
||||
`Animal` for `Dog`, we need to use *fully qualified syntax*, which is the most
|
||||
specific we can be when calling a function. Listing 19-29 demonstrates how to
|
||||
use fully qualified syntax:
|
||||
`Animal` for `Dog`, we need to use fully qualified syntax. Listing 19-29
|
||||
demonstrates how to use fully qualified syntax.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -504,18 +503,18 @@ In general, fully qualified syntax is defined as follows:
|
||||
```
|
||||
|
||||
For associated functions, there would not be a `receiver`: there would only be
|
||||
the list of other arguments. We could use fully qualified syntax everywhere
|
||||
that we call functions or methods. However, we’re allowed to omit any part of
|
||||
this syntax that Rust can figure out from other information in the program. We
|
||||
the list of other arguments. You could use fully qualified syntax everywhere
|
||||
that you call functions or methods. However, you’re allowed to omit any part of
|
||||
this syntax that Rust can figure out from other information in the program. You
|
||||
only need to use this more verbose syntax in cases where there are multiple
|
||||
implementations that use the same name and Rust needs help to identify which
|
||||
implementation we want to call.
|
||||
implementation you want to call.
|
||||
|
||||
### Using Supertraits to Require One Trait’s Functionality Within Another Trait
|
||||
|
||||
Sometimes, we might need one trait to use another trait’s functionality. In
|
||||
this case, we need to rely on the dependent trait also being implemented. The
|
||||
trait we’re relying on is a *supertrait* of the trait we’re implementing.
|
||||
Sometimes, you might need one trait to use another trait’s functionality. In
|
||||
this case, you need to rely on the dependent trait’s also being implemented.
|
||||
The trait you rely on is a *supertrait* of the trait you’re implementing.
|
||||
|
||||
For example, let’s say we want to make an `OutlinePrint` trait with an
|
||||
`outline_print` method that will print a value framed in asterisks. That is,
|
||||
@ -533,10 +532,10 @@ call `outline_print` on a `Point` instance that has `1` for `x` and `3` for
|
||||
|
||||
In the implementation of `outline_print`, we want to use the `Display` trait’s
|
||||
functionality. Therefore, we need to specify that the `OutlinePrint` trait will
|
||||
only work for types that also implement `Display` and provide the functionality
|
||||
work only for types that also implement `Display` and provide the functionality
|
||||
that `OutlinePrint` needs. We can do that in the trait definition by specifying
|
||||
`OutlinePrint: Display`. This technique is similar to adding a trait bound to
|
||||
the trait. Listing 19-30 shows an implementation of the `OutlinePrint` trait:
|
||||
the trait. Listing 19-30 shows an implementation of the `OutlinePrint` trait.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -561,9 +560,10 @@ requires the functionality from `Display`</span>
|
||||
|
||||
Because we’ve specified that `OutlinePrint` requires the `Display` trait, we
|
||||
can use the `to_string` function that is automatically implemented for any type
|
||||
that implements `Display`. If we tried to use `to_string` without adding`:
|
||||
Display` after the trait name, we’d get an error saying that no method named
|
||||
`to_string` was found for the type `&Self` in the current scope.
|
||||
that implements `Display`. If we tried to use `to_string` without adding a
|
||||
colon and specifying the `Display` trait after the trait name, we’d get an
|
||||
error saying that no method named `to_string` was found for the type `&Self` in
|
||||
the current scope.
|
||||
|
||||
Let’s see what happens when we try to implement `OutlinePrint` on a type that
|
||||
doesn’t implement `Display`, such as the `Point` struct:
|
||||
@ -588,7 +588,7 @@ error[E0277]: the trait bound `Point: std::fmt::Display` is not satisfied
|
||||
|
|
||||
20 | impl OutlinePrint for Point {}
|
||||
| ^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter;
|
||||
try using `:?` instead if you are using a format string
|
||||
try using `:?` instead if you are using a format string
|
||||
|
|
||||
= help: the trait `std::fmt::Display` is not implemented for `Point`
|
||||
```
|
||||
@ -617,25 +617,25 @@ Then implementing the `OutlinePrint` trait on `Point` will compile
|
||||
successfully, and we can call `outline_print` on a `Point` instance to display
|
||||
it within an outline of asterisks.
|
||||
|
||||
### The Newtype Pattern to Implement External Traits on External Types
|
||||
### Using the Newtype Pattern to Implement External Traits on External Types
|
||||
|
||||
In Chapter 10 in the “Implementing a Trait on a Type” section, we mentioned the
|
||||
orphan rule that states we’re allowed to implement a trait on a type as long as
|
||||
either the trait or the type are local to our crate. It’s possible to get
|
||||
around this restriction using the *newtype pattern*, which involves creating a
|
||||
new type in a tuple struct. (We covered tuple structs in the “Tuple Structs
|
||||
without Named Fields to Create Different Types” section of Chapter 5.) The
|
||||
tuple struct will have one field and be a thin wrapper around the type we want
|
||||
to implement a trait for. Then the wrapper type is local to our crate, and we
|
||||
can implement the trait on the wrapper. *Newtype* is a term that originates
|
||||
new type in a tuple struct. (We covered tuple structs in the “Using Tuple
|
||||
Structs without Named Fields to Create Different Types” section of Chapter 5.)
|
||||
The tuple struct will have one field and be a thin wrapper around the type we
|
||||
want to implement a trait for. Then the wrapper type is local to our crate, and
|
||||
we can implement the trait on the wrapper. *Newtype* is a term that originates
|
||||
from the Haskell programming language. There is no runtime performance penalty
|
||||
for using this pattern, and the wrapper type is elided at compile time.
|
||||
|
||||
As an example, let’s say we want to implement `Display` on `Vec`, which the
|
||||
As an example, let’s say we want to implement `Display` on `Vec<T>`, which the
|
||||
orphan rule prevents us from doing directly because the `Display` trait and the
|
||||
`Vec` type are defined outside our crate. We can make a `Wrapper` struct that
|
||||
holds an instance of `Vec`; then we can implement `Display` on `Wrapper` and
|
||||
use the `Vec` value, as shown in Listing 19-31:
|
||||
`Vec<T>` type are defined outside our crate. We can make a `Wrapper` struct
|
||||
that holds an instance of `Vec<T>`; then we can implement `Display` on
|
||||
`Wrapper` and use the `Vec<T>` value, as shown in Listing 19-31.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -659,21 +659,21 @@ fn main() {
|
||||
<span class="caption">Listing 19-31: Creating a `Wrapper` type around
|
||||
`Vec<String>` to implement `Display`</span>
|
||||
|
||||
The implementation of `Display` uses `self.0` to access the inner `Vec`,
|
||||
because `Wrapper` is a tuple struct and `Vec` is the item at index 0 in the
|
||||
The implementation of `Display` uses `self.0` to access the inner `Vec<T>`,
|
||||
because `Wrapper` is a tuple struct and `Vec<T>` is the item at index 0 in the
|
||||
tuple. Then we can use the functionality of the `Display` type on `Wrapper`.
|
||||
|
||||
The downside of using this technique is that `Wrapper` is a new type, so it
|
||||
doesn’t have the methods of the value it’s holding. We would have to implement
|
||||
all the methods of `Vec` directly on `Wrapper` so it can delegate to `self.0`,
|
||||
allowing us to treat `Wrapper` exactly like a `Vec`. If we wanted the new type
|
||||
to have every method the inner type has, implementing the `Deref` trait
|
||||
(discussed in Chapter 15 in the “Treating Smart Pointers like Regular
|
||||
References with the `Deref` Trait” section) on the `Wrapper` to return the
|
||||
inner type would be a solution. If we don’t want the `Wrapper` type to have all
|
||||
the methods of the inner type, in order to restrict the `Wrapper` type’s
|
||||
behavior for example, we would have to implement just the methods we do want
|
||||
manually.
|
||||
all the methods of `Vec<T>` directly on `Wrapper` such that the methods
|
||||
delegate to `self.0`, which would allow us to treat `Wrapper` exactly like a
|
||||
`Vec<T>`. If we wanted the new type to have every method the inner type has,
|
||||
implementing the `Deref` trait (discussed in Chapter 15 in the “Treating Smart
|
||||
Pointers like Regular References with the `Deref` Trait” section) on the
|
||||
`Wrapper` to return the inner type would be a solution. If we don’t want the
|
||||
`Wrapper` type to have all the methods of the inner type—for example, to
|
||||
restrict the `Wrapper` type’s behavior—we would have to implement just the
|
||||
methods we do want manually.
|
||||
|
||||
Now you know how the newtype pattern is used in relation to traits; it’s also a
|
||||
useful pattern even when traits are not involved. Let’s switch focus and look
|
||||
|
||||
@ -6,19 +6,18 @@ examine why newtypes are useful as types. Then we’ll move on to type aliases,
|
||||
feature similar to newtypes but with slightly different semantics. We’ll also
|
||||
discuss the `!` type and dynamically sized types.
|
||||
|
||||
> Note: The next section assumes you’ve read the earlier section “The Newtype
|
||||
> Pattern to Implement External Traits on External Types.”
|
||||
|
||||
### Using the Newtype Pattern for Type Safety and Abstraction
|
||||
|
||||
> Note: This section assumes you’ve read the earlier section “The Newtype
|
||||
> Pattern to Implement External Traits on External Types”.
|
||||
|
||||
The newtype pattern is useful for other tasks beyond what we’ve discussed so
|
||||
far, including statically enforcing that values are never confused and as an
|
||||
indication of the units of a value. You saw an example of using newtypes to
|
||||
indicate units in Listing 19-23: recall that the `Millimeters` and `Meters`
|
||||
structs wrapped `u32` values in a newtype. If we wrote a function with a
|
||||
parameter of type `Millimeters`, we couldn’t compile a program that
|
||||
accidentally tried to call that function with a value of type `Meters` or a
|
||||
plain `u32`.
|
||||
The newtype pattern is useful for tasks beyond those we’ve discussed so far,
|
||||
including statically enforcing that values are never confused and indicating
|
||||
the units of a value. You saw an example of using newtypes to indicate units in
|
||||
Listing 19-23: recall that the `Millimeters` and `Meters` structs wrapped `u32`
|
||||
values in a newtype. If we wrote a function with a parameter of type
|
||||
`Millimeters`, we couldn’t compile a program that accidentally tried to call
|
||||
that function with a value of type `Meters` or a plain `u32`.
|
||||
|
||||
Another use of the newtype pattern is in abstracting away some implementation
|
||||
details of a type: the new type can expose a public API that is different from
|
||||
@ -34,7 +33,7 @@ internally. The newtype pattern is a lightweight way to achieve encapsulation
|
||||
to hide implementation details, which we discussed in the “Encapsulation that
|
||||
Hides Implementation Details” section of Chapter 17.
|
||||
|
||||
### Type Aliases Create Type Synonyms
|
||||
### Creating Type Synonyms with Type Aliases
|
||||
|
||||
Along with the newtype pattern, Rust provides the ability to declare a *type
|
||||
alias* to give an existing type another name. For this we use the `type`
|
||||
@ -67,21 +66,21 @@ The main use case for type synonyms is to reduce repetition. For example, we
|
||||
might have a lengthy type like this:
|
||||
|
||||
```rust,ignore
|
||||
Box<Fn() + Send + 'static>
|
||||
Box<dyn Fn() + Send + 'static>
|
||||
```
|
||||
|
||||
Writing this lengthy type in function signatures and as type annotations all
|
||||
over the code can be tiresome and error prone. Imagine having a project full of
|
||||
code like that in Listing 19-32:
|
||||
code like that in Listing 19-32.
|
||||
|
||||
```rust
|
||||
let f: Box<Fn() + Send + 'static> = Box::new(|| println!("hi"));
|
||||
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));
|
||||
|
||||
fn takes_long_type(f: Box<Fn() + Send + 'static>) {
|
||||
fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {
|
||||
// --snip--
|
||||
}
|
||||
|
||||
fn returns_long_type() -> Box<Fn() + Send + 'static> {
|
||||
fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {
|
||||
// --snip--
|
||||
# Box::new(|| ())
|
||||
}
|
||||
@ -91,10 +90,10 @@ fn returns_long_type() -> Box<Fn() + Send + 'static> {
|
||||
|
||||
A type alias makes this code more manageable by reducing the repetition. In
|
||||
Listing 19-33, we’ve introduced an alias named `Thunk` for the verbose type and
|
||||
can replace all uses of the type with the shorter alias `Thunk`:
|
||||
can replace all uses of the type with the shorter alias `Thunk`.
|
||||
|
||||
```rust
|
||||
type Thunk = Box<Fn() + Send + 'static>;
|
||||
type Thunk = Box<dyn Fn() + Send + 'static>;
|
||||
|
||||
let f: Thunk = Box::new(|| println!("hi"));
|
||||
|
||||
@ -145,7 +144,7 @@ type Result<T> = Result<T, std::io::Error>;
|
||||
```
|
||||
|
||||
Because this declaration is in the `std::io` module, we can use the fully
|
||||
qualified alias `std::io::Result<T>`; that is, a `Result<T, E>` with the `E`
|
||||
qualified alias `std::io::Result<T>`—that is, a `Result<T, E>` with the `E`
|
||||
filled in as `std::io::Error`. The `Write` trait function signatures end up
|
||||
looking like this:
|
||||
|
||||
@ -162,9 +161,9 @@ pub trait Write {
|
||||
The type alias helps in two ways: it makes code easier to write *and* it gives
|
||||
us a consistent interface across all of `std::io`. Because it’s an alias, it’s
|
||||
just another `Result<T, E>`, which means we can use any methods that work on
|
||||
`Result<T, E>` with it, as well as special syntax like `?`.
|
||||
`Result<T, E>` with it, as well as special syntax like the `?` operator.
|
||||
|
||||
### The `!` Never Type that Never Returns
|
||||
### The Never Type that Never Returns
|
||||
|
||||
Rust has a special type named `!` that’s known in type theory lingo as the
|
||||
*empty type* because it has no values. We prefer to call it the *never type*
|
||||
@ -181,9 +180,8 @@ This code is read as “the function `bar` returns never.” Functions that retu
|
||||
never are called *diverging functions*. We can’t create values of the type `!`
|
||||
so `bar` can never possibly return.
|
||||
|
||||
But what use is a type you can never create values for? Recall the code in
|
||||
Chapter 2 that we added in the “Handling Invalid Input” section; we’ve
|
||||
reproduced it here in Listing 19-34:
|
||||
But what use is a type you can never create values for? Recall the code from
|
||||
Listing 2-5; we’ve reproduced part of it here in Listing 19-34.
|
||||
|
||||
```rust
|
||||
# let guess = "3";
|
||||
@ -211,7 +209,7 @@ let guess = match guess.trim().parse() {
|
||||
```
|
||||
|
||||
The type of `guess` in this code would have to be an integer *and* a string,
|
||||
and Rust requires that `guess` can only have one type. So what does `continue`
|
||||
and Rust requires that `guess` have only one type. So what does `continue`
|
||||
return? How were we allowed to return a `u32` from one arm and have another arm
|
||||
that ends with `continue` in Listing 19-34?
|
||||
|
||||
@ -242,10 +240,10 @@ impl<T> Option<T> {
|
||||
```
|
||||
|
||||
In this code, the same thing happens as in the `match` in Listing 19-34: Rust
|
||||
sees that `val` has the type `T` and `panic!` has the type `!` so the result of
|
||||
the overall `match` expression is `T`. This code works because `panic!` doesn’t
|
||||
produce a value; it ends the program. In the `None` case, we won’t be returning
|
||||
a value from `unwrap`, so this code is valid.
|
||||
sees that `val` has the type `T` and `panic!` has the type `!`, so the result
|
||||
of the overall `match` expression is `T`. This code works because `panic!`
|
||||
doesn’t produce a value; it ends the program. In the `None` case, we won’t be
|
||||
returning a value from `unwrap`, so this code is valid.
|
||||
|
||||
One final expression that has the type `!` is a `loop`:
|
||||
|
||||
@ -261,13 +259,13 @@ Here, the loop never ends, so `!` is the value of the expression. However, this
|
||||
wouldn’t be true if we included a `break`, because the loop would terminate
|
||||
when it got to the `break`.
|
||||
|
||||
### Dynamically Sized Types and `Sized`
|
||||
### Dynamically Sized Types and the `Sized` Trait
|
||||
|
||||
Due to Rust’s need to know certain details, such as how much space to allocate
|
||||
for a value of a particular type, there is a corner of its type system that can
|
||||
be confusing: the concept of *dynamically sized types*. Sometimes referred to
|
||||
as *DSTs* or *unsized types*, these types let us write code using values whose
|
||||
size we can only know at runtime.
|
||||
size we can know only at runtime.
|
||||
|
||||
Let’s dig into the details of a dynamically sized type called `str`, which
|
||||
we’ve been using throughout the book. That’s right, not `&str`, but `str` on
|
||||
@ -289,26 +287,26 @@ holding a dynamically sized type.
|
||||
|
||||
So what do we do? In this case, you already know the answer: we make the types
|
||||
of `s1` and `s2` a `&str` rather than a `str`. Recall that in the “String
|
||||
Slices” section of Chapter 4 we said the slice data structure stores the
|
||||
Slices” section of Chapter 4, we said the slice data structure stores the
|
||||
starting position and the length of the slice.
|
||||
|
||||
So although a `&T` is a single value that stores the memory address of where
|
||||
the `T` is located, a `&str` is *two* values: the address of the `str` and its
|
||||
length. As such, we can know the size of a `&str` value at compile time: it’s
|
||||
two times the size of a `usize` in length. That is, we always know the size of
|
||||
a `&str`, no matter how long the string it refers to is. In general, this is
|
||||
the way in which dynamically sized types are used in Rust: they have an extra
|
||||
bit of metadata that stores the size of the dynamic information. The golden
|
||||
rule of dynamically sized types is that we must always put values of
|
||||
dynamically sized types behind a pointer of some kind.
|
||||
twice the length of a `usize`. That is, we always know the size of a `&str`, no
|
||||
matter how long the string it refers to is. In general, this is the way in
|
||||
which dynamically sized types are used in Rust: they have an extra bit of
|
||||
metadata that stores the size of the dynamic information. The golden rule of
|
||||
dynamically sized types is that we must always put values of dynamically sized
|
||||
types behind a pointer of some kind.
|
||||
|
||||
We can combine `str` with all kinds of pointers: for example, `Box<str>` or
|
||||
`Rc<str>`. In fact, you’ve seen this before but with a different dynamically
|
||||
sized type: traits. Every trait is a dynamically sized type we can refer to by
|
||||
using the name of the trait. In Chapter 17 in the “Using Trait Objects that
|
||||
Allow for Values of Different Types” section, we mentioned that to use traits
|
||||
as trait objects, we must put them behind a pointer, such as `&Trait` or
|
||||
`Box<Trait>` (`Rc<Trait>` would work too).
|
||||
as trait objects, we must put them behind a pointer, such as `&dyn Trait` or
|
||||
`Box<dyn Trait>` (`Rc<dyn Trait>` would work too).
|
||||
|
||||
To work with DSTs, Rust has a particular trait called the `Sized` trait to
|
||||
determine whether or not a type’s size is known at compile time. This trait is
|
||||
@ -330,7 +328,7 @@ fn generic<T: Sized>(t: T) {
|
||||
}
|
||||
```
|
||||
|
||||
By default, generic functions will only work on types that have a known size at
|
||||
By default, generic functions will work only on types that have a known size at
|
||||
compile time. However, you can use the following special syntax to relax this
|
||||
restriction:
|
||||
|
||||
|
||||
@ -6,13 +6,13 @@ closures, which include function pointers and returning closures.
|
||||
### Function Pointers
|
||||
|
||||
We’ve talked about how to pass closures to functions; you can also pass regular
|
||||
functions to functions! This technique is useful when we want to pass a
|
||||
function we’ve already defined rather than defining a new closure. We do this
|
||||
using function pointers to allow us to use functions as arguments to other
|
||||
functions to functions! This technique is useful when you want to pass a
|
||||
function you’ve already defined rather than defining a new closure. Doing this
|
||||
with function pointers will allow you to use functions as arguments to other
|
||||
functions. Functions coerce to the type `fn` (with a lowercase f), not to be
|
||||
confused with the `Fn` closure trait. The `fn` type is called a function
|
||||
pointer. The syntax for specifying that a parameter is a function pointer is
|
||||
similar to that of closures, as shown in Listing 19-35:
|
||||
confused with the `Fn` closure trait. The `fn` type is called a *function
|
||||
pointer*. The syntax for specifying that a parameter is a function pointer is
|
||||
similar to that of closures, as shown in Listing 19-35.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -45,7 +45,7 @@ parameter type directly rather than declaring a generic type parameter with one
|
||||
of the `Fn` traits as a trait bound.
|
||||
|
||||
Function pointers implement all three of the closure traits (`Fn`, `FnMut`, and
|
||||
`FnOnce`), so we can always pass a function pointer as an argument for a
|
||||
`FnOnce`), so you can always pass a function pointer as an argument for a
|
||||
function that expects a closure. It’s best to write functions using a generic
|
||||
type and one of the closure traits so your functions can accept either
|
||||
functions or closures.
|
||||
@ -54,7 +54,7 @@ An example of where you would want to only accept `fn` and not closures is when
|
||||
interfacing with external code that doesn’t have closures: C functions can
|
||||
accept functions as arguments, but C doesn’t have closures.
|
||||
|
||||
As an example of where we can use either a closure defined inline or a named
|
||||
As an example of where you could use either a closure defined inline or a named
|
||||
function, let’s look at a use of `map`. To use the `map` function to turn a
|
||||
vector of numbers into a vector of strings, we could use a closure, like this:
|
||||
|
||||
@ -88,12 +88,12 @@ up compiling to the same code, so use whichever style is clearer to you.
|
||||
|
||||
### Returning Closures
|
||||
|
||||
Closures are represented by traits, which means we can’t return closures
|
||||
directly. In most cases where we might want to return a trait, we can instead
|
||||
Closures are represented by traits, which means you can’t return closures
|
||||
directly. In most cases where you might want to return a trait, you can instead
|
||||
use the concrete type that implements the trait as the return value of the
|
||||
function. But we can’t do that with closures because they don’t have a concrete
|
||||
type that is returnable; we’re not allowed to use the function pointer `fn` as
|
||||
a return type, for example.
|
||||
function. But you can’t do that with closures because they don’t have a
|
||||
concrete type that is returnable; you’re not allowed to use the function
|
||||
pointer `fn` as a return type, for example.
|
||||
|
||||
The following code tries to return a closure directly, but it won’t compile:
|
||||
|
||||
@ -124,13 +124,14 @@ it will need to store the closure. We saw a solution to this problem earlier.
|
||||
We can use a trait object:
|
||||
|
||||
```rust
|
||||
fn returns_closure() -> Box<Fn(i32) -> i32> {
|
||||
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
|
||||
Box::new(|x| x + 1)
|
||||
}
|
||||
```
|
||||
|
||||
This code will compile just fine. For more about trait objects, refer to the
|
||||
“Trait Objects” section in Chapter 17.
|
||||
“Using Trait Objects That Allow for Values of Different Types” section in
|
||||
Chapter 17.
|
||||
|
||||
## Summary
|
||||
|
||||
@ -143,4 +144,3 @@ you to solutions.
|
||||
|
||||
Next, we’ll put everything we’ve discussed throughout the book into practice
|
||||
and do one more project!
|
||||
|
||||
|
||||
@ -1,34 +1,33 @@
|
||||
# Final Project: Building a Multithreaded Web Server
|
||||
|
||||
It’s been a long journey, but here we are! The end of the book. Parting is such
|
||||
sweet sorrow. But before we go, let’s build one more project together, to show
|
||||
off some of the concepts we covered in these final chapters, as well as recap
|
||||
some lessons from earlier.
|
||||
It’s been a long journey, but we’ve reached the end of the book. In this
|
||||
chapter, we’ll build one more project together to demonstrate some of the
|
||||
concepts we covered in the final chapters, as well as recap some earlier
|
||||
lessons.
|
||||
|
||||
For our final project we’re going to make a web server that only says “hello”;
|
||||
which will look like Figure 20-1 in a web browser:
|
||||
For our final project, we’ll make a web server that says “hello” and looks like
|
||||
Figure 20-1 in a web browser.
|
||||
|
||||

|
||||
|
||||
<span class="caption">Figure 20-1: Our final shared project together</span>
|
||||
<span class="caption">Figure 20-1: Our final shared project</span>
|
||||
|
||||
Here’s the plan of how we’ll build the web server:
|
||||
Here is the plan to build the web server:
|
||||
|
||||
1. Learn a little bit about TCP and HTTP
|
||||
2. Listen for TCP connections on a socket
|
||||
3. Parse a small number of HTTP requests
|
||||
4. Create a proper HTTP response
|
||||
5. Improve the throughput of our server with a thread pool
|
||||
1. Learn a bit about TCP and HTTP.
|
||||
2. Listen for TCP connections on a socket.
|
||||
3. Parse a small number of HTTP requests.
|
||||
4. Create a proper HTTP response.
|
||||
5. Improve the throughput of our server with a thread pool.
|
||||
|
||||
Before we get started, however, there’s one thing we should mention: the method
|
||||
we use here will not be the best way to build a web server with Rust. There are
|
||||
a number of production-ready crates available on *https://crates.io* that
|
||||
provide much more complete web server and thread pool implementations than we
|
||||
are going to build.
|
||||
But before we get started, we should mention one detail: the method we’ll use
|
||||
won’t be the best way to build a web server with Rust. A number of
|
||||
production-ready crates are available on *https://crates.io/* that provide more
|
||||
complete web server and thread pool implementations than we’ll build.
|
||||
|
||||
However, for this chapter, our intention is to help you learn, not to take the
|
||||
easy route. Because Rust is a systems programming language, we’re able to
|
||||
choose what level of abstraction we want to work with, and can go to a lower
|
||||
level than is possible or practical in other languages. We’ll therefore write
|
||||
the basic HTTP server and thread pool ourselves so you can learn the general
|
||||
ideas and techniques behind the crates you might use in the future.
|
||||
However, our intention in this chapter is to help you learn, not to take the
|
||||
easy route. Because Rust is a systems programming language, we can choose the
|
||||
level of abstraction we want to work with and can go to a lower level than is
|
||||
possible or practical in other languages. We’ll write the basic HTTP server and
|
||||
thread pool manually so you can learn the general ideas and techniques behind
|
||||
the crates you might use in the future.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user