mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-06 04:34:06 +00:00
New upstream version 1.24.1+dfsg1
This commit is contained in:
parent
abe05a734d
commit
ff7c6d114e
@ -112,14 +112,17 @@ There are large number of options provided in this config file that will alter t
|
||||
configuration used in the build process. Some options to note:
|
||||
|
||||
#### `[llvm]`:
|
||||
- `assertions = true` = This enables LLVM assertions, which makes LLVM misuse cause an assertion failure instead of weird misbehavior. This also slows down the compiler's runtime by ~20%.
|
||||
- `ccache = true` - Use ccache when building llvm
|
||||
|
||||
#### `[build]`:
|
||||
- `compiler-docs = true` - Build compiler documentation
|
||||
|
||||
#### `[rust]`:
|
||||
- `debuginfo = true` - Build a compiler with debuginfo
|
||||
- `optimize = false` - Disable optimizations to speed up compilation of stage1 rust
|
||||
- `debuginfo = true` - Build a compiler with debuginfo. Makes building rustc slower, but then you can use a debugger to debug `rustc`.
|
||||
- `debuginfo-lines = true` - An alternative to `debuginfo = true` that doesn't let you use a debugger, but doesn't make building rustc slower and still gives you line numbers in backtraces.
|
||||
- `debug-assertions = true` - Makes the log output of `debug!` work.
|
||||
- `optimize = false` - Disable optimizations to speed up compilation of stage1 rust, but makes the stage1 compiler x100 slower.
|
||||
|
||||
For more options, the `config.toml` file contains commented out defaults, with
|
||||
descriptions of what each option will do.
|
||||
@ -273,6 +276,27 @@ build, you'll need to build rustdoc specially, since it's not normally built in
|
||||
stage 1. `python x.py build --stage 1 src/libstd src/tools/rustdoc` will build
|
||||
rustdoc and libstd, which will allow rustdoc to be run with that toolchain.)
|
||||
|
||||
### Out-of-tree builds
|
||||
[out-of-tree-builds]: #out-of-tree-builds
|
||||
|
||||
Rust's `x.py` script fully supports out-of-tree builds - it looks for
|
||||
the Rust source code from the directory `x.py` was found in, but it
|
||||
reads the `config.toml` configuration file from the directory it's
|
||||
run in, and places all build artifacts within a subdirectory named `build`.
|
||||
|
||||
This means that if you want to do an out-of-tree build, you can just do it:
|
||||
```
|
||||
$ cd my/build/dir
|
||||
$ cp ~/my-config.toml config.toml # Or fill in config.toml otherwise
|
||||
$ path/to/rust/x.py build
|
||||
...
|
||||
$ # This will use the Rust source code in `path/to/rust`, but build
|
||||
$ # artifacts will now be in ./build
|
||||
```
|
||||
|
||||
It's absolutely fine to have multiple build directories with different
|
||||
`config.toml` configurations using the same code.
|
||||
|
||||
## Pull Requests
|
||||
[pull-requests]: #pull-requests
|
||||
|
||||
@ -336,7 +360,7 @@ will run all the tests on every platform we support. If it all works out,
|
||||
|
||||
Speaking of tests, Rust has a comprehensive test suite. More information about
|
||||
it can be found
|
||||
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md).
|
||||
[here](https://github.com/rust-lang/rust/blob/master/src/test/COMPILER_TESTS.md).
|
||||
|
||||
### External Dependencies
|
||||
[external-dependencies]: #external-dependencies
|
||||
@ -345,26 +369,29 @@ Currently building Rust will also build the following external projects:
|
||||
|
||||
* [clippy](https://github.com/rust-lang-nursery/rust-clippy)
|
||||
* [miri](https://github.com/solson/miri)
|
||||
* [rustfmt](https://github.com/rust-lang-nursery/rustfmt)
|
||||
* [rls](https://github.com/rust-lang-nursery/rls/)
|
||||
|
||||
If your changes break one of these projects, you need to fix them by opening
|
||||
a pull request against the broken project asking to put the fix on a branch.
|
||||
Then you can disable the tool building via `src/tools/toolstate.toml`.
|
||||
Once the branch containing your fix is likely to be merged, you can point
|
||||
the affected submodule at this branch.
|
||||
We allow breakage of these tools in the nightly channel. Maintainers of these
|
||||
projects will be notified of the breakages and should fix them as soon as
|
||||
possible.
|
||||
|
||||
Don't forget to also add your changes with
|
||||
After the external is fixed, one could add the changes with
|
||||
|
||||
```
|
||||
```sh
|
||||
git add path/to/submodule
|
||||
```
|
||||
|
||||
outside the submodule.
|
||||
|
||||
In order to prepare your PR, you can run the build locally by doing
|
||||
In order to prepare your tool-fixing PR, you can run the build locally by doing
|
||||
`./x.py build src/tools/TOOL`. If you will be editing the sources
|
||||
there, you may wish to set `submodules = false` in the `config.toml`
|
||||
to prevent `x.py` from resetting to the original branch.
|
||||
|
||||
Breakage is not allowed in the beta and stable channels, and must be addressed
|
||||
before the PR is merged.
|
||||
|
||||
#### Breaking Tools Built With The Compiler
|
||||
[breaking-tools-built-with-the-compiler]: #breaking-tools-built-with-the-compiler
|
||||
|
||||
@ -382,12 +409,12 @@ tests.
|
||||
That means that, in the default state, you can't update the compiler without first
|
||||
fixing rustfmt, rls and the other tools that the compiler builds.
|
||||
|
||||
Luckily, a feature was [added to Rust's build](https://github.com/rust-lang/rust/pull/45243)
|
||||
to make all of this easy to handle. The idea is that you mark the tools as "broken",
|
||||
Luckily, a feature was [added to Rust's build](https://github.com/rust-lang/rust/issues/45861)
|
||||
to make all of this easy to handle. The idea is that we allow these tools to be "broken",
|
||||
so that the rust-lang/rust build passes without trying to build them, then land the change
|
||||
in the compiler, wait for a nightly, and go update the tools that you broke. Once you're done
|
||||
and the tools are working again, you go back in the compiler and change the tools back
|
||||
from "broken".
|
||||
and the tools are working again, you go back in the compiler and update the tools
|
||||
so they can be distributed again.
|
||||
|
||||
This should avoid a bunch of synchronization dances and is also much easier on contributors as
|
||||
there's no need to block on rls/rustfmt/other tools changes going upstream.
|
||||
@ -406,22 +433,17 @@ Here are those same steps in detail:
|
||||
4. (optional) Maintainers of these submodules will **not** merge the PR. The PR can't be
|
||||
merged because CI will be broken. You'll want to write a message on the PR referencing
|
||||
your change, and how the PR should be merged once your change makes it into a nightly.
|
||||
5. Update `src/tools/toolstate.toml` to indicate that the tool in question is "broken",
|
||||
that will disable building it on CI. See the documentation in that file for the exact
|
||||
configuration values you can use.
|
||||
6. Commit the changes to `src/tools/toolstate.toml`, **do not update submodules in your commit**,
|
||||
and then update the PR you have for rust-lang/rust.
|
||||
7. Wait for your PR to merge.
|
||||
8. Wait for a nightly
|
||||
9. (optional) Help land your PR on the upstream repository now that your changes are in nightly.
|
||||
10. (optional) Send a PR to rust-lang/rust updating the submodule, reverting `src/tools/toolstate.toml` back to a "building" or "testing" state.
|
||||
5. Wait for your PR to merge.
|
||||
6. Wait for a nightly
|
||||
7. (optional) Help land your PR on the upstream repository now that your changes are in nightly.
|
||||
8. (optional) Send a PR to rust-lang/rust updating the submodule.
|
||||
|
||||
#### Updating submodules
|
||||
[updating-submodules]: #updating-submodules
|
||||
|
||||
These instructions are specific to updating `rustfmt`, however they may apply
|
||||
to the other submodules as well. Please help by improving these instructions
|
||||
if you find any discrepencies or special cases that need to be addressed.
|
||||
if you find any discrepancies or special cases that need to be addressed.
|
||||
|
||||
To update the `rustfmt` submodule, start by running the appropriate
|
||||
[`git submodule` command](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
|
||||
|
@ -129,6 +129,9 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.
|
||||
python x.py build
|
||||
```
|
||||
|
||||
If you are seeing build failure when compiling `rustc_binaryen`, make sure the path
|
||||
length of the rust folder is not longer than 22 characters.
|
||||
|
||||
#### Specifying an ABI
|
||||
[specifying-an-abi]: #specifying-an-abi
|
||||
|
||||
|
256
RELEASES.md
256
RELEASES.md
@ -1,13 +1,237 @@
|
||||
Version 1.22.0 (2017-11-23)
|
||||
Version 1.24.1 (2018-03-01)
|
||||
==========================
|
||||
|
||||
- [Do not abort when unwinding through FFI][48251]
|
||||
- [Emit UTF-16 files for linker arguments on Windows][48318]
|
||||
- [Make the error index generator work again][48308]
|
||||
- [Cargo will warn on Windows 7 if an update is needed][cargo/5069].
|
||||
|
||||
[48251]: https://github.com/rust-lang/rust/issues/48251
|
||||
[48308]: https://github.com/rust-lang/rust/issues/48308
|
||||
[48318]: https://github.com/rust-lang/rust/issues/48318
|
||||
[cargo/5069]: https://github.com/rust-lang/cargo/pull/5069
|
||||
|
||||
Version 1.24.0 (2018-02-15)
|
||||
==========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [External `sysv64` ffi is now available.][46528]
|
||||
eg. `extern "sysv64" fn foo () {}`
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [rustc now uses 16 codegen units by default for release builds.][46910]
|
||||
For the fastest builds, utilize `codegen-units=1`.
|
||||
- [Added `armv4t-unknown-linux-gnueabi` target.][47018]
|
||||
- [Add `aarch64-unknown-openbsd` support][46760]
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [`str::find::<char>` now uses memchr.][46735] This should lead to a 10x
|
||||
improvement in performance in the majority of cases.
|
||||
- [`OsStr`'s `Debug` implementation is now lossless and consistent
|
||||
with Windows.][46798]
|
||||
- [`time::{SystemTime, Instant}` now implement `Hash`.][46828]
|
||||
- [impl `From<bool>` for `AtomicBool`][46293]
|
||||
- [impl `From<{CString, &CStr}>` for `{Arc<CStr>, Rc<CStr>}`][45990]
|
||||
- [impl `From<{OsString, &OsStr}>` for `{Arc<OsStr>, Rc<OsStr>}`][45990]
|
||||
- [impl `From<{PathBuf, &Path}>` for `{Arc<Path>, Rc<Path>}`][45990]
|
||||
- [float::from_bits now just uses transmute.][46012] This provides
|
||||
some optimisations from LLVM.
|
||||
- [Copied `AsciiExt` methods onto `char`][46077]
|
||||
- [Remove `T: Sized` requirement on `ptr::is_null()`][46094]
|
||||
- [impl `From<RecvError>` for `{TryRecvError, RecvTimeoutError}`][45506]
|
||||
- [Optimised `f32::{min, max}` to generate more efficent x86 assembly][47080]
|
||||
- [`[u8]::contains` now uses memchr which provides a 3x speed improvement][46713]
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
- [`RefCell::replace`]
|
||||
- [`RefCell::swap`]
|
||||
- [`atomic::spin_loop_hint`]
|
||||
|
||||
The following functions can now be used in a constant expression.
|
||||
eg. `let buffer: [u8; size_of::<usize>()];`, `static COUNTER: AtomicUsize = AtomicUsize::new(1);`
|
||||
|
||||
- [`AtomicBool::new`][46287]
|
||||
- [`AtomicUsize::new`][46287]
|
||||
- [`AtomicIsize::new`][46287]
|
||||
- [`AtomicPtr::new`][46287]
|
||||
- [`Cell::new`][46287]
|
||||
- [`{integer}::min_value`][46287]
|
||||
- [`{integer}::max_value`][46287]
|
||||
- [`mem::size_of`][46287]
|
||||
- [`mem::align_of`][46287]
|
||||
- [`ptr::null`][46287]
|
||||
- [`ptr::null_mut`][46287]
|
||||
- [`RefCell::new`][46287]
|
||||
- [`UnsafeCell::new`][46287]
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Added a `workspace.default-members` config that
|
||||
overrides implied `--all` in virtual workspaces.][cargo/4743]
|
||||
- [Enable incremental by default on development builds.][cargo/4817] Also added
|
||||
configuration keys to `Cargo.toml` and `.cargo/config` to disable on a
|
||||
per-project or global basis respectively.
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Floating point types `Debug` impl now always prints a decimal point.][46831]
|
||||
- [`Ipv6Addr` now rejects superfluous `::`'s in IPv6 addresses][46671] This is
|
||||
in accordance with IETF RFC 4291 §2.2.
|
||||
- [Unwinding will no longer go past FFI boundaries, and will instead abort.][46833]
|
||||
- [`Formatter::flags` method is now deprecated.][46284] The `sign_plus`,
|
||||
`sign_minus`, `alternate`, and `sign_aware_zero_pad` should be used instead.
|
||||
- [Leading zeros in tuple struct members is now an error][47084]
|
||||
- [`column!()` macro is one-based instead of zero-based][46977]
|
||||
- [`fmt::Arguments` can no longer be shared across threads][45198]
|
||||
- [Access to `#[repr(packed)]` struct fields is now unsafe][44884]
|
||||
|
||||
[44884]: https://github.com/rust-lang/rust/pull/44884
|
||||
[45198]: https://github.com/rust-lang/rust/pull/45198
|
||||
[45506]: https://github.com/rust-lang/rust/pull/45506
|
||||
[45904]: https://github.com/rust-lang/rust/pull/45904
|
||||
[45990]: https://github.com/rust-lang/rust/pull/45990
|
||||
[46012]: https://github.com/rust-lang/rust/pull/46012
|
||||
[46077]: https://github.com/rust-lang/rust/pull/46077
|
||||
[46094]: https://github.com/rust-lang/rust/pull/46094
|
||||
[46284]: https://github.com/rust-lang/rust/pull/46284
|
||||
[46287]: https://github.com/rust-lang/rust/pull/46287
|
||||
[46293]: https://github.com/rust-lang/rust/pull/46293
|
||||
[46528]: https://github.com/rust-lang/rust/pull/46528
|
||||
[46671]: https://github.com/rust-lang/rust/pull/46671
|
||||
[46713]: https://github.com/rust-lang/rust/pull/46713
|
||||
[46735]: https://github.com/rust-lang/rust/pull/46735
|
||||
[46749]: https://github.com/rust-lang/rust/pull/46749
|
||||
[46760]: https://github.com/rust-lang/rust/pull/46760
|
||||
[46798]: https://github.com/rust-lang/rust/pull/46798
|
||||
[46828]: https://github.com/rust-lang/rust/pull/46828
|
||||
[46831]: https://github.com/rust-lang/rust/pull/46831
|
||||
[46833]: https://github.com/rust-lang/rust/pull/46833
|
||||
[46910]: https://github.com/rust-lang/rust/pull/46910
|
||||
[46977]: https://github.com/rust-lang/rust/pull/46977
|
||||
[47018]: https://github.com/rust-lang/rust/pull/47018
|
||||
[47080]: https://github.com/rust-lang/rust/pull/47080
|
||||
[47084]: https://github.com/rust-lang/rust/pull/47084
|
||||
[cargo/4743]: https://github.com/rust-lang/cargo/pull/4743
|
||||
[cargo/4817]: https://github.com/rust-lang/cargo/pull/4817
|
||||
[`RefCell::replace`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.replace
|
||||
[`RefCell::swap`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.swap
|
||||
[`atomic::spin_loop_hint`]: https://doc.rust-lang.org/std/sync/atomic/fn.spin_loop_hint.html
|
||||
|
||||
|
||||
Version 1.23.0 (2018-01-04)
|
||||
==========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Arbitrary `auto` traits are now permitted in trait objects.][45772]
|
||||
- [rustc now uses subtyping on the left hand side of binary operations.][45435]
|
||||
Which should fix some confusing errors in some operations.
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Enabled `TrapUnreachable` in LLVM which should mitigate the impact of
|
||||
undefined behaviour.][45920]
|
||||
- [rustc now suggests renaming import if names clash.][45660]
|
||||
- [Display errors/warnings correctly when there are zero-width or
|
||||
wide characters.][45711]
|
||||
- [rustc now avoids unnecessary copies of arguments that are
|
||||
simple bindings][45380] This should improve memory usage on average by 5-10%.
|
||||
- [Updated musl used to build musl rustc to 1.1.17][45393]
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Allow a trailing comma in `assert_eq/ne` macro][45887]
|
||||
- [Implement Hash for raw pointers to unsized types][45483]
|
||||
- [impl `From<*mut T>` for `AtomicPtr<T>`][45610]
|
||||
- [impl `From<usize/isize>` for `AtomicUsize/AtomicIsize`.][45610]
|
||||
- [Removed the `T: Sync` requirement for `RwLock<T>: Send`][45267]
|
||||
- [Removed `T: Sized` requirement for `{<*const T>, <*mut T>}::as_ref`
|
||||
and `<*mut T>::as_mut`][44932]
|
||||
- [Optimized `Thread::{park, unpark}` implementation][45524]
|
||||
- [Improved `SliceExt::binary_search` performance.][45333]
|
||||
- [impl `FromIterator<()>` for `()`][45379]
|
||||
- [Copied `AsciiExt` trait methods to primitive types.][44042] Use of `AsciiExt`
|
||||
is now deprecated.
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [Cargo now supports alternative registries][cargo/4506]
|
||||
- [Cargo now supports uninstallation of multiple packages][cargo/4561]
|
||||
eg. `cargo uninstall foo bar` uninstalls `foo` and `bar`.
|
||||
- [Added unit test checking to `cargo check`][cargo/4592]
|
||||
- [Cargo now lets you install a specific version
|
||||
using `cargo install --version`][cargo/4637]
|
||||
|
||||
Misc
|
||||
----
|
||||
- [Releases now ship with the Cargo book documentation.][45692]
|
||||
- [rustdoc now prints rendering warnings on every run.][45324]
|
||||
- [Release tarballs now come with rustfmt][45903]
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Changes have been made to type equality to make it more correct,
|
||||
in rare cases this could break some code.][45853] [Tracking issue for
|
||||
further information][45852]
|
||||
- [`char::escape_debug` now uses Unicode 10 over 9.][45571]
|
||||
- [Upgraded Android SDK to 27, and NDK to r15c.][45580] This drops support for
|
||||
Android 9, the minimum supported version is Android 14.
|
||||
- [Bumped the minimum LLVM to 3.9][45326]
|
||||
|
||||
[44042]: https://github.com/rust-lang/rust/pull/44042
|
||||
[44932]: https://github.com/rust-lang/rust/pull/44932
|
||||
[45267]: https://github.com/rust-lang/rust/pull/45267
|
||||
[45324]: https://github.com/rust-lang/rust/pull/45324
|
||||
[45326]: https://github.com/rust-lang/rust/pull/45326
|
||||
[45333]: https://github.com/rust-lang/rust/pull/45333
|
||||
[45379]: https://github.com/rust-lang/rust/pull/45379
|
||||
[45380]: https://github.com/rust-lang/rust/pull/45380
|
||||
[45393]: https://github.com/rust-lang/rust/pull/45393
|
||||
[45435]: https://github.com/rust-lang/rust/pull/45435
|
||||
[45483]: https://github.com/rust-lang/rust/pull/45483
|
||||
[45524]: https://github.com/rust-lang/rust/pull/45524
|
||||
[45571]: https://github.com/rust-lang/rust/pull/45571
|
||||
[45580]: https://github.com/rust-lang/rust/pull/45580
|
||||
[45610]: https://github.com/rust-lang/rust/pull/45610
|
||||
[45660]: https://github.com/rust-lang/rust/pull/45660
|
||||
[45692]: https://github.com/rust-lang/rust/pull/45692
|
||||
[45711]: https://github.com/rust-lang/rust/pull/45711
|
||||
[45772]: https://github.com/rust-lang/rust/pull/45772
|
||||
[45852]: https://github.com/rust-lang/rust/issues/45852
|
||||
[45853]: https://github.com/rust-lang/rust/pull/45853
|
||||
[45887]: https://github.com/rust-lang/rust/pull/45887
|
||||
[45903]: https://github.com/rust-lang/rust/pull/45903
|
||||
[45920]: https://github.com/rust-lang/rust/pull/45920
|
||||
[cargo/4506]: https://github.com/rust-lang/cargo/pull/4506
|
||||
[cargo/4561]: https://github.com/rust-lang/cargo/pull/4561
|
||||
[cargo/4592]: https://github.com/rust-lang/cargo/pull/4592
|
||||
[cargo/4637]: https://github.com/rust-lang/cargo/pull/4637
|
||||
|
||||
|
||||
Version 1.22.1 (2017-11-22)
|
||||
==========================
|
||||
|
||||
- [Update Cargo to fix an issue with macOS 10.13 "High Sierra"][46183]
|
||||
|
||||
[46183]: https://github.com/rust-lang/rust/pull/46183
|
||||
|
||||
Version 1.22.0 (2017-11-22)
|
||||
==========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [`non_snake_case` lint now allows extern no-mangle functions][44966]
|
||||
- [Now accepts underscores in unicode escapes][43716]
|
||||
- [`#![feature(const_fn)]` is now no longer required for
|
||||
calling const functions.][43017] It's still required for creating
|
||||
constant functions.
|
||||
- [`T op= &T` now works for numeric types.][44287] eg. `let mut x = 2; x += &8;`
|
||||
- [types that impl `Drop` are now allowed in `const` and `static` types][44456]
|
||||
|
||||
@ -45,8 +269,8 @@ Cargo
|
||||
Misc
|
||||
----
|
||||
- [`libbacktrace` is now available on Apple platforms.][44251]
|
||||
- [Stabilised the `compile_fail` attribute for code fences.][43949] This now
|
||||
lets you specify that a given code example will fail to compile.
|
||||
- [Stabilised the `compile_fail` attribute for code fences in doc-comments.][43949]
|
||||
This now lets you specify that a given code example will fail to compile.
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
@ -624,7 +848,7 @@ Misc
|
||||
----
|
||||
|
||||
- [rustdoc can now use pulldown-cmark with the `--enable-commonmark` flag][40338]
|
||||
- [Added rust-winbg script for better debugging on Windows][39983]
|
||||
- [Added rust-windbg script for better debugging on Windows][39983]
|
||||
- [Rust now uses the official cross compiler for NetBSD][40612]
|
||||
- [rustdoc now accepts `#` at the start of files][40828]
|
||||
- [Fixed jemalloc support for musl][41168]
|
||||
@ -1658,7 +1882,7 @@ Diagnostics
|
||||
-----------
|
||||
|
||||
* [Replace macro backtraces with labeled local uses][35702]
|
||||
* [Improve error message for missplaced doc comments][33922]
|
||||
* [Improve error message for misplaced doc comments][33922]
|
||||
* [Buffer unix and lock windows to prevent message interleaving][35975]
|
||||
* [Update lifetime errors to specifically note temporaries][36171]
|
||||
* [Special case a few colors for Windows][36178]
|
||||
@ -1966,7 +2190,7 @@ Language
|
||||
useful](https://github.com/rust-lang/rust/pull/34908)
|
||||
* [`macro_rules!` `stmt` matchers correctly consume the entire contents when
|
||||
inside non-braces invocations](https://github.com/rust-lang/rust/pull/34886)
|
||||
* [Semicolons are properly required as statement delimeters inside
|
||||
* [Semicolons are properly required as statement delimiters inside
|
||||
`macro_rules!` invocations](https://github.com/rust-lang/rust/pull/34660)
|
||||
* [`cfg_attr` works on `path` attributes](https://github.com/rust-lang/rust/pull/34546)
|
||||
|
||||
@ -2191,7 +2415,7 @@ Compatibility Notes
|
||||
* [`const`s and `static`s may not have unsized types](https://github.com/rust-lang/rust/pull/34443)
|
||||
* [The new follow-set rules that place restrictions on `macro_rules!`
|
||||
in order to ensure syntax forward-compatibility have been enabled](https://github.com/rust-lang/rust/pull/33982)
|
||||
This was an [ammendment to RFC 550](https://github.com/rust-lang/rfcs/pull/1384),
|
||||
This was an [amendment to RFC 550](https://github.com/rust-lang/rfcs/pull/1384),
|
||||
and has been a warning since 1.10.
|
||||
* [`cfg` attribute process has been refactored to fix various bugs](https://github.com/rust-lang/rust/pull/33706).
|
||||
This causes breakage in some corner cases.
|
||||
@ -3348,7 +3572,7 @@ Libraries
|
||||
* `FromStr` is [implemented for `SockAddrV4` and `SockAddrV6`][1.5s].
|
||||
* There are now `From` conversions [between floating point
|
||||
types][1.5f] where the conversions are lossless.
|
||||
* Thera are now `From` conversions [between integer types][1.5i] where
|
||||
* There are now `From` conversions [between integer types][1.5i] where
|
||||
the conversions are lossless.
|
||||
* [`fs::Metadata` implements `Clone`][1.5fs].
|
||||
* The `parse` method [accepts a leading "+" when parsing
|
||||
@ -3548,7 +3772,7 @@ Libraries
|
||||
* [`IntoIterator` is implemented for references to `Option` and
|
||||
`Result`][into2].
|
||||
* [`HashMap` and `HashSet` implement `Extend<&T>` where `T:
|
||||
Copy`][ext] as part of [RFC 839]. This will cause type inferance
|
||||
Copy`][ext] as part of [RFC 839]. This will cause type inference
|
||||
breakage in rare situations.
|
||||
* [`BinaryHeap` implements `Debug`][bh2].
|
||||
* [`Borrow` and `BorrowMut` are implemented for fixed-size
|
||||
@ -3559,7 +3783,7 @@ Libraries
|
||||
* `&mut T` where `T: std::fmt::Write` [also implements
|
||||
`std::fmt::Write`][mutw].
|
||||
* [A stable regression in `VecDeque::push_back` and other
|
||||
capicity-altering methods that caused panics for zero-sized types
|
||||
capacity-altering methods that caused panics for zero-sized types
|
||||
was fixed][vd].
|
||||
* [Function pointers implement traits for up to 12 parameters][fp2].
|
||||
|
||||
@ -3746,7 +3970,7 @@ Libraries
|
||||
[better for long data][sh].
|
||||
* [`AtomicPtr`] implements [`Send`].
|
||||
* The [`read_to_end`] implementations for [`Stdin`] and [`File`]
|
||||
are now [specialized to use uninitalized buffers for increased
|
||||
are now [specialized to use uninitialized buffers for increased
|
||||
performance][rte].
|
||||
* Lifetime parameters of foreign functions [are now resolved
|
||||
properly][f].
|
||||
@ -3875,7 +4099,7 @@ Highlights
|
||||
* This is the first release with [experimental support for linking
|
||||
with the MSVC linker and lib C on Windows (instead of using the GNU
|
||||
variants via MinGW)][win]. It is yet recommended only for the most
|
||||
intrepid Rusticians.
|
||||
intrepid Rustaceans.
|
||||
* Benchmark compilations are showing a 30% improvement in
|
||||
bootstrapping over 1.1.
|
||||
|
||||
@ -4741,7 +4965,7 @@ Version 0.11.0 (2014-07-02)
|
||||
* Libraries
|
||||
* The standard library is now a "facade" over a number of underlying
|
||||
libraries. This means that development on the standard library should
|
||||
be speeder due to smaller crates, as well as a clearer line between
|
||||
be speedier due to smaller crates, as well as a clearer line between
|
||||
all dependencies.
|
||||
* A new library, libcore, lives under the standard library's facade
|
||||
which is Rust's "0-assumption" library, suitable for embedded and
|
||||
|
@ -60,10 +60,9 @@
|
||||
# LLVM experimental targets to build support for. These targets are specified in
|
||||
# the same format as above, but since these targets are experimental, they are
|
||||
# not built by default and the experimental Rust compilation targets that depend
|
||||
# on them will not work unless the user opts in to building them. Possible
|
||||
# experimental LLVM targets include WebAssembly for the
|
||||
# wasm32-experimental-emscripten Rust target.
|
||||
#experimental-targets = ""
|
||||
# on them will not work unless the user opts in to building them. By default the
|
||||
# `WebAssembly` target is enabled when compiling LLVM from scratch.
|
||||
#experimental-targets = "WebAssembly"
|
||||
|
||||
# Cap the number of parallel linker invocations when compiling LLVM.
|
||||
# This can be useful when building LLVM with debug info, which significantly
|
||||
@ -302,6 +301,10 @@
|
||||
# As a side-effect also generates MIR for all libraries.
|
||||
#test-miri = false
|
||||
|
||||
# After building or testing extended tools (e.g. clippy and rustfmt), append the
|
||||
# result (broken, compiling, testing) into this JSON file.
|
||||
#save-toolstates = "/path/to/toolstates.json"
|
||||
|
||||
# =============================================================================
|
||||
# Options for specific targets
|
||||
#
|
||||
|
@ -1 +1 @@
|
||||
766bd11c8a3c019ca53febdcd77b2215379dd67d
|
||||
d3ae9a9e08edf12de0ed82af57ba2a56c26496ea
|
882
src/Cargo.lock
generated
882
src/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -20,21 +20,22 @@ members = [
|
||||
"tools/rustdoc",
|
||||
"tools/rls",
|
||||
"tools/rustfmt",
|
||||
"tools/miri",
|
||||
# FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude
|
||||
"tools/rls/test_data/bin_lib",
|
||||
"tools/rls/test_data/borrow_error",
|
||||
"tools/rls/test_data/common",
|
||||
"tools/rls/test_data/deglob",
|
||||
"tools/rls/test_data/features",
|
||||
"tools/rls/test_data/find_all_refs_no_cfg_test",
|
||||
"tools/rls/test_data/reformat",
|
||||
"tools/rls/test_data/multiple_bins",
|
||||
"tools/rls/test_data/bin_lib",
|
||||
"tools/rls/test_data/reformat_with_range",
|
||||
"tools/rls/test_data/find_impls",
|
||||
"tools/rls/test_data/infer_bin",
|
||||
"tools/rls/test_data/infer_custom_bin",
|
||||
"tools/rls/test_data/infer_lib",
|
||||
"tools/rls/test_data/multiple_bins",
|
||||
"tools/rls/test_data/reformat",
|
||||
"tools/rls/test_data/reformat_with_range",
|
||||
"tools/rls/test_data/workspace_symbol",
|
||||
"tools/rls/test_data/deglob",
|
||||
]
|
||||
|
||||
# Curiously, compiletest will segfault if compiled with opt-level=3 on 64-bit
|
||||
|
@ -41,3 +41,4 @@ serde_derive = "1.0.8"
|
||||
serde_json = "1.0.2"
|
||||
toml = "0.4"
|
||||
lazy_static = "0.2"
|
||||
time = "0.1"
|
||||
|
@ -175,7 +175,7 @@ fn main() {
|
||||
if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
|
||||
cmd.arg("-C").arg(format!("codegen-units={}", s));
|
||||
}
|
||||
if stage != "0" && env::var("RUSTC_THINLTO").is_ok() {
|
||||
if env::var("RUSTC_THINLTO").is_ok() {
|
||||
cmd.arg("-Ccodegen-units=16").arg("-Zthinlto");
|
||||
}
|
||||
|
||||
@ -183,7 +183,8 @@ fn main() {
|
||||
if env::var("RUSTC_SAVE_ANALYSIS") == Ok("api".to_string()) {
|
||||
cmd.arg("-Zsave-analysis");
|
||||
cmd.env("RUST_SAVE_ANALYSIS_CONFIG",
|
||||
"{\"output_file\": null,\"full_docs\": false,\"pub_only\": true,\
|
||||
"{\"output_file\": null,\"full_docs\": false,\
|
||||
\"pub_only\": true,\"reachable_only\": false,\
|
||||
\"distro_crate\": true,\"signatures\": false,\"borrow_data\": false}");
|
||||
}
|
||||
|
||||
@ -245,6 +246,9 @@ fn main() {
|
||||
// When running miri tests, we need to generate MIR for all libraries
|
||||
if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
|
||||
cmd.arg("-Zalways-encode-mir");
|
||||
if stage != "0" {
|
||||
cmd.arg("-Zmiri");
|
||||
}
|
||||
cmd.arg("-Zmir-emit-validate=1");
|
||||
}
|
||||
|
||||
@ -261,6 +265,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() {
|
||||
cmd.arg("--cfg").arg("parallel_queries");
|
||||
}
|
||||
|
||||
let color = match env::var("RUSTC_COLOR") {
|
||||
Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
|
||||
Err(_) => 0,
|
||||
|
@ -57,6 +57,10 @@ fn main() {
|
||||
// This "unstable-options" can be removed when `--crate-version` is stabilized
|
||||
cmd.arg("-Z").arg("unstable-options")
|
||||
.arg("--crate-version").arg(version);
|
||||
|
||||
// While we can assume that `-Z unstable-options` is set, let's also force rustdoc to panic
|
||||
// if pulldown rendering differences are found
|
||||
cmd.arg("--deny-render-differences");
|
||||
}
|
||||
|
||||
std::process::exit(match cmd.status() {
|
||||
|
@ -484,8 +484,8 @@ impl<'a> Builder<'a> {
|
||||
} else {
|
||||
PathBuf::from("/path/to/nowhere/rustdoc/not/required")
|
||||
})
|
||||
.env("TEST_MIRI", self.config.test_miri.to_string());
|
||||
|
||||
.env("TEST_MIRI", self.config.test_miri.to_string())
|
||||
.env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir());
|
||||
if let Some(n) = self.config.rust_codegen_units {
|
||||
cargo.env("RUSTC_CODEGEN_UNITS", n.to_string());
|
||||
}
|
||||
@ -500,9 +500,10 @@ impl<'a> Builder<'a> {
|
||||
if mode != Mode::Tool {
|
||||
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
||||
// get compiled with debuginfo.
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
|
||||
.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string())
|
||||
.env("RUSTC_FORCE_UNSTABLE", "1");
|
||||
// Adding debuginfo increases their sizes by a factor of 3-4.
|
||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
|
||||
cargo.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string());
|
||||
cargo.env("RUSTC_FORCE_UNSTABLE", "1");
|
||||
|
||||
// Currently the compiler depends on crates from crates.io, and
|
||||
// then other crates can depend on the compiler (e.g. proc-macro
|
||||
@ -625,9 +626,7 @@ impl<'a> Builder<'a> {
|
||||
cargo.arg("--release");
|
||||
}
|
||||
|
||||
if mode != Mode::Libstd && // FIXME(#45320)
|
||||
mode != Mode::Libtest && // FIXME(#45511)
|
||||
self.config.rust_codegen_units.is_none() &&
|
||||
if self.config.rust_codegen_units.is_none() &&
|
||||
self.build.is_rust_llvm(compiler.host)
|
||||
{
|
||||
cargo.env("RUSTC_THINLTO", "1");
|
||||
|
@ -24,12 +24,7 @@ use Build;
|
||||
use config::Config;
|
||||
|
||||
// The version number
|
||||
pub const CFG_RELEASE_NUM: &str = "1.23.0";
|
||||
|
||||
// An optional number to put after the label, e.g. '.2' -> '-beta.2'
|
||||
// Be sure to make this starts with a dot to conform to semver pre-release
|
||||
// versions (section 9)
|
||||
pub const CFG_PRERELEASE_VERSION: &str = ".2";
|
||||
pub const CFG_RELEASE_NUM: &str = "1.24.1";
|
||||
|
||||
pub struct GitInfo {
|
||||
inner: Option<Info>,
|
||||
|
@ -23,7 +23,7 @@ use std::path::{PathBuf, Path};
|
||||
use std::process::Command;
|
||||
use std::io::Read;
|
||||
|
||||
use build_helper::{self, output, BuildExpectation};
|
||||
use build_helper::{self, output};
|
||||
|
||||
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
|
||||
use cache::{INTERNER, Interned};
|
||||
@ -65,19 +65,17 @@ impl fmt::Display for TestKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_run_expecting(build: &Build, cmd: &mut Command, expect: BuildExpectation) {
|
||||
fn try_run(build: &Build, cmd: &mut Command) -> bool {
|
||||
if !build.fail_fast {
|
||||
if !build.try_run(cmd, expect) {
|
||||
if !build.try_run(cmd) {
|
||||
let mut failures = build.delayed_failures.borrow_mut();
|
||||
failures.push(format!("{:?}", cmd));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
build.run_expecting(cmd, expect);
|
||||
build.run(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_run(build: &Build, cmd: &mut Command) {
|
||||
try_run_expecting(build, cmd, BuildExpectation::None)
|
||||
true
|
||||
}
|
||||
|
||||
fn try_run_quiet(build: &Build, cmd: &mut Command) {
|
||||
@ -257,11 +255,9 @@ impl Step for Rls {
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.rls.passes(ToolState::Testing),
|
||||
);
|
||||
if try_run(build, &mut cargo) {
|
||||
build.save_toolstate("rls", ToolState::TestPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,16 +301,15 @@ impl Step for Rustfmt {
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.rustfmt.passes(ToolState::Testing),
|
||||
);
|
||||
if try_run(build, &mut cargo) {
|
||||
build.save_toolstate("rustfmt", ToolState::TestPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Miri {
|
||||
stage: u32,
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
@ -330,6 +325,7 @@ impl Step for Miri {
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Miri {
|
||||
stage: run.builder.top_stage,
|
||||
host: run.target,
|
||||
});
|
||||
}
|
||||
@ -337,8 +333,9 @@ impl Step for Miri {
|
||||
/// Runs `cargo test` for miri.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let host = self.host;
|
||||
let compiler = builder.compiler(1, host);
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
if let Some(miri) = builder.ensure(tool::Miri { compiler, target: self.host }) {
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
@ -354,11 +351,9 @@ impl Step for Miri {
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.miri.passes(ToolState::Testing),
|
||||
);
|
||||
if try_run(build, &mut cargo) {
|
||||
build.save_toolstate("miri", ToolState::TestPass);
|
||||
}
|
||||
} else {
|
||||
eprintln!("failed to test miri: could not build");
|
||||
}
|
||||
@ -411,11 +406,9 @@ impl Step for Clippy {
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.clippy.passes(ToolState::Testing),
|
||||
);
|
||||
if try_run(build, &mut cargo) {
|
||||
build.save_toolstate("clippy-driver", ToolState::TestPass);
|
||||
}
|
||||
} else {
|
||||
eprintln!("failed to test clippy: could not build");
|
||||
}
|
||||
@ -575,6 +568,11 @@ static HOST_COMPILETESTS: &[Test] = &[
|
||||
mode: "compile-fail",
|
||||
suite: "compile-fail-fulldeps",
|
||||
},
|
||||
Test {
|
||||
path: "src/test/incremental-fulldeps",
|
||||
mode: "incremental",
|
||||
suite: "incremental-fulldeps",
|
||||
},
|
||||
Test { path: "src/test/run-make", mode: "run-make", suite: "run-make" },
|
||||
Test { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" },
|
||||
|
||||
@ -756,6 +754,7 @@ impl Step for Compiletest {
|
||||
if build.config.rust_debuginfo_tests {
|
||||
flags.push("-g".to_string());
|
||||
}
|
||||
flags.push("-Zmiri -Zunstable-options".to_string());
|
||||
|
||||
if let Some(linker) = build.linker(target) {
|
||||
cmd.arg("--linker").arg(linker);
|
||||
@ -981,7 +980,8 @@ impl Step for ErrorIndex {
|
||||
build.run(builder.tool_cmd(Tool::ErrorIndex)
|
||||
.arg("markdown")
|
||||
.arg(&output)
|
||||
.env("CFG_BUILD", &build.build));
|
||||
.env("CFG_BUILD", &build.build)
|
||||
.env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir()));
|
||||
|
||||
markdown_test(builder, compiler, &output);
|
||||
}
|
||||
@ -1245,6 +1245,17 @@ impl Step for Crate {
|
||||
if target.contains("emscripten") {
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
build.config.nodejs.as_ref().expect("nodejs not configured"));
|
||||
} else if target.starts_with("wasm32") {
|
||||
// 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 = build.config.nodejs.as_ref()
|
||||
.expect("nodejs not configured");
|
||||
let runner = format!("{} {}/src/etc/wasm32-shim.js",
|
||||
node.display(),
|
||||
build.src.display());
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)), &runner);
|
||||
} else if build.remote_tested(target) {
|
||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||
format!("{} run",
|
||||
|
@ -550,6 +550,7 @@ pub fn rustc_cargo(build: &Build,
|
||||
// Building with a static libstdc++ is only supported on linux right now,
|
||||
// not for MSVC or macOS
|
||||
if build.config.llvm_static_stdcpp &&
|
||||
!target.contains("freebsd") &&
|
||||
!target.contains("windows") &&
|
||||
!target.contains("apple") {
|
||||
cargo.env("LLVM_STATIC_STDCPP",
|
||||
@ -561,6 +562,9 @@ pub fn rustc_cargo(build: &Build,
|
||||
if let Some(ref s) = build.config.rustc_default_linker {
|
||||
cargo.env("CFG_DEFAULT_LINKER", s);
|
||||
}
|
||||
if build.config.rustc_parallel_queries {
|
||||
cargo.env("RUSTC_PARALLEL_QUERIES", "1");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -27,7 +27,6 @@ use util::exe;
|
||||
use cache::{INTERNER, Interned};
|
||||
use flags::Flags;
|
||||
pub use flags::Subcommand;
|
||||
use toolstate::ToolStates;
|
||||
|
||||
/// Global configuration for the entire build and/or bootstrap.
|
||||
///
|
||||
@ -76,7 +75,7 @@ pub struct Config {
|
||||
pub llvm_static_stdcpp: bool,
|
||||
pub llvm_link_shared: bool,
|
||||
pub llvm_targets: Option<String>,
|
||||
pub llvm_experimental_targets: Option<String>,
|
||||
pub llvm_experimental_targets: String,
|
||||
pub llvm_link_jobs: Option<u32>,
|
||||
|
||||
// rust codegen options
|
||||
@ -87,6 +86,7 @@ pub struct Config {
|
||||
pub rust_debuginfo_lines: bool,
|
||||
pub rust_debuginfo_only_std: bool,
|
||||
pub rust_rpath: bool,
|
||||
pub rustc_parallel_queries: bool,
|
||||
pub rustc_default_linker: Option<String>,
|
||||
pub rust_optimize_tests: bool,
|
||||
pub rust_debuginfo_tests: bool,
|
||||
@ -112,6 +112,8 @@ pub struct Config {
|
||||
pub channel: String,
|
||||
pub quiet_tests: bool,
|
||||
pub test_miri: bool,
|
||||
pub save_toolstates: Option<PathBuf>,
|
||||
|
||||
// Fallback musl-root for all targets
|
||||
pub musl_root: Option<PathBuf>,
|
||||
pub prefix: Option<PathBuf>,
|
||||
@ -131,8 +133,6 @@ pub struct Config {
|
||||
// These are either the stage0 downloaded binaries or the locally installed ones.
|
||||
pub initial_cargo: PathBuf,
|
||||
pub initial_rustc: PathBuf,
|
||||
|
||||
pub toolstate: ToolStates,
|
||||
}
|
||||
|
||||
/// Per-target configuration stored in the global configuration structure.
|
||||
@ -264,6 +264,7 @@ struct Rust {
|
||||
debuginfo: Option<bool>,
|
||||
debuginfo_lines: Option<bool>,
|
||||
debuginfo_only_std: Option<bool>,
|
||||
experimental_parallel_queries: Option<bool>,
|
||||
debug_jemalloc: Option<bool>,
|
||||
use_jemalloc: Option<bool>,
|
||||
backtrace: Option<bool>,
|
||||
@ -279,6 +280,7 @@ struct Rust {
|
||||
dist_src: Option<bool>,
|
||||
quiet_tests: Option<bool>,
|
||||
test_miri: Option<bool>,
|
||||
save_toolstates: Option<String>,
|
||||
}
|
||||
|
||||
/// TOML representation of how each build target is configured.
|
||||
@ -343,18 +345,6 @@ impl Config {
|
||||
}
|
||||
}).unwrap_or_else(|| TomlConfig::default());
|
||||
|
||||
let toolstate_toml_path = config.src.join("src/tools/toolstate.toml");
|
||||
let parse_toolstate = || -> Result<_, Box<::std::error::Error>> {
|
||||
let mut f = File::open(toolstate_toml_path)?;
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
Ok(toml::from_str(&contents)?)
|
||||
};
|
||||
config.toolstate = parse_toolstate().unwrap_or_else(|err| {
|
||||
println!("failed to parse TOML configuration 'toolstate.toml': {}", err);
|
||||
process::exit(2);
|
||||
});
|
||||
|
||||
let build = toml.build.clone().unwrap_or(Build::default());
|
||||
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
|
||||
set(&mut config.build, flags.build);
|
||||
@ -447,7 +437,8 @@ impl Config {
|
||||
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
|
||||
set(&mut config.llvm_link_shared, llvm.link_shared);
|
||||
config.llvm_targets = llvm.targets.clone();
|
||||
config.llvm_experimental_targets = llvm.experimental_targets.clone();
|
||||
config.llvm_experimental_targets = llvm.experimental_targets.clone()
|
||||
.unwrap_or("WebAssembly".to_string());
|
||||
config.llvm_link_jobs = llvm.link_jobs;
|
||||
}
|
||||
|
||||
@ -470,8 +461,10 @@ impl Config {
|
||||
set(&mut config.rust_dist_src, rust.dist_src);
|
||||
set(&mut config.quiet_tests, rust.quiet_tests);
|
||||
set(&mut config.test_miri, rust.test_miri);
|
||||
config.rustc_parallel_queries = rust.experimental_parallel_queries.unwrap_or(false);
|
||||
config.rustc_default_linker = rust.default_linker.clone();
|
||||
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
|
||||
config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from);
|
||||
|
||||
match rust.codegen_units {
|
||||
Some(0) => config.rust_codegen_units = Some(num_cpus::get() as u32),
|
||||
|
@ -77,6 +77,7 @@ o("debuginfo", "rust.debuginfo", "build with debugger metadata")
|
||||
o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
|
||||
o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
|
||||
o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill")
|
||||
v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
|
||||
|
||||
v("prefix", "install.prefix", "set installation prefix")
|
||||
v("localstatedir", "install.localstatedir", "local state directory")
|
||||
|
@ -28,11 +28,12 @@ use build_helper::output;
|
||||
|
||||
use {Build, Compiler, Mode};
|
||||
use channel;
|
||||
use util::{cp_r, libdir, is_dylib, cp_filtered, copy};
|
||||
use util::{cp_r, libdir, is_dylib, cp_filtered, copy, replace_in_file};
|
||||
use builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
use compile;
|
||||
use tool::{self, Tool};
|
||||
use cache::{INTERNER, Interned};
|
||||
use time;
|
||||
|
||||
pub fn pkgname(build: &Build, component: &str) -> String {
|
||||
if component == "cargo" {
|
||||
@ -434,7 +435,21 @@ impl Step for Rustc {
|
||||
|
||||
// Man pages
|
||||
t!(fs::create_dir_all(image.join("share/man/man1")));
|
||||
cp_r(&build.src.join("src/doc/man"), &image.join("share/man/man1"));
|
||||
let man_src = build.src.join("src/doc/man");
|
||||
let man_dst = image.join("share/man/man1");
|
||||
let month_year = t!(time::strftime("%B %Y", &time::now()));
|
||||
// don't use our `bootstrap::util::{copy, cp_r}`, because those try
|
||||
// to hardlink, and we don't want to edit the source templates
|
||||
for entry_result in t!(fs::read_dir(man_src)) {
|
||||
let file_entry = t!(entry_result);
|
||||
let page_src = file_entry.path();
|
||||
let page_dst = man_dst.join(file_entry.file_name());
|
||||
t!(fs::copy(&page_src, &page_dst));
|
||||
// template in month/year and version number
|
||||
replace_in_file(&page_dst,
|
||||
&[("<INSERT DATE HERE>", &month_year),
|
||||
("<INSERT VERSION HERE>", channel::CFG_RELEASE_NUM)]);
|
||||
}
|
||||
|
||||
// Debugger scripts
|
||||
builder.ensure(DebuggerScripts {
|
||||
@ -489,6 +504,7 @@ impl Step for DebuggerScripts {
|
||||
install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"),
|
||||
0o755);
|
||||
|
||||
cp_debugger_script("natvis/intrinsic.natvis");
|
||||
cp_debugger_script("natvis/liballoc.natvis");
|
||||
cp_debugger_script("natvis/libcore.natvis");
|
||||
} else {
|
||||
@ -1061,11 +1077,6 @@ impl Step for Rls {
|
||||
let target = self.target;
|
||||
assert!(build.config.extended);
|
||||
|
||||
if !builder.config.toolstate.rls.testing() {
|
||||
println!("skipping Dist RLS stage{} ({})", stage, target);
|
||||
return None
|
||||
}
|
||||
|
||||
println!("Dist RLS stage{} ({})", stage, target);
|
||||
let src = build.src.join("src/tools/rls");
|
||||
let release_num = build.release_num("rls");
|
||||
@ -1083,7 +1094,8 @@ impl Step for Rls {
|
||||
let rls = builder.ensure(tool::Rls {
|
||||
compiler: builder.compiler(stage, build.build),
|
||||
target
|
||||
}).expect("Rls to build: toolstate is testing");
|
||||
}).or_else(|| { println!("Unable to build RLS, skipping dist"); None })?;
|
||||
|
||||
install(&rls, &image.join("bin"), 0o755);
|
||||
let doc = image.join("share/doc/rls");
|
||||
install(&src.join("README.md"), &doc, 0o644);
|
||||
@ -1147,11 +1159,6 @@ impl Step for Rustfmt {
|
||||
let target = self.target;
|
||||
assert!(build.config.extended);
|
||||
|
||||
if !builder.config.toolstate.rustfmt.testing() {
|
||||
println!("skipping Dist Rustfmt stage{} ({})", stage, target);
|
||||
return None
|
||||
}
|
||||
|
||||
println!("Dist Rustfmt stage{} ({})", stage, target);
|
||||
let src = build.src.join("src/tools/rustfmt");
|
||||
let release_num = build.release_num("rustfmt");
|
||||
@ -1167,8 +1174,14 @@ impl Step for Rustfmt {
|
||||
let rustfmt = builder.ensure(tool::Rustfmt {
|
||||
compiler: builder.compiler(stage, build.build),
|
||||
target
|
||||
}).expect("Rustfmt to build: toolstate is testing");
|
||||
}).or_else(|| { println!("Unable to build Rustfmt, skipping dist"); None })?;
|
||||
let cargofmt = builder.ensure(tool::Cargofmt {
|
||||
compiler: builder.compiler(stage, build.build),
|
||||
target
|
||||
}).or_else(|| { println!("Unable to build Cargofmt, skipping dist"); None })?;
|
||||
|
||||
install(&rustfmt, &image.join("bin"), 0o755);
|
||||
install(&cargofmt, &image.join("bin"), 0o755);
|
||||
let doc = image.join("share/doc/rustfmt");
|
||||
install(&src.join("README.md"), &doc, 0o644);
|
||||
install(&src.join("LICENSE-MIT"), &doc, 0o644);
|
||||
@ -1637,7 +1650,6 @@ fn add_env(build: &Build, cmd: &mut Command, target: Interned<String>) {
|
||||
cmd.env("CFG_RELEASE_INFO", build.rust_version())
|
||||
.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM)
|
||||
.env("CFG_RELEASE", build.rust_release())
|
||||
.env("CFG_PRERELEASE_VERSION", channel::CFG_PRERELEASE_VERSION)
|
||||
.env("CFG_VER_MAJOR", parts.next().unwrap())
|
||||
.env("CFG_VER_MINOR", parts.next().unwrap())
|
||||
.env("CFG_VER_PATCH", parts.next().unwrap())
|
||||
|
@ -671,7 +671,8 @@ impl Step for ErrorIndex {
|
||||
index.arg(out.join("error-index.html"));
|
||||
|
||||
// FIXME: shouldn't have to pass this env var
|
||||
index.env("CFG_BUILD", &build.build);
|
||||
index.env("CFG_BUILD", &build.build)
|
||||
.env("RUSTC_ERROR_METADATA_DST", build.extended_error_dir());
|
||||
|
||||
build.run(&mut index);
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ pub unsafe fn setup(build: &mut Build) {
|
||||
0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
|
||||
// If this failed, well at least we tried! An example of DuplicateHandle
|
||||
// failing in the past has been when the wrong python2 package spawed this
|
||||
// failing in the past has been when the wrong python2 package spawned this
|
||||
// build system (e.g. the `python2` package in MSYS instead of
|
||||
// `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
|
||||
// mode" here is that we only clean everything up when the build system
|
||||
|
@ -130,11 +130,12 @@ extern crate cc;
|
||||
extern crate getopts;
|
||||
extern crate num_cpus;
|
||||
extern crate toml;
|
||||
extern crate time;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{RefCell, Cell};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
@ -143,8 +144,7 @@ use std::path::{PathBuf, Path};
|
||||
use std::process::{self, Command};
|
||||
use std::slice;
|
||||
|
||||
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime,
|
||||
BuildExpectation};
|
||||
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
|
||||
|
||||
use util::{exe, libdir, OutputFolder, CiEnv};
|
||||
|
||||
@ -190,6 +190,7 @@ mod job {
|
||||
pub use config::Config;
|
||||
use flags::Subcommand;
|
||||
use cache::{Interned, INTERNER};
|
||||
use toolstate::ToolState;
|
||||
|
||||
/// A structure representing a Rust compiler.
|
||||
///
|
||||
@ -250,6 +251,7 @@ pub struct Build {
|
||||
is_sudo: bool,
|
||||
ci_env: CiEnv,
|
||||
delayed_failures: RefCell<Vec<String>>,
|
||||
prerelease_version: Cell<Option<u32>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -335,6 +337,7 @@ impl Build {
|
||||
is_sudo,
|
||||
ci_env: CiEnv::current(),
|
||||
delayed_failures: RefCell::new(Vec::new()),
|
||||
prerelease_version: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,31 +571,24 @@ impl Build {
|
||||
.join(libdir(&self.config.build))
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if its build
|
||||
/// status is not the expected one
|
||||
fn run_expecting(&self, cmd: &mut Command, expect: BuildExpectation) {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_silent(cmd, expect)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run(&self, cmd: &mut Command) {
|
||||
self.run_expecting(cmd, BuildExpectation::None)
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_silent(cmd)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run_quiet(&self, cmd: &mut Command) {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_suppressed(cmd, BuildExpectation::None)
|
||||
run_suppressed(cmd)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if its build
|
||||
/// status is not the expected one.
|
||||
/// Exits if the command failed to execute at all, otherwise returns whether
|
||||
/// the expectation was met
|
||||
fn try_run(&self, cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
/// Exits if the command failed to execute at all, otherwise returns its
|
||||
/// `status.success()`.
|
||||
fn try_run(&self, cmd: &mut Command) -> bool {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_silent(cmd, expect)
|
||||
try_run_silent(cmd)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
@ -600,7 +596,7 @@ impl Build {
|
||||
/// `status.success()`.
|
||||
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_suppressed(cmd, BuildExpectation::None)
|
||||
try_run_suppressed(cmd)
|
||||
}
|
||||
|
||||
pub fn is_verbose(&self) -> bool {
|
||||
@ -725,6 +721,11 @@ impl Build {
|
||||
self.config.python.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Temporary directory that extended error information is emitted to.
|
||||
fn extended_error_dir(&self) -> PathBuf {
|
||||
self.out.join("tmp/extended-error-metadata")
|
||||
}
|
||||
|
||||
/// Tests whether the `compiler` compiling for `target` should be forced to
|
||||
/// use a stage1 compiler instead.
|
||||
///
|
||||
@ -776,12 +777,63 @@ impl Build {
|
||||
fn release(&self, num: &str) -> String {
|
||||
match &self.config.channel[..] {
|
||||
"stable" => num.to_string(),
|
||||
"beta" => format!("{}-beta{}", num, channel::CFG_PRERELEASE_VERSION),
|
||||
"beta" => if self.rust_info.is_git() {
|
||||
format!("{}-beta.{}", num, self.beta_prerelease_version())
|
||||
} else {
|
||||
format!("{}-beta", num)
|
||||
},
|
||||
"nightly" => format!("{}-nightly", num),
|
||||
_ => format!("{}-dev", num),
|
||||
}
|
||||
}
|
||||
|
||||
fn beta_prerelease_version(&self) -> u32 {
|
||||
if let Some(s) = self.prerelease_version.get() {
|
||||
return s
|
||||
}
|
||||
|
||||
let beta = output(
|
||||
Command::new("git")
|
||||
.arg("ls-remote")
|
||||
.arg("origin")
|
||||
.arg("beta")
|
||||
.current_dir(&self.src)
|
||||
);
|
||||
let beta = beta.trim().split_whitespace().next().unwrap();
|
||||
let master = output(
|
||||
Command::new("git")
|
||||
.arg("ls-remote")
|
||||
.arg("origin")
|
||||
.arg("master")
|
||||
.current_dir(&self.src)
|
||||
);
|
||||
let master = master.trim().split_whitespace().next().unwrap();
|
||||
|
||||
// Figure out where the current beta branch started.
|
||||
let base = output(
|
||||
Command::new("git")
|
||||
.arg("merge-base")
|
||||
.arg(beta)
|
||||
.arg(master)
|
||||
.current_dir(&self.src),
|
||||
);
|
||||
let base = base.trim();
|
||||
|
||||
// Next figure out how many merge commits happened since we branched off
|
||||
// beta. That's our beta number!
|
||||
let count = output(
|
||||
Command::new("git")
|
||||
.arg("rev-list")
|
||||
.arg("--count")
|
||||
.arg("--merges")
|
||||
.arg(format!("{}...HEAD", base))
|
||||
.current_dir(&self.src),
|
||||
);
|
||||
let n = count.trim().parse().unwrap();
|
||||
self.prerelease_version.set(Some(n));
|
||||
return n
|
||||
}
|
||||
|
||||
/// Returns the value of `release` above for Rust itself.
|
||||
fn rust_release(&self) -> String {
|
||||
self.release(channel::CFG_RELEASE_NUM)
|
||||
@ -874,6 +926,30 @@ impl Build {
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the actual toolstate of a tool.
|
||||
///
|
||||
/// The toolstates are saved to the file specified by the key
|
||||
/// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be
|
||||
/// done. The file is updated immediately after this function completes.
|
||||
pub fn save_toolstate(&self, tool: &str, state: ToolState) {
|
||||
use std::io::{Seek, SeekFrom};
|
||||
|
||||
if let Some(ref path) = self.config.save_toolstates {
|
||||
let mut file = t!(fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path));
|
||||
|
||||
let mut current_toolstates: HashMap<Box<str>, ToolState> =
|
||||
serde_json::from_reader(&mut file).unwrap_or_default();
|
||||
current_toolstates.insert(tool.into(), state);
|
||||
t!(file.seek(SeekFrom::Start(0)));
|
||||
t!(file.set_len(0));
|
||||
t!(serde_json::to_writer(file, ¤t_toolstates));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of crates from a root crate.
|
||||
///
|
||||
/// Returns Vec<(crate, path to crate, is_root_crate)>
|
||||
|
@ -53,9 +53,7 @@ check:
|
||||
check-aux:
|
||||
$(Q)$(BOOTSTRAP) test \
|
||||
src/tools/cargo \
|
||||
src/tools/rls \
|
||||
src/tools/rustfmt \
|
||||
src/tools/miri \
|
||||
src/tools/cargotest \
|
||||
src/test/pretty \
|
||||
src/test/run-pass/pretty \
|
||||
src/test/run-fail/pretty \
|
||||
|
@ -110,10 +110,7 @@ impl Step for Llvm {
|
||||
None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon",
|
||||
};
|
||||
|
||||
let llvm_exp_targets = match build.config.llvm_experimental_targets {
|
||||
Some(ref s) => s,
|
||||
None => "",
|
||||
};
|
||||
let llvm_exp_targets = &build.config.llvm_experimental_targets;
|
||||
|
||||
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
||||
|
||||
@ -319,7 +316,7 @@ impl Step for TestHelpers {
|
||||
.warnings(false)
|
||||
.debug(false)
|
||||
.file(build.src.join("src/rt/rust_test_helpers.c"))
|
||||
.compile("librust_test_helpers.a");
|
||||
.compile("rust_test_helpers");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ pub fn check(build: &mut Build) {
|
||||
}
|
||||
|
||||
let mut cmd_finder = Finder::new();
|
||||
// If we've got a git directory we're gona need git to update
|
||||
// If we've got a git directory we're gonna need git to update
|
||||
// submodules and learn about various other aspects.
|
||||
if build.rust_info.is_git() {
|
||||
cmd_finder.must_have("git");
|
||||
|
@ -11,7 +11,7 @@
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::{Command, exit};
|
||||
|
||||
use Mode;
|
||||
use Compiler;
|
||||
@ -22,7 +22,6 @@ use native;
|
||||
use channel::GitInfo;
|
||||
use cache::Interned;
|
||||
use toolstate::ToolState;
|
||||
use build_helper::BuildExpectation;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct CleanTools {
|
||||
@ -82,7 +81,7 @@ struct ToolBuild {
|
||||
tool: &'static str,
|
||||
path: &'static str,
|
||||
mode: Mode,
|
||||
expectation: BuildExpectation,
|
||||
is_ext_tool: bool,
|
||||
}
|
||||
|
||||
impl Step for ToolBuild {
|
||||
@ -102,7 +101,7 @@ impl Step for ToolBuild {
|
||||
let target = self.target;
|
||||
let tool = self.tool;
|
||||
let path = self.path;
|
||||
let expectation = self.expectation;
|
||||
let is_ext_tool = self.is_ext_tool;
|
||||
|
||||
match self.mode {
|
||||
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
||||
@ -115,15 +114,25 @@ impl Step for ToolBuild {
|
||||
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
||||
build.run_expecting(&mut cargo, expectation);
|
||||
if expectation == BuildExpectation::Succeeding || expectation == BuildExpectation::None {
|
||||
let is_expected = build.try_run(&mut cargo);
|
||||
build.save_toolstate(tool, if is_expected {
|
||||
ToolState::TestFail
|
||||
} else {
|
||||
ToolState::BuildFail
|
||||
});
|
||||
|
||||
if !is_expected {
|
||||
if !is_ext_tool {
|
||||
exit(1);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
let cargo_out = build.cargo_out(compiler, Mode::Tool, target)
|
||||
.join(exe(tool, &compiler.host));
|
||||
let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host));
|
||||
copy(&cargo_out, &bin);
|
||||
Some(bin)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,8 +241,8 @@ macro_rules! tool {
|
||||
tool: $tool_name,
|
||||
mode: $mode,
|
||||
path: $path,
|
||||
expectation: BuildExpectation::None,
|
||||
}).expect("expected to build -- BuildExpectation::None")
|
||||
is_ext_tool: false,
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
)+
|
||||
@ -280,8 +289,8 @@ impl Step for RemoteTestServer {
|
||||
tool: "remote-test-server",
|
||||
mode: Mode::Libstd,
|
||||
path: "src/tools/remote-test-server",
|
||||
expectation: BuildExpectation::None,
|
||||
}).expect("expected to build -- BuildExpectation::None")
|
||||
is_ext_tool: false,
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,76 +407,70 @@ impl Step for Cargo {
|
||||
tool: "cargo",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/cargo",
|
||||
expectation: BuildExpectation::None,
|
||||
}).expect("BuildExpectation::None - expected to build")
|
||||
is_ext_tool: false,
|
||||
}).expect("expected to build -- essential tool")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Clippy {
|
||||
macro_rules! tool_extended {
|
||||
(($sel:ident, $builder:ident),
|
||||
$($name:ident,
|
||||
$toolstate:ident,
|
||||
$path:expr,
|
||||
$tool_name:expr,
|
||||
$extra_deps:block;)+) => {
|
||||
$(
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct $name {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for Clippy {
|
||||
impl Step for $name {
|
||||
type Output = Option<PathBuf>;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/clippy").default_condition(builder.build.config.extended)
|
||||
run.path($path).default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Clippy {
|
||||
run.builder.ensure($name {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
fn run($sel, $builder: &Builder) -> Option<PathBuf> {
|
||||
$extra_deps
|
||||
$builder.ensure(ToolBuild {
|
||||
compiler: $sel.compiler,
|
||||
target: $sel.target,
|
||||
tool: $tool_name,
|
||||
mode: Mode::Librustc,
|
||||
path: $path,
|
||||
is_ext_tool: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
tool_extended!((self, builder),
|
||||
Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {};
|
||||
Clippy, clippy, "src/tools/clippy", "clippy-driver", {
|
||||
// Clippy depends on procedural macros (serde), which requires a full host
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "clippy-driver",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/clippy",
|
||||
expectation: builder.build.config.toolstate.clippy.passes(ToolState::Compiling),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rls {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rls {
|
||||
type Output = Option<PathBuf>;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/rls").default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rls {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
};
|
||||
Miri, miri, "src/tools/miri", "miri", {};
|
||||
Rls, rls, "src/tools/rls", "rls", {
|
||||
builder.ensure(native::Openssl {
|
||||
target: self.target,
|
||||
});
|
||||
@ -477,87 +480,9 @@ impl Step for Rls {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "rls",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/rls",
|
||||
expectation: builder.build.config.toolstate.rls.passes(ToolState::Compiling),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Rustfmt {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustfmt {
|
||||
type Output = Option<PathBuf>;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/rustfmt").default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rustfmt {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "rustfmt",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/rustfmt",
|
||||
expectation: builder.build.config.toolstate.rustfmt.passes(ToolState::Compiling),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Miri {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Miri {
|
||||
type Output = Option<PathBuf>;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let build_miri = run.builder.build.config.test_miri;
|
||||
run.path("src/tools/miri").default_condition(build_miri)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Miri {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "miri",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/miri",
|
||||
expectation: builder.build.config.toolstate.miri.passes(ToolState::Compiling),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {};
|
||||
);
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
/// Get a `Command` which is ready to run `tool` in `stage` built for
|
||||
|
@ -8,51 +8,21 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use build_helper::BuildExpectation;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Whether a tool can be compiled, tested or neither
|
||||
pub enum ToolState {
|
||||
/// The tool compiles successfully, but the test suite fails
|
||||
Compiling = 1,
|
||||
TestFail = 1,
|
||||
/// The tool compiles successfully and its test suite passes
|
||||
Testing = 2,
|
||||
TestPass = 2,
|
||||
/// The tool can't even be compiled
|
||||
Broken = 0,
|
||||
}
|
||||
|
||||
impl ToolState {
|
||||
/// If a tool with the current toolstate should be working on
|
||||
/// the given toolstate
|
||||
pub fn passes(self, other: ToolState) -> BuildExpectation {
|
||||
if self as usize >= other as usize {
|
||||
BuildExpectation::Succeeding
|
||||
} else {
|
||||
BuildExpectation::Failing
|
||||
}
|
||||
}
|
||||
|
||||
pub fn testing(&self) -> bool {
|
||||
match *self {
|
||||
ToolState::Testing => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
BuildFail = 0,
|
||||
}
|
||||
|
||||
impl Default for ToolState {
|
||||
fn default() -> Self {
|
||||
// err on the safe side
|
||||
ToolState::Broken
|
||||
ToolState::BuildFail
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Default)]
|
||||
/// Used to express which tools should (not) be compiled or tested.
|
||||
/// This is created from `toolstate.toml`.
|
||||
pub struct ToolStates {
|
||||
pub miri: ToolState,
|
||||
pub clippy: ToolState,
|
||||
pub rls: ToolState,
|
||||
pub rustfmt: ToolState,
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
use std::env;
|
||||
use std::str;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, Read, Write, Seek, SeekFrom};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{SystemTime, Instant};
|
||||
@ -51,6 +51,20 @@ pub fn copy(src: &Path, dst: &Path) {
|
||||
t!(filetime::set_file_times(dst, atime, mtime));
|
||||
}
|
||||
|
||||
/// Search-and-replaces within a file. (Not maximally efficiently: allocates a
|
||||
/// new string for each replacement.)
|
||||
pub fn replace_in_file(path: &Path, replacements: &[(&str, &str)]) {
|
||||
let mut contents = String::new();
|
||||
let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
|
||||
t!(file.read_to_string(&mut contents));
|
||||
for &(target, replacement) in replacements {
|
||||
contents = contents.replace(target, replacement);
|
||||
}
|
||||
t!(file.seek(SeekFrom::Start(0)));
|
||||
t!(file.set_len(0));
|
||||
t!(file.write_all(contents.as_bytes()));
|
||||
}
|
||||
|
||||
pub fn read_stamp_file(stamp: &Path) -> Vec<PathBuf> {
|
||||
let mut paths = Vec::new();
|
||||
let mut contents = Vec::new();
|
||||
|
@ -35,97 +35,55 @@ macro_rules! t {
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum BuildExpectation {
|
||||
Succeeding,
|
||||
Failing,
|
||||
None,
|
||||
}
|
||||
|
||||
pub fn run(cmd: &mut Command, expect: BuildExpectation) {
|
||||
pub fn run(cmd: &mut Command) {
|
||||
println!("running: {:?}", cmd);
|
||||
run_silent(cmd, expect);
|
||||
run_silent(cmd);
|
||||
}
|
||||
|
||||
pub fn run_silent(cmd: &mut Command, expect: BuildExpectation) {
|
||||
if !try_run_silent(cmd, expect) {
|
||||
pub fn run_silent(cmd: &mut Command) {
|
||||
if !try_run_silent(cmd) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_run_silent(cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
pub fn try_run_silent(cmd: &mut Command) -> bool {
|
||||
let status = match cmd.status() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
};
|
||||
process_status(
|
||||
cmd,
|
||||
status.success(),
|
||||
expect,
|
||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
if !status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status))
|
||||
status);
|
||||
}
|
||||
status.success()
|
||||
}
|
||||
|
||||
fn process_status<F: FnOnce()>(
|
||||
cmd: &Command,
|
||||
success: bool,
|
||||
expect: BuildExpectation,
|
||||
f: F,
|
||||
) -> bool {
|
||||
use BuildExpectation::*;
|
||||
match (expect, success) {
|
||||
(None, false) => { f(); false },
|
||||
// Non-tool build succeeds, everything is good
|
||||
(None, true) => true,
|
||||
// Tool expected to work and is working
|
||||
(Succeeding, true) => true,
|
||||
// Tool expected to fail and is failing
|
||||
(Failing, false) => {
|
||||
println!("This failure is expected (see `src/tools/toolstate.toml`)");
|
||||
true
|
||||
},
|
||||
// Tool expected to work, but is failing
|
||||
(Succeeding, false) => {
|
||||
f();
|
||||
println!("You can disable the tool in `src/tools/toolstate.toml`");
|
||||
false
|
||||
},
|
||||
// Tool expected to fail, but is working
|
||||
(Failing, true) => {
|
||||
println!("Expected `{:?}` to fail, but it succeeded.\n\
|
||||
Please adjust `src/tools/toolstate.toml` accordingly", cmd);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) {
|
||||
if !try_run_suppressed(cmd, expect) {
|
||||
pub fn run_suppressed(cmd: &mut Command) {
|
||||
if !try_run_suppressed(cmd) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_run_suppressed(cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
pub fn try_run_suppressed(cmd: &mut Command) -> bool {
|
||||
let output = match cmd.output() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
};
|
||||
process_status(
|
||||
cmd,
|
||||
output.status.success(),
|
||||
expect,
|
||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
if !output.status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n\
|
||||
stdout ----\n{}\n\
|
||||
stderr ----\n{}\n\n",
|
||||
cmd,
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)))
|
||||
String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
output.status.success()
|
||||
}
|
||||
|
||||
pub fn gnu_target(target: &str) -> String {
|
||||
@ -190,6 +148,9 @@ pub fn mtime(path: &Path) -> FileTime {
|
||||
///
|
||||
/// Uses last-modified time checks to verify this.
|
||||
pub fn up_to_date(src: &Path, dst: &Path) -> bool {
|
||||
if !dst.exists() {
|
||||
return false;
|
||||
}
|
||||
let threshold = mtime(dst);
|
||||
let meta = match fs::metadata(src) {
|
||||
Ok(meta) => meta,
|
||||
|
@ -36,14 +36,14 @@ a Docker image.
|
||||
|
||||
1. Select the "default" virtual machine inside VirtualBox, then click
|
||||
"Settings"
|
||||
2. Go to "Shared Folders", click "Add shared foldrer" (the folder icon with
|
||||
2. Go to "Shared Folders", click "Add shared folder" (the folder icon with
|
||||
a plus sign), fill in the following information, then click "OK":
|
||||
|
||||
* Folder path: `E:\rust`
|
||||
* Folder name: `e/rust`
|
||||
* Read-only: ☐ *unchecked*
|
||||
* Auto-mount: ☑ *checked*
|
||||
* Make Permanant: ☑ *checked*
|
||||
* Make Permanent: ☑ *checked*
|
||||
|
||||
3. VirtualBox might not support creating symbolic links inside a shared folder
|
||||
by default. You can enable it manually by running these from `cmd.exe`:
|
||||
|
@ -1,7 +1,7 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ \
|
||||
clang \
|
||||
make \
|
||||
file \
|
||||
curl \
|
||||
@ -16,16 +16,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
|
||||
COPY dist-i686-freebsd/build-toolchain.sh /tmp/
|
||||
RUN /tmp/build-toolchain.sh i686
|
||||
COPY scripts/freebsd-toolchain.sh /tmp/
|
||||
RUN /tmp/freebsd-toolchain.sh i686
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV \
|
||||
AR_i686_unknown_freebsd=i686-unknown-freebsd10-ar \
|
||||
CC_i686_unknown_freebsd=i686-unknown-freebsd10-gcc \
|
||||
CXX_i686_unknown_freebsd=i686-unknown-freebsd10-g++
|
||||
CC_i686_unknown_freebsd=i686-unknown-freebsd10-clang \
|
||||
CXX_i686_unknown_freebsd=i686-unknown-freebsd10-clang++
|
||||
|
||||
ENV HOSTS=i686-unknown-freebsd
|
||||
|
||||
|
@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set -ex
|
||||
|
||||
ARCH=$1
|
||||
BINUTILS=2.25.1
|
||||
GCC=6.4.0
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
mkdir binutils
|
||||
cd binutils
|
||||
|
||||
# First up, build binutils
|
||||
curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.bz2 | tar xjf -
|
||||
mkdir binutils-build
|
||||
cd binutils-build
|
||||
hide_output ../binutils-$BINUTILS/configure \
|
||||
--target=$ARCH-unknown-freebsd10
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
cd ../..
|
||||
rm -rf binutils
|
||||
|
||||
# Next, download the FreeBSD libc and relevant header files
|
||||
|
||||
mkdir freebsd
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.2-RELEASE/base.txz
|
||||
;;
|
||||
i686)
|
||||
URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/10.2-RELEASE/base.txz
|
||||
;;
|
||||
esac
|
||||
curl $URL | tar xJf - -C freebsd ./usr/include ./usr/lib ./lib
|
||||
|
||||
dst=/usr/local/$ARCH-unknown-freebsd10
|
||||
|
||||
cp -r freebsd/usr/include $dst/
|
||||
cp freebsd/usr/lib/crt1.o $dst/lib
|
||||
cp freebsd/usr/lib/Scrt1.o $dst/lib
|
||||
cp freebsd/usr/lib/crti.o $dst/lib
|
||||
cp freebsd/usr/lib/crtn.o $dst/lib
|
||||
cp freebsd/usr/lib/libc.a $dst/lib
|
||||
cp freebsd/usr/lib/libutil.a $dst/lib
|
||||
cp freebsd/usr/lib/libutil_p.a $dst/lib
|
||||
cp freebsd/usr/lib/libm.a $dst/lib
|
||||
cp freebsd/usr/lib/librt.so.1 $dst/lib
|
||||
cp freebsd/usr/lib/libexecinfo.so.1 $dst/lib
|
||||
cp freebsd/lib/libc.so.7 $dst/lib
|
||||
cp freebsd/lib/libm.so.5 $dst/lib
|
||||
cp freebsd/lib/libutil.so.9 $dst/lib
|
||||
cp freebsd/lib/libthr.so.3 $dst/lib/libpthread.so
|
||||
|
||||
ln -s libc.so.7 $dst/lib/libc.so
|
||||
ln -s libm.so.5 $dst/lib/libm.so
|
||||
ln -s librt.so.1 $dst/lib/librt.so
|
||||
ln -s libutil.so.9 $dst/lib/libutil.so
|
||||
ln -s libexecinfo.so.1 $dst/lib/libexecinfo.so
|
||||
rm -rf freebsd
|
||||
|
||||
# Finally, download and build gcc to target FreeBSD
|
||||
mkdir gcc
|
||||
cd gcc
|
||||
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.gz | tar xzf -
|
||||
cd gcc-$GCC
|
||||
./contrib/download_prerequisites
|
||||
|
||||
mkdir ../gcc-build
|
||||
cd ../gcc-build
|
||||
hide_output ../gcc-$GCC/configure \
|
||||
--enable-languages=c,c++ \
|
||||
--target=$ARCH-unknown-freebsd10 \
|
||||
--disable-multilib \
|
||||
--disable-nls \
|
||||
--disable-libgomp \
|
||||
--disable-libquadmath \
|
||||
--disable-libssp \
|
||||
--disable-libvtv \
|
||||
--disable-libcilkrts \
|
||||
--disable-libada \
|
||||
--disable-libsanitizer \
|
||||
--disable-libquadmath-support \
|
||||
--disable-lto
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
cd ../..
|
||||
rm -rf gcc
|
@ -24,19 +24,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
COPY cross/build-rumprun.sh /tmp/
|
||||
COPY dist-various-1/build-rumprun.sh /tmp/
|
||||
RUN ./build-rumprun.sh
|
||||
|
||||
COPY cross/build-arm-musl.sh /tmp/
|
||||
COPY dist-various-1/build-arm-musl.sh /tmp/
|
||||
RUN ./build-arm-musl.sh
|
||||
|
||||
COPY cross/install-mips-musl.sh /tmp/
|
||||
COPY dist-various-1/install-mips-musl.sh /tmp/
|
||||
RUN ./install-mips-musl.sh
|
||||
|
||||
COPY cross/install-mipsel-musl.sh /tmp/
|
||||
COPY dist-various-1/install-mipsel-musl.sh /tmp/
|
||||
RUN ./install-mipsel-musl.sh
|
||||
|
||||
COPY cross/install-x86_64-redox.sh /tmp/
|
||||
COPY dist-various-1/install-x86_64-redox.sh /tmp/
|
||||
RUN ./install-x86_64-redox.sh
|
||||
|
||||
ENV TARGETS=asmjs-unknown-emscripten
|
||||
@ -46,15 +46,20 @@ ENV TARGETS=$TARGETS,mips-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,mipsel-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi
|
||||
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-redox
|
||||
|
||||
# FIXME: remove armv5te vars after https://github.com/alexcrichton/cc-rs/issues/271
|
||||
# get fixed and cc update
|
||||
ENV CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \
|
||||
CC_mips_unknown_linux_musl=mips-openwrt-linux-gcc \
|
||||
CC_sparc64_unknown_linux_gnu=sparc64-linux-gnu-gcc \
|
||||
CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc
|
||||
CC_x86_64_unknown_redox=x86_64-unknown-redox-gcc \
|
||||
CC_armv5te_unknown_linux_gnueabi=arm-linux-gnueabi-gcc \
|
||||
CFLAGS_armv5te_unknown_linux_gnueabi="-march=armv5te -marm -mfloat-abi=soft"
|
||||
|
||||
# Suppress some warnings in the openwrt toolchains we downloaded
|
||||
ENV STAGING_DIR=/tmp
|
@ -21,8 +21,8 @@ RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7
|
||||
RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2-testing main'
|
||||
|
||||
WORKDIR /tmp
|
||||
COPY cross2/shared.sh cross2/build-fuchsia-toolchain.sh /tmp/
|
||||
COPY cross2/build-solaris-toolchain.sh /tmp/
|
||||
COPY dist-various-2/shared.sh dist-various-2/build-fuchsia-toolchain.sh /tmp/
|
||||
COPY dist-various-2/build-solaris-toolchain.sh /tmp/
|
||||
RUN /tmp/build-fuchsia-toolchain.sh
|
||||
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386
|
||||
RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc
|
||||
@ -47,6 +47,7 @@ ENV \
|
||||
ENV TARGETS=x86_64-unknown-fuchsia
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia
|
||||
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
|
||||
ENV TARGETS=$TARGETS,wasm32-unknown-unknown
|
||||
ENV TARGETS=$TARGETS,x86_64-sun-solaris
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
|
||||
|
@ -1,7 +1,7 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ \
|
||||
clang \
|
||||
make \
|
||||
file \
|
||||
curl \
|
||||
@ -16,16 +16,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libssl-dev \
|
||||
pkg-config
|
||||
|
||||
COPY dist-x86_64-freebsd/build-toolchain.sh /tmp/
|
||||
RUN /tmp/build-toolchain.sh x86_64
|
||||
COPY scripts/freebsd-toolchain.sh /tmp/
|
||||
RUN /tmp/freebsd-toolchain.sh x86_64
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV \
|
||||
AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \
|
||||
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc \
|
||||
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-g++
|
||||
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang \
|
||||
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang++
|
||||
|
||||
ENV HOSTS=x86_64-unknown-freebsd
|
||||
|
||||
|
@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set -ex
|
||||
|
||||
ARCH=$1
|
||||
BINUTILS=2.25.1
|
||||
GCC=6.4.0
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
mkdir binutils
|
||||
cd binutils
|
||||
|
||||
# First up, build binutils
|
||||
curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.bz2 | tar xjf -
|
||||
mkdir binutils-build
|
||||
cd binutils-build
|
||||
hide_output ../binutils-$BINUTILS/configure \
|
||||
--target=$ARCH-unknown-freebsd10
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
cd ../..
|
||||
rm -rf binutils
|
||||
|
||||
# Next, download the FreeBSD libc and relevant header files
|
||||
|
||||
mkdir freebsd
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.2-RELEASE/base.txz
|
||||
;;
|
||||
i686)
|
||||
URL=ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/10.2-RELEASE/base.txz
|
||||
;;
|
||||
esac
|
||||
curl $URL | tar xJf - -C freebsd ./usr/include ./usr/lib ./lib
|
||||
|
||||
dst=/usr/local/$ARCH-unknown-freebsd10
|
||||
|
||||
cp -r freebsd/usr/include $dst/
|
||||
cp freebsd/usr/lib/crt1.o $dst/lib
|
||||
cp freebsd/usr/lib/Scrt1.o $dst/lib
|
||||
cp freebsd/usr/lib/crti.o $dst/lib
|
||||
cp freebsd/usr/lib/crtn.o $dst/lib
|
||||
cp freebsd/usr/lib/libc.a $dst/lib
|
||||
cp freebsd/usr/lib/libutil.a $dst/lib
|
||||
cp freebsd/usr/lib/libutil_p.a $dst/lib
|
||||
cp freebsd/usr/lib/libm.a $dst/lib
|
||||
cp freebsd/usr/lib/librt.so.1 $dst/lib
|
||||
cp freebsd/usr/lib/libexecinfo.so.1 $dst/lib
|
||||
cp freebsd/lib/libc.so.7 $dst/lib
|
||||
cp freebsd/lib/libm.so.5 $dst/lib
|
||||
cp freebsd/lib/libutil.so.9 $dst/lib
|
||||
cp freebsd/lib/libthr.so.3 $dst/lib/libpthread.so
|
||||
|
||||
ln -s libc.so.7 $dst/lib/libc.so
|
||||
ln -s libm.so.5 $dst/lib/libm.so
|
||||
ln -s librt.so.1 $dst/lib/librt.so
|
||||
ln -s libutil.so.9 $dst/lib/libutil.so
|
||||
ln -s libexecinfo.so.1 $dst/lib/libexecinfo.so
|
||||
rm -rf freebsd
|
||||
|
||||
# Finally, download and build gcc to target FreeBSD
|
||||
mkdir gcc
|
||||
cd gcc
|
||||
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.gz | tar xzf -
|
||||
cd gcc-$GCC
|
||||
./contrib/download_prerequisites
|
||||
|
||||
mkdir ../gcc-build
|
||||
cd ../gcc-build
|
||||
hide_output ../gcc-$GCC/configure \
|
||||
--enable-languages=c,c++ \
|
||||
--target=$ARCH-unknown-freebsd10 \
|
||||
--disable-multilib \
|
||||
--disable-nls \
|
||||
--disable-libgomp \
|
||||
--disable-libquadmath \
|
||||
--disable-libssp \
|
||||
--disable-libvtv \
|
||||
--disable-libcilkrts \
|
||||
--disable-libada \
|
||||
--disable-libsanitizer \
|
||||
--disable-libquadmath-support \
|
||||
--disable-lto
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
cd ../..
|
||||
rm -rf gcc
|
@ -52,7 +52,7 @@ curl $URL/2017-03-17-netbsd-comp.tgz | \
|
||||
cd usr/src
|
||||
|
||||
# The options, in order, do the following
|
||||
# * this is an unpriviledged build
|
||||
# * this is an unprivileged build
|
||||
# * output to a predictable location
|
||||
# * disable various uneeded stuff
|
||||
MKUNPRIVED=yes TOOLDIR=/x-tools/x86_64-unknown-netbsd \
|
||||
|
@ -99,6 +99,7 @@ exec docker \
|
||||
--env LOCAL_USER_ID=`id -u` \
|
||||
--env TRAVIS \
|
||||
--env TRAVIS_BRANCH \
|
||||
--env TOOLSTATE_REPO_ACCESS_TOKEN \
|
||||
--volume "$HOME/.cargo:/cargo" \
|
||||
--volume "$HOME/rustsrc:$HOME/rustsrc" \
|
||||
--init \
|
||||
|
103
src/ci/docker/scripts/freebsd-toolchain.sh
Executable file
103
src/ci/docker/scripts/freebsd-toolchain.sh
Executable file
@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2016-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 -eux
|
||||
|
||||
arch=$1
|
||||
binutils_version=2.25.1
|
||||
freebsd_version=10.3
|
||||
triple=$arch-unknown-freebsd10
|
||||
sysroot=/usr/local/$triple
|
||||
|
||||
hide_output() {
|
||||
set +x
|
||||
local 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" &
|
||||
local ping_loop_pid=$!
|
||||
$@ &> /tmp/build.log
|
||||
trap - ERR
|
||||
kill $ping_loop_pid
|
||||
set -x
|
||||
}
|
||||
|
||||
# First up, build binutils
|
||||
mkdir binutils
|
||||
cd binutils
|
||||
curl https://ftp.gnu.org/gnu/binutils/binutils-${binutils_version}.tar.bz2 | tar xjf -
|
||||
mkdir binutils-build
|
||||
cd binutils-build
|
||||
hide_output ../binutils-${binutils_version}/configure \
|
||||
--target="$triple" --with-sysroot="$sysroot"
|
||||
hide_output make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
hide_output make install
|
||||
cd ../..
|
||||
rm -rf binutils
|
||||
|
||||
# Next, download the FreeBSD libraries and header files
|
||||
mkdir -p "$sysroot"
|
||||
case $arch in
|
||||
(x86_64) freebsd_arch=amd64 ;;
|
||||
(i686) freebsd_arch=i386 ;;
|
||||
esac
|
||||
|
||||
files_to_extract=(
|
||||
"./usr/include"
|
||||
"./usr/lib/*crt*.o"
|
||||
)
|
||||
# Try to unpack only the libraries the build needs, to save space.
|
||||
for lib in c cxxrt gcc_s m thr util; do
|
||||
files_to_extract=("${files_to_extract[@]}" "./lib/lib${lib}.*" "./usr/lib/lib${lib}.*")
|
||||
done
|
||||
for lib in c++ c_nonshared compiler_rt execinfo gcc pthread rt ssp_nonshared; do
|
||||
files_to_extract=("${files_to_extract[@]}" "./usr/lib/lib${lib}.*")
|
||||
done
|
||||
|
||||
URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz
|
||||
curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
|
||||
|
||||
# Fix up absolute symlinks from the system image. This can be removed
|
||||
# for FreeBSD 11. (If there's an easy way to make them relative
|
||||
# symlinks instead, feel free to change this.)
|
||||
set +x
|
||||
find "$sysroot" -type l | while read symlink_path; do
|
||||
symlink_target=$(readlink "$symlink_path")
|
||||
case $symlink_target in
|
||||
(/*)
|
||||
echo "Fixing symlink ${symlink_path} -> ${sysroot}${symlink_target}" >&2
|
||||
ln -nfs "${sysroot}${symlink_target}" "${symlink_path}" ;;
|
||||
esac
|
||||
done
|
||||
set -x
|
||||
|
||||
# Clang can do cross-builds out of the box, if we give it the right
|
||||
# flags. (The local binutils seem to work, but they set the ELF
|
||||
# header "OS/ABI" (EI_OSABI) field to SysV rather than FreeBSD, so
|
||||
# there might be other problems.)
|
||||
#
|
||||
# The --target option is last because the cross-build of LLVM uses
|
||||
# --target without an OS version ("-freebsd" vs. "-freebsd10"). This
|
||||
# makes Clang default to libstdc++ (which no longer exists), and also
|
||||
# controls other features, like GNU-style symbol table hashing and
|
||||
# anything predicated on the version number in the __FreeBSD__
|
||||
# preprocessor macro.
|
||||
for tool in clang clang++; do
|
||||
tool_path=/usr/local/bin/${triple}-${tool}
|
||||
cat > "$tool_path" <<EOF
|
||||
#!/bin/sh
|
||||
exec $tool --sysroot=$sysroot --prefix=${sysroot}/bin "\$@" --target=$triple
|
||||
EOF
|
||||
chmod +x "$tool_path"
|
||||
done
|
36
src/ci/docker/wasm32-unknown/Dockerfile
Normal file
36
src/ci/docker/wasm32-unknown/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 \
|
||||
python \
|
||||
git \
|
||||
cmake \
|
||||
sudo \
|
||||
gdb \
|
||||
xz-utils
|
||||
|
||||
RUN curl -sL https://nodejs.org/dist/v9.2.0/node-v9.2.0-linux-x64.tar.xz | \
|
||||
tar -xJ
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV TARGETS=wasm32-unknown-unknown
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--target=$TARGETS \
|
||||
--set build.nodejs=/node-v9.2.0-linux-x64/bin/node
|
||||
|
||||
ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \
|
||||
src/test/ui \
|
||||
src/test/run-pass \
|
||||
src/test/compile-fail \
|
||||
src/test/parse-fail \
|
||||
src/test/mir-opt \
|
||||
src/test/codegen-units \
|
||||
src/libcore \
|
||||
src/libstd_unicode/ \
|
@ -12,10 +12,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libssl-dev \
|
||||
sudo \
|
||||
xz-utils \
|
||||
pkg-config
|
||||
pkg-config \
|
||||
libgl1-mesa-dev \
|
||||
llvm-dev \
|
||||
libfreetype6-dev \
|
||||
libexpat1-dev
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-test-miri
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
|
||||
ENV RUST_CHECK_TARGET check-aux
|
||||
|
@ -1,25 +0,0 @@
|
||||
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 \
|
||||
libssl-dev \
|
||||
sudo \
|
||||
xz-utils \
|
||||
pkg-config \
|
||||
libgl1-mesa-dev \
|
||||
llvm-dev \
|
||||
libfreetype6-dev \
|
||||
libexpat1-dev
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
|
||||
ENV SCRIPT python2.7 ../x.py test src/tools/cargotest
|
27
src/ci/docker/x86_64-gnu-tools/Dockerfile
Normal file
27
src/ci/docker/x86_64-gnu-tools/Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
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 \
|
||||
libssl-dev \
|
||||
sudo \
|
||||
xz-utils \
|
||||
pkg-config
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
COPY x86_64-gnu-tools/checktools.sh /tmp/
|
||||
COPY x86_64-gnu-tools/repo.sh /tmp/
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--enable-test-miri \
|
||||
--save-toolstates=/tmp/toolstates.json
|
||||
ENV SCRIPT /tmp/checktools.sh ../x.py /tmp/toolstates.json linux
|
66
src/ci/docker/x86_64-gnu-tools/checktools.sh
Executable file
66
src/ci/docker/x86_64-gnu-tools/checktools.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set -eu
|
||||
|
||||
X_PY="$1"
|
||||
TOOLSTATE_FILE="$(realpath $2)"
|
||||
OS="$3"
|
||||
COMMIT="$(git rev-parse HEAD)"
|
||||
CHANGED_FILES="$(git diff --name-status HEAD HEAD^)"
|
||||
|
||||
touch "$TOOLSTATE_FILE"
|
||||
|
||||
set +e
|
||||
python2.7 "$X_PY" test --no-fail-fast \
|
||||
src/tools/rls \
|
||||
src/tools/rustfmt
|
||||
set -e
|
||||
|
||||
cat "$TOOLSTATE_FILE"
|
||||
|
||||
# If this PR is intended to update one of these tools, do not let the build pass
|
||||
# when they do not test-pass.
|
||||
for TOOL in rls rustfmt; do
|
||||
echo "Verifying status of $TOOL..."
|
||||
if echo "$CHANGED_FILES" | grep -q "^M[[:blank:]]src/tools/$TOOL$"; then
|
||||
echo "This PR updated 'src/tools/$TOOL', verifying if status is 'test-pass'..."
|
||||
if grep -vq '"'"$TOOL"'[^"]*":"test-pass"' "$TOOLSTATE_FILE"; then
|
||||
echo
|
||||
echo "⚠️ We detected that this PR updated '$TOOL', but its tests failed."
|
||||
echo
|
||||
echo "If you do intend to update '$TOOL', please check the error messages above and"
|
||||
echo "commit another update."
|
||||
echo
|
||||
echo "If you do NOT intend to update '$TOOL', please ensure you did not accidentally"
|
||||
echo "change the submodule at 'src/tools/$TOOL'. You may ask your reviewer for the"
|
||||
echo "proper steps."
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
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"
|
||||
rm -f "$MESSAGE_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if grep -q fail "$TOOLSTATE_FILE"; then
|
||||
exit 4
|
||||
fi
|
90
src/ci/docker/x86_64-gnu-tools/repo.sh
Normal file
90
src/ci/docker/x86_64-gnu-tools/repo.sh
Normal file
@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
# This file provides the function `commit_toolstate_change` for pushing a change
|
||||
# to the `rust-toolstate` repository.
|
||||
#
|
||||
# The function relies on a GitHub bot user, which should have a Personal access
|
||||
# token defined in the environment variable $TOOLSTATE_REPO_ACCESS_TOKEN. If for
|
||||
# some reason you need to change the token, please update `.travis.yml` and
|
||||
# `appveyor.yml`:
|
||||
#
|
||||
# 1. Generate a new Personal access token:
|
||||
#
|
||||
# * Login to the bot account, and go to Settings -> Developer settings ->
|
||||
# Personal access tokens
|
||||
# * Click "Generate new token"
|
||||
# * Enable the "public_repo" permission, then click "Generate token"
|
||||
# * Copy the generated token (should be a 40-digit hexadecimal number).
|
||||
# Save it somewhere secure, as the token would be gone once you leave
|
||||
# the page.
|
||||
#
|
||||
# 2. Encrypt the token for Travis CI
|
||||
#
|
||||
# * Install the `travis` tool locally (`gem install travis`).
|
||||
# * Encrypt the token:
|
||||
# ```
|
||||
# travis -r rust-lang/rust encrypt \
|
||||
# TOOLSTATE_REPO_ACCESS_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
# ```
|
||||
# * Copy output to replace the existing one in `.travis.yml`.
|
||||
# * Details of this step can be found in
|
||||
# <https://docs.travis-ci.com/user/encryption-keys/>
|
||||
#
|
||||
# 3. Encrypt the token for AppVeyor
|
||||
#
|
||||
# * Login to AppVeyor using your main account, and login as the rust-lang
|
||||
# organization.
|
||||
# * Open the ["Encrypt data" tool](https://ci.appveyor.com/tools/encrypt)
|
||||
# * Paste the 40-digit token into the "Value to encrypt" box, then click
|
||||
# "Encrypt"
|
||||
# * Copy the output to replace the existing one in `appveyor.yml`.
|
||||
# * Details of this step can be found in
|
||||
# <https://www.appveyor.com/docs/how-to/git-push/>
|
||||
#
|
||||
# 4. Replace the email address below if the bot account identity is changed
|
||||
#
|
||||
# * See <https://help.github.com/articles/about-commit-email-addresses/>
|
||||
# if a private email by GitHub is wanted.
|
||||
|
||||
commit_toolstate_change() {
|
||||
OLDFLAGS="$-"
|
||||
set -eu
|
||||
|
||||
git config --global user.email '34210020+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" \
|
||||
> "$HOME/.git-credentials"
|
||||
git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git
|
||||
|
||||
cd rust-toolstate
|
||||
FAILURE=1
|
||||
MESSAGE_FILE="$1"
|
||||
shift
|
||||
for RETRY_COUNT in 1 2 3 4 5; do
|
||||
"$@"
|
||||
# `git commit` failing means nothing to commit.
|
||||
FAILURE=0
|
||||
git commit -a -F "$MESSAGE_FILE" || break
|
||||
# On failure randomly sleep for 0 to 3 seconds as a crude way to introduce jittering.
|
||||
git push origin master && break || sleep $(LC_ALL=C tr -cd 0-3 < /dev/urandom | head -c 1)
|
||||
FAILURE=1
|
||||
git fetch origin master
|
||||
git reset --hard origin/master
|
||||
done
|
||||
cd ..
|
||||
|
||||
set +eu
|
||||
set "-$OLDFLAGS"
|
||||
return $FAILURE
|
||||
}
|
@ -16,5 +16,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-sanitizers --enable-profiler
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--build=x86_64-unknown-linux-gnu \
|
||||
--enable-sanitizers \
|
||||
--enable-profiler \
|
||||
--enable-compiler-docs
|
||||
ENV SCRIPT python2.7 ../x.py test
|
||||
|
@ -36,6 +36,12 @@ fi
|
||||
rm -rf "$CACHE_DIR"
|
||||
mkdir "$CACHE_DIR"
|
||||
|
||||
# On the beta channel we'll be automatically calculating the prerelease version
|
||||
# via the git history, so unshallow our shallow clone from CI.
|
||||
if grep -q RUST_RELEASE_CHANNEL=beta src/ci/run.sh; then
|
||||
git fetch origin --unshallow beta master
|
||||
fi
|
||||
|
||||
travis_fold start update_cache
|
||||
travis_time_start
|
||||
|
||||
|
@ -37,13 +37,14 @@ if [ "$DIST_SRC" = "" ]; then
|
||||
fi
|
||||
|
||||
# If we're deploying artifacts then we set the release channel, otherwise if
|
||||
# we're not deploying then we want to be sure to enable all assertions becauase
|
||||
# we're not deploying then we want to be sure to enable all assertions because
|
||||
# we'll be running tests
|
||||
#
|
||||
# FIXME: need a scheme for changing this `nightly` value to `beta` and `stable`
|
||||
# either automatically or manually.
|
||||
export RUST_RELEASE_CHANNEL=stable
|
||||
if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=stable"
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL"
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp"
|
||||
|
||||
if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then
|
||||
|
@ -1,3 +1,13 @@
|
||||
# NOTICE ABOUT STATUS
|
||||
|
||||
The second edition of The Rust Programming Language is getting ever closer to being printed!
|
||||
This means we're not able to make large changes to chapters that are in any column to the
|
||||
right of, and including, the "Frozen" column [on our Project board][proj]. Issues or pull
|
||||
requests submitted for frozen chapters are welcome but will be closed until we start work
|
||||
on a third edition. Thank you!
|
||||
|
||||
[proj]: https://github.com/rust-lang/book/projects/1
|
||||
|
||||
# The Rust Programming Language
|
||||
|
||||
[](https://travis-ci.org/rust-lang/book)
|
||||
@ -7,16 +17,27 @@ This repo contains two editions of “The Rust Programming Language”.
|
||||
The second edition is a rewrite that will be printed by NoStarch Press,
|
||||
available around October 2017.
|
||||
|
||||
[You can read it online][html]; the last few chapters aren't completed yet, but
|
||||
[You can read the very latest online][html]; the last few chapters aren't completed yet, but
|
||||
the first half of the book is much improved from the first edition. We recommend
|
||||
starting with the second edition.
|
||||
|
||||
[html]: http://rust-lang.github.io/book/
|
||||
|
||||
Note that links to the standard library won't work in this version; this is intentional
|
||||
so that links work with the book and API docs shipped with Rust installations for offline
|
||||
reading. For a version of the book where these links do work, please see the book as shipped
|
||||
with the latest [stable], [beta], or [nightly] Rust releases. Be aware that issues in those
|
||||
versions may have been fixed in this repository already.
|
||||
|
||||
[stable]: https://doc.rust-lang.org/stable/book/second-edition/
|
||||
[beta]: https://doc.rust-lang.org/beta/book/second-edition/
|
||||
[nightly]: https://doc.rust-lang.org/nightly/book/second-edition/
|
||||
|
||||
[The first edition is still available to read online][first].
|
||||
|
||||
[first]: https://doc.rust-lang.org/book/
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
Building the book requires [mdBook], ideally the same version that
|
||||
|
@ -60,7 +60,7 @@ A cast `e as U` is valid if `e` has type `T` and `T` *coerces* to `U`.
|
||||
A cast `e as U` is also valid in any of the following cases:
|
||||
|
||||
* `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast*
|
||||
* `e` is a C-like enum (with no data attached to the variants),
|
||||
* `e` is an enum with no data attached to the variants (a "field-less enumeration"),
|
||||
and `U` is an integer type; *enum-cast*
|
||||
* `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast*
|
||||
* `e` has type `u8` and `U` is `char`; *u8-char-cast*
|
||||
|
@ -737,11 +737,11 @@ void foo(struct Foo *arg);
|
||||
void bar(struct Bar *arg);
|
||||
```
|
||||
|
||||
To do this in Rust, let’s create our own opaque types with `enum`:
|
||||
To do this in Rust, let’s create our own opaque types:
|
||||
|
||||
```rust
|
||||
pub enum Foo {}
|
||||
pub enum Bar {}
|
||||
#[repr(C)] pub struct Foo { private: [u8; 0] }
|
||||
#[repr(C)] pub struct Bar { private: [u8; 0] }
|
||||
|
||||
extern "C" {
|
||||
pub fn foo(arg: *mut Foo);
|
||||
@ -750,7 +750,9 @@ extern "C" {
|
||||
# fn main() {}
|
||||
```
|
||||
|
||||
By using an `enum` with no variants, we create an opaque type that we can’t
|
||||
instantiate, as it has no variants. But because our `Foo` and `Bar` types are
|
||||
By including a private field and no constructor,
|
||||
we create an opaque type that we can’t instantiate outside of this module.
|
||||
An empty array is both zero-size and compatible with `#[repr(C)]`.
|
||||
But because our `Foo` and `Bar` types are
|
||||
different, we’ll get type safety between the two of them, so we cannot
|
||||
accidentally pass a pointer to `Foo` to `bar()`.
|
||||
|
@ -4,11 +4,13 @@ abcd
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
adaptor
|
||||
adaptors
|
||||
AddAssign
|
||||
Addr
|
||||
aggregator
|
||||
AGraph
|
||||
aliasability
|
||||
alignof
|
||||
alloc
|
||||
allocator
|
||||
Amir
|
||||
anotherusername
|
||||
@ -29,19 +31,23 @@ Baz’s
|
||||
benchmarking
|
||||
bitand
|
||||
BitAnd
|
||||
BitAndAssign
|
||||
bitor
|
||||
BitOr
|
||||
BitOrAssign
|
||||
bitwise
|
||||
Bitwise
|
||||
bitxor
|
||||
BitXor
|
||||
BitXorAssign
|
||||
Bjarne
|
||||
Boehm
|
||||
bool
|
||||
boolean
|
||||
booleans
|
||||
Boolean
|
||||
Booleans
|
||||
Bors
|
||||
BorrowMutError
|
||||
BTreeSet
|
||||
BuildHasher
|
||||
Cacher
|
||||
Cagain
|
||||
@ -88,6 +94,7 @@ dereferenced
|
||||
dereferences
|
||||
dereferencing
|
||||
DerefMut
|
||||
DeriveInput
|
||||
destructor
|
||||
destructure
|
||||
destructured
|
||||
@ -102,6 +109,7 @@ doccratesio
|
||||
DOCTYPE
|
||||
doesn
|
||||
disambiguating
|
||||
DivAssign
|
||||
DraftPost
|
||||
DSTs
|
||||
ebooks
|
||||
@ -119,6 +127,7 @@ eprintln
|
||||
Erlang
|
||||
ErrorKind
|
||||
Executables
|
||||
expr
|
||||
extern
|
||||
favicon
|
||||
FFFD
|
||||
@ -135,6 +144,7 @@ FnBox
|
||||
FnMut
|
||||
FnOnce
|
||||
formatter
|
||||
FrenchToast
|
||||
FromIterator
|
||||
frontend
|
||||
getter
|
||||
@ -155,20 +165,26 @@ HashSet
|
||||
Haskell
|
||||
hasn
|
||||
helloworld
|
||||
HelloWorld
|
||||
HelloWorldName
|
||||
Hmmm
|
||||
Hoare
|
||||
Hola
|
||||
homogenous
|
||||
html
|
||||
hyperoptimize
|
||||
Iceburgh
|
||||
ident
|
||||
IEEE
|
||||
impl
|
||||
implementor
|
||||
implementors
|
||||
ImportantExcerpt
|
||||
incrementing
|
||||
IndexMut
|
||||
indices
|
||||
init
|
||||
initializer
|
||||
inline
|
||||
instantiation
|
||||
internet
|
||||
@ -190,6 +206,7 @@ JoinHandle
|
||||
kinded
|
||||
lang
|
||||
latin
|
||||
liballoc
|
||||
libc
|
||||
libcollections
|
||||
libcore
|
||||
@ -213,6 +230,7 @@ Metadata
|
||||
metaprogramming
|
||||
mibbit
|
||||
Mibbit
|
||||
millis
|
||||
minigrep
|
||||
mixup
|
||||
mkdir
|
||||
@ -225,6 +243,7 @@ monomorphized
|
||||
MoveMessage
|
||||
Mozilla
|
||||
mpsc
|
||||
MulAssign
|
||||
multibyte
|
||||
multithreaded
|
||||
mutex
|
||||
@ -248,6 +267,7 @@ nitty
|
||||
nocapture
|
||||
nomicon
|
||||
Nomicon
|
||||
nonequality
|
||||
NotFound
|
||||
null's
|
||||
OCaml
|
||||
@ -261,6 +281,7 @@ OsStr
|
||||
OsString
|
||||
other's
|
||||
OutlinePrint
|
||||
overloadable
|
||||
overread
|
||||
param
|
||||
parameterize
|
||||
@ -288,6 +309,9 @@ pushups
|
||||
QuitMessage
|
||||
RAII
|
||||
randcrate
|
||||
RangeFrom
|
||||
RangeTo
|
||||
RangeFull
|
||||
README
|
||||
READMEs
|
||||
rect
|
||||
@ -302,6 +326,7 @@ RefCell
|
||||
RefMut
|
||||
refutability
|
||||
reimplement
|
||||
RemAssign
|
||||
repr
|
||||
representable
|
||||
request's
|
||||
@ -326,6 +351,9 @@ SecondaryColor
|
||||
SelectBox
|
||||
semver
|
||||
SemVer
|
||||
serde
|
||||
ShlAssign
|
||||
ShrAssign
|
||||
shouldn
|
||||
Simula
|
||||
situps
|
||||
@ -346,12 +374,15 @@ Stdin
|
||||
stdlib
|
||||
stdout
|
||||
steveklabnik's
|
||||
stringify
|
||||
Stroustrup
|
||||
Stroustrup's
|
||||
struct
|
||||
Struct
|
||||
structs
|
||||
struct's
|
||||
Structs
|
||||
SubAssign
|
||||
subclasses
|
||||
subcommand
|
||||
subcommands
|
||||
@ -369,6 +400,7 @@ supertrait
|
||||
supertraits
|
||||
TcpListener
|
||||
TcpStream
|
||||
templating
|
||||
test's
|
||||
TextField
|
||||
That'd
|
||||
@ -378,7 +410,9 @@ threadsafe
|
||||
timestamp
|
||||
Tiếng
|
||||
timeline
|
||||
tlborm
|
||||
TODO
|
||||
TokenStream
|
||||
toml
|
||||
TOML
|
||||
ToString
|
||||
@ -389,7 +423,9 @@ trpl
|
||||
tuesday
|
||||
tuple
|
||||
tuples
|
||||
turbofish
|
||||
typeof
|
||||
TypeName
|
||||
UFCS
|
||||
unary
|
||||
Unary
|
||||
|
@ -1,63 +1,912 @@
|
||||
# Appendix
|
||||
|
||||
The following sections contain reference material you may find useful in your
|
||||
Rust journey.
|
||||
|
||||
## Keywords
|
||||
## Appendix A: Keywords
|
||||
|
||||
The following keywords are reserved by the Rust language and may not be used as
|
||||
names of functions, variables, macros, modules, crates, constants, static
|
||||
values, attributes, struct fields, or arguments.
|
||||
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` - primitive casting, disambiguating the specific trait containing an
|
||||
item, or renaming items in `use` and `extern crate` statements
|
||||
* `break` - exit a loop immediately
|
||||
* `const` - constant items and constant raw pointers
|
||||
* `continue` - continue to the next loop iteration
|
||||
* `crate` - external crate linkage or a macro variable representing the crate
|
||||
in which the macro is defined
|
||||
* `else` - fallback for `if` and `if let` control flow constructs
|
||||
* `enum` - defining an enumeration
|
||||
* `extern` - external crate, function, and variable linkage
|
||||
* `false` - Boolean false literal
|
||||
* `fn` - function definition and function pointer type
|
||||
* `for` - iterator loop, part of trait impl syntax, and higher-ranked lifetime
|
||||
syntax
|
||||
* `if` - conditional branching
|
||||
* `impl` - inherent and trait implementation block
|
||||
* `in` - part of `for` loop syntax
|
||||
* `let` - variable binding
|
||||
* `loop` - unconditional, infinite loop
|
||||
* `match` - pattern matching
|
||||
* `mod` - module declaration
|
||||
* `move` - makes a closure take ownership of all its captures
|
||||
* `mut` - denotes mutability in references, raw pointers, and pattern bindings
|
||||
* `pub` - denotes public visibility in struct fields, `impl` blocks, and modules
|
||||
* `ref` - by-reference binding
|
||||
* `return` - return from function
|
||||
* `Self` - type alias for the type implementing a trait
|
||||
* `self` - method subject or current module
|
||||
* `static` - global variable or lifetime lasting the entire program execution
|
||||
* `struct` - structure definition
|
||||
* `super` - parent module of the current module
|
||||
* `trait` - trait definition
|
||||
* `true` - Boolean true literal
|
||||
* `type` - type alias and associated type definition
|
||||
* `unsafe` - denotes unsafe code, functions, traits, and implementations
|
||||
* `use` - import symbols into scope
|
||||
* `where` - type constraint clauses
|
||||
* `while` - conditional loop
|
||||
|
||||
### Keywords Reserved for Future Use
|
||||
|
||||
These keywords do not have any functionality, but are reserved by Rust for
|
||||
potential future use.
|
||||
|
||||
* `abstract`
|
||||
* `alignof`
|
||||
* `as`
|
||||
* `become`
|
||||
* `box`
|
||||
* `break`
|
||||
* `const`
|
||||
* `continue`
|
||||
* `crate`
|
||||
* `do`
|
||||
* `else`
|
||||
* `enum`
|
||||
* `extern`
|
||||
* `false`
|
||||
* `final`
|
||||
* `fn`
|
||||
* `for`
|
||||
* `if`
|
||||
* `impl`
|
||||
* `in`
|
||||
* `let`
|
||||
* `loop`
|
||||
* `macro`
|
||||
* `match`
|
||||
* `mod`
|
||||
* `move`
|
||||
* `mut`
|
||||
* `offsetof`
|
||||
* `override`
|
||||
* `priv`
|
||||
* `proc`
|
||||
* `pub`
|
||||
* `pure`
|
||||
* `ref`
|
||||
* `return`
|
||||
* `Self`
|
||||
* `self`
|
||||
* `sizeof`
|
||||
* `static`
|
||||
* `struct`
|
||||
* `super`
|
||||
* `trait`
|
||||
* `true`
|
||||
* `type`
|
||||
* `typeof`
|
||||
* `unsafe`
|
||||
* `unsized`
|
||||
* `use`
|
||||
* `virtual`
|
||||
* `where`
|
||||
* `while`
|
||||
* `yield`
|
||||
|
||||
## Appendix B: Operators and Symbols
|
||||
|
||||
### Operators
|
||||
|
||||
The following lists the operators in Rust, an example of how the operator would
|
||||
appear in context, a short explanation, and whether that operator is
|
||||
overloadable. If an operator is overloadable, the relevant trait to use to
|
||||
overload that operator is listed.
|
||||
|
||||
* `!` (`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...expr`) *in an expression*: inclusive range expression.
|
||||
* `...` (`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`).
|
||||
* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`PartialOrd`).
|
||||
* `=` (`var = expr`, `ident = type`): assignment/equivalence.
|
||||
* `==` (`var == expr`): equality comparison. Overloadable (`PartialEq`).
|
||||
* `=>` (`pat => expr`): part of match arm syntax.
|
||||
* `>` (`expr > expr`): greater-than comparison. Overloadable (`PartialOrd`).
|
||||
* `>=` (`var >= expr`): greater-than or equal-to comparison. Overloadable (`PartialOrd`).
|
||||
* `>>` (`expr >> expr`): right-shift. Overloadable (`Shr`).
|
||||
* `>>=` (`var >>= expr`): right-shift and assignment. Overloadable (`ShrAssign`).
|
||||
* `@` (`ident @ pat`): pattern binding.
|
||||
* `^` (`expr ^ expr`): bitwise exclusive OR. Overloadable (`BitXor`).
|
||||
* `^=` (`var ^= expr`): bitwise exclusive OR and assignment. Overloadable (`BitXorAssign`).
|
||||
* `|` (`pat | pat`): pattern alternatives.
|
||||
* `|` (`|…| expr`): closures.
|
||||
* `|` (`expr | expr`): bitwise OR. Overloadable (`BitOr`).
|
||||
* `|=` (`var |= expr`): bitwise OR and assignment. Overloadable (`BitOrAssign`).
|
||||
* `||` (`expr || expr`): logical OR.
|
||||
* `_`: “ignored” pattern binding. Also used to make integer-literals readable.
|
||||
* `?` (`expr?`): Error propagation.
|
||||
|
||||
### Non-operator Symbols
|
||||
|
||||
#### Standalone Syntax
|
||||
|
||||
* `'ident`: named lifetime or loop label
|
||||
* `…u8`, `…i32`, `…f64`, `…usize`, *etc.*: numeric literal of specific type.
|
||||
* `"…"`: string literal.
|
||||
* `r"…"`, `r#"…"#`, `r##"…"##`, *etc.*: raw string literal, escape characters are not processed.
|
||||
* `b"…"`: byte string literal, constructs a `[u8]` instead of a string.
|
||||
* `br"…"`, `br#"…"#`, `br##"…"##`, *etc.*: raw byte string literal, combination of raw and byte string literal.
|
||||
* `'…'`: character literal.
|
||||
* `b'…'`: ASCII byte literal.
|
||||
* `|…| expr`: closure.
|
||||
* `!`: always empty bottom type for diverging functions.
|
||||
|
||||
#### Path-related Syntax
|
||||
|
||||
* `ident::ident`: namespace path.
|
||||
* `::path`: path relative to the crate root (*i.e.* an explicitly absolute path).
|
||||
* `self::path`: path relative to the current module (*i.e.* an explicitly relative path).
|
||||
* `super::path`: path relative to the parent of the current module.
|
||||
* `type::ident`, `<type as trait>::ident`: associated constants, functions, and types.
|
||||
* `<type>::…`: associated item for a type which cannot be directly named (*e.g.* `<&T>::…`, `<[T]>::…`, *etc.*).
|
||||
* `trait::method(…)`: disambiguating a method call by naming the trait which defines it.
|
||||
* `type::method(…)`: disambiguating a method call by naming the type for which it’s defined.
|
||||
* `<type as trait>::method(…)`: disambiguating a method call by naming the trait *and* type.
|
||||
|
||||
#### Generics
|
||||
|
||||
* `path<…>` (*e.g.* `Vec<u8>`): specifies parameters to generic type *in a type*.
|
||||
* `path::<…>`, `method::<…>` (*e.g.* `"42".parse::<i32>()`): specifies parameters to generic type, function, or method *in an expression*. Often referred to as *turbofish*.
|
||||
* `fn ident<…> …`: define generic function.
|
||||
* `struct ident<…> …`: define generic structure.
|
||||
* `enum ident<…> …`: define generic enumeration.
|
||||
* `impl<…> …`: define generic implementation.
|
||||
* `for<…> type`: higher-ranked lifetime bounds.
|
||||
* `type<ident=type>` (*e.g.* `Iterator<Item=T>`): a generic type where one or more associated types have specific assignments.
|
||||
|
||||
#### Trait Bound Constraints
|
||||
|
||||
* `T: U`: generic parameter `T` constrained to types that implement `U`.
|
||||
* `T: 'a`: generic type `T` must outlive lifetime `'a`. When we say that a type ‘outlives’ the lifetime, we mean that it cannot transitively contain any references with lifetimes shorter than `'a`.
|
||||
* `T : 'static`: The generic type `T` contains no borrowed references other than `'static` ones.
|
||||
* `'b: 'a`: generic lifetime `'b` must outlive lifetime `'a`.
|
||||
* `T: ?Sized`: allow generic type parameter to be a dynamically-sized type.
|
||||
* `'a + trait`, `trait + trait`: compound type constraint.
|
||||
|
||||
#### Macros and Attributes
|
||||
|
||||
* `#[meta]`: outer attribute.
|
||||
* `#![meta]`: inner attribute.
|
||||
* `$ident`: macro substitution.
|
||||
* `$ident:kind`: macro capture.
|
||||
* `$(…)…`: macro repetition.
|
||||
|
||||
#### Comments
|
||||
|
||||
* `//`: line comment.
|
||||
* `//!`: inner line doc comment.
|
||||
* `///`: outer line doc comment.
|
||||
* `/*…*/`: block comment.
|
||||
* `/*!…*/`: inner block doc comment.
|
||||
* `/**…*/`: outer block doc comment.
|
||||
|
||||
#### Tuples
|
||||
|
||||
* `()`: empty tuple (*a.k.a.* unit), both literal and type.
|
||||
* `(expr)`: parenthesized expression.
|
||||
* `(expr,)`: single-element tuple expression.
|
||||
* `(type,)`: single-element tuple type.
|
||||
* `(expr, …)`: tuple expression.
|
||||
* `(type, …)`: tuple type.
|
||||
* `expr(expr, …)`: function call expression. Also used to initialize tuple `struct`s and tuple `enum` variants.
|
||||
* `ident!(…)`, `ident!{…}`, `ident![…]`: macro invocation.
|
||||
* `expr.0`, `expr.1`, …: tuple indexing.
|
||||
|
||||
#### Curly Brackets
|
||||
|
||||
* `{…}`: block expression.
|
||||
* `Type {…}`: `struct` literal.
|
||||
|
||||
#### Square Brackets
|
||||
|
||||
* `[…]`: array literal.
|
||||
* `[expr; len]`: array literal containing `len` copies of `expr`.
|
||||
* `[type; len]`: array type containing `len` instances of `type`.
|
||||
* `expr[expr]`: collection indexing. Overloadable (`Index`, `IndexMut`).
|
||||
* `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]`: collection indexing pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, `RangeFull` as the “index”.
|
||||
|
||||
# C - Derivable Traits
|
||||
|
||||
In various places in the book, we discussed the `derive` attribute that is
|
||||
applied to a struct or enum. This attribute generates code that implements a
|
||||
trait on the annotated type with a default implementation. In this example, the
|
||||
`#[derive(Debug)]` attribute implements the `Debug` trait for the `Point`
|
||||
struct:
|
||||
|
||||
```
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
```
|
||||
|
||||
The code that the compiler generates for the implementation of `Debug` is
|
||||
similar to this code:
|
||||
|
||||
```
|
||||
impl ::std::fmt::Debug for Point {
|
||||
fn fmt(&self, __arg_0: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
match *self {
|
||||
Point { x: ref __self_0_0, y: ref __self_0_1 } => {
|
||||
let mut builder = __arg_0.debug_struct("Point");
|
||||
let _ = builder.field("x", &&(*__self_0_0));
|
||||
let _ = builder.field("y", &&(*__self_0_1));
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The generated code implements sensible default behavior for the `Debug` trait’s
|
||||
`fmt` function: a `match` expression destructures a `Point` instance into its
|
||||
field values. Then it builds up a string containing the struct’s name and each
|
||||
field’s name and value. This means we’re able to use debug formatting on a
|
||||
`Point` instance to see what value each field has.
|
||||
|
||||
The generated code isn’t particularly easy to read because it’s only for the
|
||||
compiler to consume, rather than for programmers to read! The `derive`
|
||||
attribute and the default implementation of `Debug` has saved us all of the
|
||||
work of writing this code for every struct or enum that we want to be able to
|
||||
print using debug formatting.
|
||||
|
||||
The `derive` attribute has default implementations for the following traits
|
||||
provided by the standard library. If you want different behavior than what the
|
||||
`derive` attribute provides, consult the standard library documentation for
|
||||
each trait for the details needed for manual implementation of the traits.
|
||||
|
||||
## Standard Library Traits that Can Be Derived
|
||||
|
||||
The following sections list 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
|
||||
|
||||
### `Debug` for Programmer Output
|
||||
|
||||
The `Debug` trait enables debug formatting in format strings, indicated by
|
||||
adding `:?` within `{}` placeholders.
|
||||
|
||||
The `Debug` trait signifies that instances of a type may be printed by
|
||||
programmers in order to debug their programs by inspecting an instance of a
|
||||
type at a particular point in a program’s execution.
|
||||
|
||||
An example of when `Debug` is required is the `assert_eq!` macro, which prints
|
||||
the values of the instances given as arguments if the equality assertion fails
|
||||
so that programmers can see why the two instances weren’t equal.
|
||||
|
||||
### `PartialEq` and `Eq` for Equality Comparisons
|
||||
|
||||
The `PartialEq` trait signifies that instances of a type can be compared to
|
||||
each other for equality, and enables use of the `==` and `!=` operators.
|
||||
|
||||
Deriving `PartialEq` implements the `eq` method. When derived on structs, two
|
||||
instances are equal 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.
|
||||
|
||||
An example of when `PartialEq` is required is the `assert_eq!` macro, which
|
||||
needs to be able to compare two instances of a type for equality.
|
||||
|
||||
The `Eq` trait doesn’t have any methods. It only signals 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`. An example of types that
|
||||
implements `PartialEq` but that cannot implement `Eq` are floating point number
|
||||
types: the implementation of floating point numbers says that two instances of
|
||||
the not-a-number value, `NaN`, are not equal to each other.
|
||||
|
||||
An example of when `Eq` is required is for keys in a `HashMap` so that the
|
||||
`HashMap` can tell whether two keys are the same.
|
||||
|
||||
### `PartialOrd` and `Ord` for Ordering Comparisons
|
||||
|
||||
The `PartialOrd` trait signifies that instances of a type can be compared to
|
||||
each other to see which is larger than the other for sorting purposes. A type
|
||||
that implements `PartialOrd` may be used with the `<`, `>`, `<=`, and `>=`
|
||||
operators. The `PartialOrd` trait can only be applied to types that also
|
||||
implement `PartialEq`.
|
||||
|
||||
Deriving `PartialOrd` implements the `partial_cmp` method, which returns an
|
||||
`Option<Ordering>` that may be `None` if comparing the given values does not
|
||||
produce an ordering. When derived on structs, two instances of the struct are
|
||||
compared 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 greater than the variants listed
|
||||
later.
|
||||
|
||||
An example of when `PartialOrd` is required is the `gen_range` method in the
|
||||
`rand` crate that generates a random value in the range specified by a low
|
||||
value and a high value.
|
||||
|
||||
The `Ord` trait signifies that for any two value of the annotated type, a valid
|
||||
ordering exists. The `Ord` trait implements the `cmp` method, which returns an
|
||||
`Ordering` rather than an `Option<Ordering>` because a valid ordering will
|
||||
always be possible. The `Ord` trait can only be applied to types that also
|
||||
implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When derived
|
||||
on structs and enums, `cmp` behaves the same way as the derived implementation
|
||||
for `partial_cmp` does with `PartialOrd`.
|
||||
|
||||
An example of when `Ord` is required is when storing values in a `BTreeSet<T>`,
|
||||
a data structure that stores data based on the sort order of the values.
|
||||
|
||||
### `Clone` and `Copy` for Duplicating Values
|
||||
|
||||
The `Clone` trait signifies there is a way to explicitly create a duplicate of
|
||||
a value, and the duplication process might involve running arbitrary code.
|
||||
Deriving `Clone` implements the `clone` method. When derived, the
|
||||
implementation of `clone` for the whole type calls `clone` on each of the parts
|
||||
of the type, so all of the fields or values in the type must also implement
|
||||
`Clone` to derive `Clone`.
|
||||
|
||||
An example of when `Clone` is required is when calling the `to_vec` method on a
|
||||
slice containing instances of some type. The slice doesn’t own the instances
|
||||
but the vector returned from `to_vec` will need to own its instances, so the
|
||||
implementation of `to_vec` calls `clone` on each item. Thus, the type stored in
|
||||
the slice must implement `Clone`.
|
||||
|
||||
The `Copy` trait signifies that a value can be duplicated by only copying bits;
|
||||
no other code is necessary. 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. You can derive `Copy` on any type whose
|
||||
parts all implement `Copy`. The `Copy` trait can only be applied to types that
|
||||
also implement `Clone`, as a type that implements `Copy` has a trivial
|
||||
implementation of `Clone`, doing the same thing as `Copy`.
|
||||
|
||||
`Copy` is rarely required; when types implement `Copy`, there are optimizations
|
||||
that can be applied and the code becomes nicer because you don’t have to call
|
||||
`clone`. Everything possible with `Copy` can also be accomplished with `Clone`,
|
||||
but the code might be slower or have to use `clone` in places.
|
||||
|
||||
### `Hash` for Mapping a Value to a Value of Fixed Size
|
||||
|
||||
The `Hash` trait signifies there is a way to take an instance of a type that
|
||||
takes up an arbitrary amount of size and map that instance to a value of fixed
|
||||
size by using a hash function. Deriving `Hash` implements the `hash` method.
|
||||
When derived, the implementation of `hash` for the whole type combines the
|
||||
result of calling `hash` on each of the parts of the type, so all of the fields
|
||||
or values in the type must also implement `Hash` to derive `Hash`.
|
||||
|
||||
An example of when `Hash` is required is for keys in a `HashMap` so that the
|
||||
`HashMap` can store data efficiently.
|
||||
|
||||
### `Default` for Default Values
|
||||
|
||||
The `Default` trait signifies there is a way to create a default value for a
|
||||
type. Deriving `Default` implements the `default` method. When derived, the
|
||||
implementation of `Default` for the whole type calls the `default` method on
|
||||
each of the parts of the type, so all of the fields or values in the type must
|
||||
also implement `Default` to derive `Default.`
|
||||
|
||||
A common use of `Default::default` is 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 use the default values for the rest by using `..Default::default()`.
|
||||
|
||||
An example of when `Default` is required is 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>`.
|
||||
|
||||
## Standard Library Traits that Can’t Be Derived
|
||||
|
||||
The rest of the traits defined in the standard library can’t be implemented on
|
||||
your types using `derive`. These traits don’t have a sensible default behavior
|
||||
they could have, so you are required to implement them in the way that makes
|
||||
sense for what you are trying to accomplish with your code.
|
||||
|
||||
An example of a trait that can’t be derived is `Display`, which handles
|
||||
formatting of a type for end users of your programs. You should 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 into your application, so you must provide it.
|
||||
|
||||
## Making Custom Traits Derivable
|
||||
|
||||
The above list is not comprehensive, however: libraries can implement `derive`
|
||||
for their own types! 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 the next appendix, “Macros.”
|
||||
|
||||
# D - Macros
|
||||
|
||||
We’ve used macros, such as `println!`, throughout this book. This appendix will
|
||||
explain:
|
||||
|
||||
- What macros are and how they differ from functions
|
||||
- How to define a declarative macro to do metaprogramming
|
||||
- How to define a procedural macro to create custom `derive` traits
|
||||
|
||||
Macros are covered in an appendix because they’re still evolving. They have
|
||||
changed and will change more than the rest of the language and standard library
|
||||
since Rust 1.0, so this section will likely get out of date more than the rest
|
||||
of this book. The code shown here will still continue to work due to Rust’s
|
||||
stability guarantees, but there may be additional capabilities or easier ways
|
||||
to write macros that aren’t available at the time of this publication.
|
||||
|
||||
## Macros are More Flexible and Complex than Functions
|
||||
|
||||
Fundamentally, macros are a way of writing code that writes other code, which
|
||||
is known as *metaprogramming*. In the previous appendix, we discussed the
|
||||
`derive` attribute, which generates an implementation of various traits for
|
||||
you. We’ve also used the `println!` and `vec!` macros. All of these macros
|
||||
*expand* to produce more code than what you’ve written in your source code.
|
||||
|
||||
Metaprogramming is useful to reduce 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, as we discussed in Chapter 1.
|
||||
A function signature has to declare the number and type of parameters the
|
||||
function has. Macros can take a variable number of parameters: we can call
|
||||
`println!("hello")` with one argument, or `println!("hello {}", name)` with two
|
||||
arguments. Also, macros are expanded before the compiler interprets the meaning
|
||||
of the code, so a macro can, for example, implement a trait on a given type,
|
||||
whereas a function can’t because a function gets called at runtime and a trait
|
||||
needs to be implemented at compile time.
|
||||
|
||||
The downside to implementing a macro rather than a function is that macro
|
||||
definitions are more complex than function definitions. You’re writing Rust
|
||||
code that writes Rust code, and 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 a crate, when bringing an external
|
||||
crate into the scope of your project, you have to explicitly bring the macros
|
||||
into the scope of your project as well with the `#[macro_use]` annotation. This
|
||||
example would bring all the macros defined in the `serde` crate into the scope
|
||||
of the current crate:
|
||||
|
||||
```
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
```
|
||||
|
||||
If `extern crate` also brought macros into scope by default, you wouldn’t be
|
||||
allowed to use two crates that happened to define macros with the same name. In
|
||||
practice this conflict doesn’t come up much, but the more crates you use, the
|
||||
more likely it is.
|
||||
|
||||
One last important difference between macros and functions: macros must be
|
||||
defined or brought into scope before they’re called in a file. Unlike
|
||||
functions, where we can define a function at the bottom of a file yet call it
|
||||
at the top, we always have to define macros before we’re able to call them.
|
||||
|
||||
## Declarative Macros with `macro_rules!` for General Metaprogramming
|
||||
|
||||
The first form of macros in Rust, and the one that’s most widely used, is
|
||||
called *declarative macros*. These are also sometimes referred to as *macros by
|
||||
example*, *`macro_rules!` macros*, or just plain *macros*. At their core,
|
||||
declarative macros allow you to write something similar to a Rust `match`
|
||||
expression. As discussed in Chapter 6, `match` expressions are control
|
||||
structures that take an expression, compare the resulting value of the
|
||||
expression to patterns, and then choose the code specified with the matching
|
||||
pattern when the program runs. Macros also have a value that is compared to
|
||||
patterns that have code associated with them, but the value is the literal Rust
|
||||
code passed to the macro, the patterns match the structure of that source code,
|
||||
and the code associated with each pattern is the code that is generated to
|
||||
replace 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 that holds
|
||||
particular values. For example, this macro creates a new vector with three
|
||||
integers inside:
|
||||
|
||||
```
|
||||
let v: Vec<u32> = vec![1, 2, 3];
|
||||
```
|
||||
|
||||
We can also use `vec!` to make a vector of two integers or a vector of five
|
||||
string slices. Because we don’t know the number or type of values, we can’t
|
||||
define a function that is able to create a new vector with the given elements
|
||||
like `vec!` can.
|
||||
|
||||
Let’s take a look at a slightly simplified definition of the `vec!` macro:
|
||||
|
||||
```
|
||||
#[macro_export]
|
||||
macro_rules! vec {
|
||||
( $( $x:expr ),* ) => {
|
||||
{
|
||||
let mut temp_vec = Vec::new();
|
||||
$(
|
||||
temp_vec.push($x);
|
||||
)*
|
||||
temp_vec
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
> Note: the actual definition of the `vec!` macro in the standard library also
|
||||
> has code to pre-allocate the correct amount of memory up-front. That code
|
||||
> is an optimization that we’ve chosen not to include here for simplicity.
|
||||
|
||||
The `#[macro_export]` annotation indicates that this macro should be made
|
||||
available when other crates import the crate in which we’re defining this
|
||||
macro. Without this annotation, even if someone depending on this crate uses
|
||||
the `#[macro_use]` annotation, this macro would not be brought into scope.
|
||||
|
||||
Macro definitions start with `macro_rules!` and the name of the macro we’re
|
||||
defining without the exclamation mark, which in this case is `vec`. This is
|
||||
followed by curly brackets denoting the body of the macro definition.
|
||||
|
||||
Inside the body is a structure similar to the structure of a `match`
|
||||
expression. This macro definition has one arm with the pattern `( $( $x:expr
|
||||
),* )`, followed by `=>` and the block of code associated with this pattern. If
|
||||
this pattern matches, then the block of code will be emitted. Given that this
|
||||
is the only pattern in this macro, there’s only one valid way to match; any
|
||||
other will be an error. More complex macros will have more than one arm.
|
||||
|
||||
The pattern syntax valid in macro definitions is different than the pattern
|
||||
syntax covered in Chapter 18 because the patterns are for matching against Rust
|
||||
code structure rather than values. Let’s walk through what the pieces of the
|
||||
pattern used here mean; for the full macro pattern syntax, see the reference at
|
||||
*https://doc.rust-lang.org/stable/reference/macros.html*.
|
||||
|
||||
The `$x:expr` part of the pattern matches any Rust expression and gives the
|
||||
expression the name `$x`. The `*` specifies that the pattern matches zero or
|
||||
more of whatever precedes the `*`. In this case, `*` is preceded by `$(),` so
|
||||
this pattern matches zero or more of whatever is inside the parentheses,
|
||||
delimited by a comma. When we call this macro with `vec![1, 2, 3];`, the
|
||||
pattern matches the three expressions `1`, `2`, and `3`.
|
||||
|
||||
In the body of the code associated with this arm, 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` in the code associated with the
|
||||
arm 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:
|
||||
|
||||
```
|
||||
let mut temp_vec = Vec::new();
|
||||
temp_vec.push(1);
|
||||
temp_vec.push(2);
|
||||
temp_vec.push(3);
|
||||
temp_vec
|
||||
```
|
||||
|
||||
We’ve defined a macro that can take any number of arguments of any type and can
|
||||
generate code to create a vector containing the specified elements.
|
||||
|
||||
Given that most Rust programmers will *use* macros more than *write* macros,
|
||||
that’s all we’ll discuss about `macro_rules!` in this book. To learn more about
|
||||
how to write macros, consult the online documentation or other resources such
|
||||
as The Little Book of Rust Macros at
|
||||
*https://danielkeep.github.io/tlborm/book/index.html*.
|
||||
|
||||
## Procedural Macros for Custom `derive`
|
||||
|
||||
The second form of macros is called *procedural macros* because they’re more
|
||||
like functions (which are a type of procedure). Procedural macros accept some
|
||||
Rust code as an input, operate on that code, and produce some Rust code as an
|
||||
output, rather than matching against patterns and replacing the code with other
|
||||
code as declarative macros do. Today, the only thing you can define procedural
|
||||
macros for is to allow your traits to be implemented on a type by specifying
|
||||
the trait name in a `derive` annotation.
|
||||
|
||||
Let’s create a crate named `hello-world` that defines a trait named
|
||||
`HelloWorld` with one associated function named `hello_world`. Rather than
|
||||
making users of our crate implement the `HelloWorld` trait for each of their
|
||||
types, we’d like users to be able to annotate their type with
|
||||
`#[derive(HelloWorld)]` to get a default implementation of the `hello_world`
|
||||
function associated with their type. The default implementation will print
|
||||
`Hello world, 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 that looks like Listing A4-1 using our crate:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
extern crate hello_world;
|
||||
#[macro_use]
|
||||
extern crate hello_world_derive;
|
||||
|
||||
use hello_world::HelloWorld;
|
||||
|
||||
#[derive(HelloWorld)]
|
||||
struct Pancakes;
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
Listing A4-1: The code a user of our crate will be able to write when we’ve
|
||||
written the procedural macro
|
||||
|
||||
This code will print `Hello world, my name is Pancakes!` when we’re done. Let’s
|
||||
get started!
|
||||
|
||||
Let’s make a new library crate:
|
||||
|
||||
```
|
||||
$ cargo new hello-world
|
||||
```
|
||||
|
||||
First, we’ll define the `HelloWorld` trait and associated function:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
```
|
||||
pub trait HelloWorld {
|
||||
fn hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
At this point, a user of our crate could implement the trait themselves to
|
||||
achieve the functionality we wanted to enable, like so:
|
||||
|
||||
```
|
||||
extern crate hello_world;
|
||||
|
||||
use hello_world::HelloWorld;
|
||||
|
||||
struct Pancakes;
|
||||
|
||||
impl HelloWorld for Pancakes {
|
||||
fn hello_world() {
|
||||
println!("Hello world, my name is Pancakes!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
However, they would need to write out the implementation block for each type
|
||||
they wanted to be able to use with `hello_world`; we’d like to make using our
|
||||
trait more convenient for other programmers by saving them this work.
|
||||
|
||||
Additionally, we can’t provide a default implementation for the `hello_world`
|
||||
function that has the behavior we want of printing out the name of the type the
|
||||
trait is implemented on: Rust doesn’t have reflection capabilities, so we 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
|
||||
|
||||
The next step is to define the procedural macro. At the moment, procedural
|
||||
macros need to be in their own crate. Eventually, this restriction may be
|
||||
lifted, but for now, it’s required. As such, there’s a convention: for a crate
|
||||
named `foo`, a custom derive procedural macro crate is called `foo-derive`.
|
||||
Let’s start a new crate called `hello-world-derive` inside our `hello-world`
|
||||
project:
|
||||
|
||||
```
|
||||
$ cargo new hello-world-derive
|
||||
```
|
||||
|
||||
We’ve chosen to create the procedural macro crate within the directory of our
|
||||
`hello-world` crate because the two crates are tightly related: if we change
|
||||
the trait definition in `hello-world`, we’ll have to change the implementation
|
||||
of the procedural macro in `hello-world-derive` as well. The two crates will
|
||||
need to be published separately, and programmers using these crates will need
|
||||
to add both as dependencies and bring them both into scope. It’s possible to
|
||||
have the `hello-world` crate use `hello-world-derive` as a dependency and
|
||||
re-export the procedural macro code, but structuring the project this way makes
|
||||
it possible for programmers to easily decide they only want to use
|
||||
`hello-world` if they don’t want the `derive` functionality.
|
||||
|
||||
We need to declare that the `hello-world-derive` crate is a procedural macro
|
||||
crate. We also need to add dependencies on the `syn` and `quote` crates to get
|
||||
useful functionality for operating on Rust code. To do these two things, add
|
||||
the following to the *Cargo.toml* for `hello-world-derive`:
|
||||
|
||||
Filename: hello-world-derive/Cargo.toml
|
||||
|
||||
```
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.11.11"
|
||||
quote = "0.3.15"
|
||||
```
|
||||
|
||||
To start defining the procedural macro, place the code from Listing A4-2 in
|
||||
*src/lib.rs* for the `hello-world-derive` crate. Note that this won’t compile
|
||||
until we add a definition for the `impl_hello_world` function. We’ve split the
|
||||
code into functions in this way because the code in Listing A4-2 will be the
|
||||
same for almost every procedural macro crate; it’s code that makes writing a
|
||||
procedural macro more convenient. What you choose to do in the place where the
|
||||
`impl_hello_world` function is called will be different and depend on the
|
||||
purpose of your procedural macro.
|
||||
|
||||
Filename: hello-world-derive/src/lib.rs
|
||||
|
||||
```
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_derive(HelloWorld)]
|
||||
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let s = input.to_string();
|
||||
|
||||
// Parse the string representation
|
||||
let ast = syn::parse_derive_input(&s).unwrap();
|
||||
|
||||
// Build the impl
|
||||
let gen = impl_hello_world(&ast);
|
||||
|
||||
// Return the generated impl
|
||||
gen.parse().unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
Listing A4-2: Code that most procedural macro crates will need to have for
|
||||
processing Rust code
|
||||
|
||||
We have introduced three new crates: `proc_macro`, `syn` (available from
|
||||
*https://crates.io/crates/syn*), and `quote` (available from
|
||||
*https://crates.io/crates/quote*). The `proc_macro` crate comes with Rust, so
|
||||
we didn’t need to add that to the dependencies in *Cargo.toml*. The
|
||||
`proc_macro` crate allows us to convert Rust code into a string containing that
|
||||
Rust code. The `syn` crate parses Rust code from a string into a data structure
|
||||
that we can perform operations on. The `quote` crate takes `syn` data
|
||||
structures and turns them back into Rust code. These crates make it much
|
||||
simpler to parse any sort of Rust code we might want to handle: writing a full
|
||||
parser for Rust code is no simple task.
|
||||
|
||||
The `hello_world_derive` function is the code that will get called when a user
|
||||
of our library specifies the `#[derive(HelloWorld)]` annotation on a type
|
||||
because we’ve annotated the `hello_world_derive` function here with
|
||||
`proc_macro_derive` and specified the same name, `HelloWorld`. This name
|
||||
matches our trait named `HelloWorld`; that’s the convention most procedural
|
||||
macros follow.
|
||||
|
||||
The first thing this function does is convert 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 `HelloWorld`. In the example in
|
||||
Listing A4-1, `s` will have the `String` value `struct Pancakes;` because
|
||||
that’s the Rust code we added the `#[derive(HelloWorld)]` annotation to.
|
||||
|
||||
At the moment, the only thing you can do with a `TokenStream` is convert it to
|
||||
a string. A richer API will exist in the future.
|
||||
|
||||
What we really need is 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.
|
||||
Here’s the relevant parts of the `DeriveInput` struct we get from parsing the
|
||||
string `struct Pancakes;`:
|
||||
|
||||
```
|
||||
DeriveInput {
|
||||
// --snip--
|
||||
|
||||
ident: Ident(
|
||||
"Pancakes"
|
||||
),
|
||||
body: Struct(
|
||||
Unit
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The fields of this struct show that the Rust code we’ve parsed is a unit struct
|
||||
with the `ident` (identifier, meaning the name) of `Pancakes`. There are more
|
||||
fields on this struct for describing all sorts of Rust code; check the `syn`
|
||||
API docs for `DeriveInput` at
|
||||
*https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html* for more information.
|
||||
|
||||
We haven’t defined the `impl_hello_world` function; that’s where we’ll build
|
||||
the new Rust code we want to include. Before we get to that, the last part of
|
||||
this `hello_world_derive` function is using the `quote` crate’s `parse`
|
||||
function to turn the output of the `impl_hello_world` function back into a
|
||||
`TokenStream`. The returned `TokenStream` is added to the code that users of
|
||||
our crate write so that when they compile their crate, they get extra
|
||||
functionality we provide.
|
||||
|
||||
You may have noticed that we’re calling `unwrap` to panic if the calls to the
|
||||
`parse_derive_input` or `parse` functions fail because they’re unable to parse
|
||||
the `TokenStream` or generate a `TokenStream`. Panicking on errors is necessary
|
||||
in procedural macro code because `proc_macro_derive` functions must return
|
||||
`TokenStream` rather than `Result` in order to conform to the procedural macro
|
||||
API. We’ve chosen to keep this example simple by using `unwrap`; in production
|
||||
code you should provide more specific error messages about what went wrong by
|
||||
using `expect` or `panic!`.
|
||||
|
||||
Now that we have the code to turn the annotated Rust code from a `TokenStream`
|
||||
into a `String` and into a `DeriveInput` instance, let’s write the code that
|
||||
will generate the code implementing the `HelloWorld` trait on the annotated
|
||||
type:
|
||||
|
||||
Filename: hello-world-derive/src/lib.rs
|
||||
|
||||
```
|
||||
fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let name = &ast.ident;
|
||||
quote! {
|
||||
impl HelloWorld for #name {
|
||||
fn hello_world() {
|
||||
println!("Hello, World! My name is {}", stringify!(#name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We are able to get an `Ident` struct instance containing the name (identifier)
|
||||
of the annotated type using `ast.ident`. With the code from Listing A4-1,
|
||||
`name` will be `Ident("Pancakes")`.
|
||||
|
||||
The `quote!` macro from the `quote` crate lets us write up the Rust code that
|
||||
we wish to return and convert it into `quote::Tokens`. The `quote!` macro lets
|
||||
us use some really cool templating mechanics; we can write `#name` and `quote!`
|
||||
will replace it with the value in the variable named `name`. You can even do
|
||||
some repetition similar to the way regular macros work. Check out the `quote`
|
||||
crate’s docs at *https://docs.rs/quote* for a thorough introduction.
|
||||
|
||||
What we want to do for our procedural macro is generate an implementation of
|
||||
our `HelloWorld` trait for the type the user of our crate has annotated, which
|
||||
we can get by using `#name`. The trait implementation has one function,
|
||||
`hello_world`, and the function body contains the functionality we want to
|
||||
provide: printing `Hello, World! My name is` and then the name of the type the
|
||||
user of our crate has annotated. 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 `#name` would be an expression that
|
||||
we would want to print out literally, and `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-world`
|
||||
and `hello-world-derive`. Let’s hook these crates up to the code in Listing
|
||||
A4-1 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-world`
|
||||
and `hello-world-derive` as dependencies in the `pancakes` crate’s
|
||||
*Cargo.toml*. If you’ve chosen to publish your versions of `hello-world` and
|
||||
`hello-world-derive` to *https://crates.io* they would be regular dependencies;
|
||||
if not, you can specify them as `path` dependencies as follows:
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
hello_world = { path = "../hello-world" }
|
||||
hello_world_derive = { path = "../hello-world/hello-world-derive" }
|
||||
```
|
||||
|
||||
Put the code from Listing A4-1 into *src/main.rs*, and executing `cargo run`
|
||||
should print `Hello, World! My name is Pancakes`! The implementation of the
|
||||
`HelloWorld` trait from the procedural macro was included without the
|
||||
`pancakes` crate needing to implement it; the `#[derive(HelloWorld)]` took care
|
||||
of adding the trait implementation.
|
||||
|
||||
## The Future of Macros
|
||||
|
||||
In the future, we’ll be expanding both declarative and procedural macros. A
|
||||
better declarative macro system will be used with the `macro` keyword, and
|
||||
we’ll add more types of procedural macros, for more powerful tasks than only
|
||||
`derive`. These systems are still under development at the time of publication;
|
||||
please consult the online Rust documentation for the latest information.
|
||||
|
@ -331,7 +331,7 @@ To ensure memory safety, there’s one more detail to what happens in this
|
||||
situation in Rust. Instead of trying to copy the allocated memory, Rust
|
||||
considers `s1` to no longer be valid and therefore, Rust doesn’t need to free
|
||||
anything when `s1` goes out of scope. Check out what happens when you try to
|
||||
use `s1` after `s2` is created, it won't work:
|
||||
use `s1` after `s2` is created, it won’t work:
|
||||
|
||||
```
|
||||
let s1 = String::from("hello");
|
||||
|
@ -678,7 +678,7 @@ parameter will be by looking at the code that calls the method:
|
||||
read `rect2` (rather than write, which would mean we’d need a mutable borrow),
|
||||
and we want `main` to retain ownership of `rect2` so we can use it again after
|
||||
calling the `can_hold` method. The return value of `can_hold` will be a
|
||||
boolean, and the implementation will check whether the width and height of
|
||||
Boolean, and the implementation will check whether the width and height of
|
||||
`self` are both greater than the width and height of the other `Rectangle`,
|
||||
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||
Listing 5-13, shown in Listing 5-15:
|
||||
|
@ -432,7 +432,7 @@ as its patterns.
|
||||
Let’s break down the `match` in the `value_in_cents` function. First, we list
|
||||
the `match` keyword followed by an expression, which in this case is the value
|
||||
`coin`. This seems very similar to an expression used with `if`, but there’s a
|
||||
big difference: with `if`, the expression needs to return a boolean value.
|
||||
big difference: with `if`, the expression needs to return a Boolean value.
|
||||
Here, it can be any type. The type of `coin` in this example is the `Coin` enum
|
||||
that we defined in Listing 6-3.
|
||||
|
||||
|
@ -231,7 +231,7 @@ These would be good reasons to separate the `client`, `network`, and `server`
|
||||
modules from *src/lib.rs* and place them into their own files.
|
||||
|
||||
First, replace the `client` module code with only the declaration of the
|
||||
`client` module, so that your *src/lib.rs* looks like the following:
|
||||
`client` module, so that your *src/lib.rs* looks like code shown in Listing 7-4:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
@ -249,6 +249,9 @@ mod network {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 7-4: Extracting the contents of the `client` module but leaving the
|
||||
declaration in *src/lib.rs*
|
||||
|
||||
We’re still *declaring* the `client` module here, but by replacing the block
|
||||
with a semicolon, we’re telling Rust to look in another location for the code
|
||||
defined within the scope of the `client` module. In other words, the line `mod
|
||||
@ -372,7 +375,7 @@ fn connect() {
|
||||
}
|
||||
```
|
||||
|
||||
When we try to `cargo build`, we’ll get the error shown in Listing 7-4:
|
||||
When we try to `cargo build`, we’ll get the error shown in Listing 7-5:
|
||||
|
||||
```
|
||||
$ cargo build
|
||||
@ -395,14 +398,14 @@ note: ... or maybe `use` the module `server` instead of possibly redeclaring it
|
||||
| ^^^^^^
|
||||
```
|
||||
|
||||
Listing 7-4: Error when trying to extract the `server` submodule into
|
||||
Listing 7-5: Error when trying to extract the `server` submodule into
|
||||
*src/server.rs*
|
||||
|
||||
The error says we `cannot declare a new module at this location` and is
|
||||
pointing to the `mod server;` line in *src/network.rs*. So *src/network.rs* is
|
||||
different than *src/lib.rs* somehow: keep reading to understand why.
|
||||
|
||||
The note in the middle of Listing 7-4 is actually very helpful because it
|
||||
The note in the middle of Listing 7-5 is actually very helpful because it
|
||||
points out something we haven’t yet talked about doing:
|
||||
|
||||
```
|
||||
@ -509,7 +512,7 @@ Next, we’ll talk about the `pub` keyword and get rid of those warnings!
|
||||
|
||||
## Controlling Visibility with `pub`
|
||||
|
||||
We resolved the error messages shown in Listing 7-4 by moving the `network` and
|
||||
We resolved the error messages shown in Listing 7-5 by moving the `network` and
|
||||
`network::server` code into the *src/network/mod.rs* and
|
||||
*src/network/server.rs* files, respectively. At that point, `cargo build` was
|
||||
able to build our project, but we still get warning messages about the
|
||||
@ -750,7 +753,7 @@ Overall, these are the rules for item visibility:
|
||||
### Privacy Examples
|
||||
|
||||
Let’s look at a few more privacy examples to get some practice. Create a new
|
||||
library project and enter the code in Listing 7-5 into your new project’s
|
||||
library project and enter the code in Listing 7-6 into your new project’s
|
||||
*src/lib.rs*:
|
||||
|
||||
Filename: src/lib.rs
|
||||
@ -776,7 +779,7 @@ fn try_me() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 7-5: Examples of private and public functions, some of which are
|
||||
Listing 7-6: Examples of private and public functions, some of which are
|
||||
incorrect
|
||||
|
||||
Before you try to compile this code, make a guess about which lines in the
|
||||
@ -826,7 +829,7 @@ Next, let’s talk about bringing items into scope with the `use` keyword.
|
||||
|
||||
We’ve covered how to call functions defined within a module using the module
|
||||
name as part of the call, as in the call to the `nested_modules` function shown
|
||||
here in Listing 7-6:
|
||||
here in Listing 7-7:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@ -844,7 +847,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 7-6: Calling a function by fully specifying its enclosing module’s path
|
||||
Listing 7-7: Calling a function by fully specifying its enclosing module’s path
|
||||
|
||||
As you can see, referring to the fully qualified name can get quite lengthy.
|
||||
Fortunately, Rust has a keyword to make these calls more concise.
|
||||
@ -991,7 +994,7 @@ communicator
|
||||
|
||||
Tests are for exercising the code within our library, so let’s try to call our
|
||||
`client::connect` function from this `it_works` function, even though we won’t
|
||||
be checking any functionality right now. This won't work yet:
|
||||
be checking any functionality right now. This won’t work yet:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
@ -1081,7 +1084,7 @@ $ cargo test
|
||||
running 1 test
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
@ -141,7 +141,7 @@ argument, which gives us an `Option<&T>`.
|
||||
|
||||
The reason Rust has two ways to reference an element is 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
|
||||
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:
|
||||
|
||||
@ -177,7 +177,7 @@ 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 we 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
|
||||
the first element in a vector and try to add an element to the end, which won't
|
||||
the first element in a vector and try to add an element to the end, which won’t
|
||||
work:
|
||||
|
||||
```
|
||||
@ -194,15 +194,16 @@ to an item
|
||||
Compiling this code will result in this error:
|
||||
|
||||
```
|
||||
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
|
||||
immutable
|
||||
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
|
||||
-->
|
||||
|
|
||||
4 | let first = &v[0];
|
||||
| - immutable borrow occurs here
|
||||
5 |
|
||||
6 | v.push(6);
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
7 |
|
||||
8 | }
|
||||
| - immutable borrow ends here
|
||||
```
|
||||
|
||||
|
@ -102,17 +102,18 @@ Filename: src/main.rs
|
||||
fn main() {
|
||||
let v = vec![1, 2, 3];
|
||||
|
||||
v[100];
|
||||
v[99];
|
||||
}
|
||||
```
|
||||
|
||||
Listing 9-1: Attempting to access an element beyond the end of a vector, which
|
||||
will cause a `panic!`
|
||||
|
||||
Here, we’re attempting to access the hundredth element of our vector, but it
|
||||
has only three elements. In this situation, Rust will panic. Using `[]` is
|
||||
supposed to return an element, but if you pass an invalid index, there’s no
|
||||
element that Rust could return here that would be correct.
|
||||
Here, we’re attempting to access the hundredth element of our vector (which is
|
||||
at index 99 because indexing starts at zero), but it has only three elements.
|
||||
In this situation, Rust will panic. Using `[]` is supposed to return an
|
||||
element, but if you pass an invalid index, there’s no element that Rust could
|
||||
return here that would be correct.
|
||||
|
||||
Other languages, like C, will attempt to give you exactly what you asked for in
|
||||
this situation, even though it isn’t what you want: you’ll get whatever is at
|
||||
@ -634,6 +635,7 @@ fn read_username_from_file() -> Result<String, io::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
File::open("hello.txt")?.read_to_string(&mut s)?;
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
```
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -220,7 +220,7 @@ Filename: src/lib.rs
|
||||
//! calculations more convenient.
|
||||
|
||||
/// Adds one to the number given.
|
||||
// ...snip...
|
||||
// --snip--
|
||||
```
|
||||
|
||||
Listing 14-3: Documentation for the `my_crate` crate as a whole
|
||||
@ -303,7 +303,7 @@ pub mod utils {
|
||||
/// Combines two primary colors in equal amounts to create
|
||||
/// a secondary color.
|
||||
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -372,11 +372,11 @@ pub use kinds::SecondaryColor;
|
||||
pub use utils::mix;
|
||||
|
||||
pub mod kinds {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
pub mod utils {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -404,7 +404,7 @@ use art::PrimaryColor;
|
||||
use art::mix;
|
||||
|
||||
fn main() {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -469,7 +469,7 @@ $ cargo publish
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
warning: manifest has no description, license, license-file, documentation,
|
||||
homepage or repository.
|
||||
...snip...
|
||||
--snip--
|
||||
error: api errors: missing or empty metadata fields: description, license.
|
||||
```
|
||||
|
||||
@ -766,7 +766,7 @@ and compile the `rand` crate:
|
||||
$ cargo build
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
Downloading rand v0.3.14
|
||||
...snip...
|
||||
--snip--
|
||||
Compiling rand v0.3.14
|
||||
Compiling add-one v0.1.0 (file:///projects/adder/add-one)
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
@ -926,7 +926,7 @@ the `grep` tool for searching files called `ripgrep`. If we want to install
|
||||
$ cargo install ripgrep
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
Downloading ripgrep v0.3.2
|
||||
...snip...
|
||||
--snip--
|
||||
Compiling ripgrep v0.3.2
|
||||
Finished release [optimized + debuginfo] target(s) in 97.91 secs
|
||||
Installing ~/.cargo/bin/rg
|
||||
|
@ -1690,7 +1690,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn it_sends_an_over_75_percent_warning_message() {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
# let mock_messenger = MockMessenger::new();
|
||||
# let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
|
||||
# limit_tracker.set_value(75);
|
||||
|
@ -817,7 +817,7 @@ shown in Listing 16-11:
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let tx1 = mpsc::Sender::clone(&tx);
|
||||
@ -848,7 +848,7 @@ thread::spawn(move || {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
});
|
||||
// ...snip...
|
||||
// --snip--
|
||||
```
|
||||
|
||||
Listing 16-11: Sending multiple messages and pausing between each one
|
||||
|
@ -795,7 +795,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl Post {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn add_text(&mut self, text: &str) {
|
||||
self.content.push_str(text);
|
||||
}
|
||||
@ -828,7 +828,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl Post {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn content(&self) -> &str {
|
||||
""
|
||||
}
|
||||
@ -860,7 +860,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl Post {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn request_review(&mut self) {
|
||||
if let Some(s) = self.state.take() {
|
||||
self.state = Some(s.request_review())
|
||||
@ -943,7 +943,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl Post {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn approve(&mut self) {
|
||||
if let Some(s) = self.state.take() {
|
||||
self.state = Some(s.approve())
|
||||
@ -959,7 +959,7 @@ trait State {
|
||||
struct Draft {}
|
||||
|
||||
impl State for Draft {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
fn approve(self: Box<Self>) -> Box<State> {
|
||||
self
|
||||
}
|
||||
@ -968,7 +968,7 @@ impl State for Draft {
|
||||
struct PendingReview {}
|
||||
|
||||
impl State for PendingReview {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
fn approve(self: Box<Self>) -> Box<State> {
|
||||
Box::new(Published {})
|
||||
}
|
||||
@ -1007,11 +1007,11 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl Post {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn content(&self) -> &str {
|
||||
self.state.as_ref().unwrap().content(&self)
|
||||
}
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1047,17 +1047,17 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
trait State {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
struct Published {}
|
||||
|
||||
impl State for Published {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
||||
&post.content
|
||||
}
|
||||
@ -1237,7 +1237,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl DraftPost {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
pub fn request_review(self) -> PendingReviewPost {
|
||||
PendingReviewPost {
|
||||
|
@ -110,9 +110,9 @@ Raw pointers:
|
||||
|
||||
- Are allowed to ignore the borrowing rules and have both immutable and a
|
||||
mutable pointer or multiple mutable pointers to the same location
|
||||
- Aren't guaranteed to point to valid memory
|
||||
- Aren’t guaranteed to point to valid memory
|
||||
- Are allowed to be null
|
||||
- Don't implement any automatic clean-up
|
||||
- Don’t implement any automatic clean-up
|
||||
|
||||
Listing 19-1 shows how to create raw pointers from references:
|
||||
|
||||
@ -129,7 +129,7 @@ The `*const T` type is an immutable raw pointer, and `*mut T` is a mutable raw
|
||||
pointer. We’ve created raw pointers by using `as` to cast an immutable and a
|
||||
mutable reference into their corresponding raw pointer types. These particular
|
||||
raw pointers will be valid since we created them directly from references that
|
||||
are guaranteed to be valid, but we can't make that assumption about any raw
|
||||
are guaranteed to be valid, but we can’t make that assumption about any raw
|
||||
pointer.
|
||||
|
||||
Listing 19-2 shows how to create a raw pointer to an arbitrary location in
|
||||
@ -1032,7 +1032,7 @@ like Listing 19-23:
|
||||
|
||||
```
|
||||
fn distance<N, E, G: GGraph<N, E>>(graph: &G, start: &N, end: &N) -> u32 {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1051,7 +1051,7 @@ Contrast with the definition of `distance` in Listing 19-24 that uses the
|
||||
|
||||
```
|
||||
fn distance<G: AGraph>(graph: &G, start: &G::Node, end: &G::Node) -> u32 {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1072,7 +1072,7 @@ trait object:
|
||||
|
||||
```
|
||||
fn distance<N, E>(graph: &GGraph<N, E>, start: &N, end: &N) -> u32 {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1094,7 +1094,7 @@ for their `Edge` type:
|
||||
|
||||
```
|
||||
fn traverse(graph: &AGraph<Node=usize, Edge=(usize, usize)>) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1561,11 +1561,11 @@ like that in Listing 19-31:
|
||||
let f: Box<Fn() + Send + 'static> = Box::new(|| println!("hi"));
|
||||
|
||||
fn takes_long_type(f: Box<Fn() + Send + 'static>) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
fn returns_long_type() -> Box<Fn() + Send + 'static> {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1582,11 +1582,11 @@ type Thunk = Box<Fn() + Send + 'static>;
|
||||
let f: Thunk = Box::new(|| println!("hi"));
|
||||
|
||||
fn takes_long_type(f: Thunk) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
fn returns_long_type() -> Thunk {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1652,7 +1652,7 @@ function will never return. For example:
|
||||
|
||||
```
|
||||
fn bar() -> ! {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1795,7 +1795,7 @@ That is, a generic function definition like this:
|
||||
|
||||
```
|
||||
fn generic<T>(t: T) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1803,7 +1803,7 @@ is actually treated as if we had written this:
|
||||
|
||||
```
|
||||
fn generic<T: Sized>(t: T) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1813,7 +1813,7 @@ restriction:
|
||||
|
||||
```
|
||||
fn generic<T: ?Sized>(t: &T) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -391,7 +391,7 @@ Filename: src/main.rs
|
||||
```
|
||||
use std::fs::File;
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
fn handle_connection(mut stream: TcpStream) {
|
||||
let mut buffer = [0; 512];
|
||||
@ -442,7 +442,7 @@ add code to treat requests differently:
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
fn handle_connection(mut stream: TcpStream) {
|
||||
let mut buffer = [0; 512];
|
||||
@ -495,7 +495,7 @@ browser indicating as such to the end user:
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
} else {
|
||||
let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
|
||||
@ -554,10 +554,10 @@ shown in Listing 20-9:
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
fn handle_connection(mut stream: TcpStream) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
let (status_line, filename) = if buffer.starts_with(get) {
|
||||
("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
|
||||
@ -625,10 +625,10 @@ Filename: src/main.rs
|
||||
```
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
fn handle_connection(mut stream: TcpStream) {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
let get = b"GET / HTTP/1.1\r\n";
|
||||
let sleep = b"GET /sleep HTTP/1.1\r\n";
|
||||
@ -642,7 +642,7 @@ fn handle_connection(mut stream: TcpStream) {
|
||||
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
|
||||
};
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -922,7 +922,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
where
|
||||
@ -1003,7 +1003,7 @@ impl ThreadPool {
|
||||
ThreadPool
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1063,7 +1063,7 @@ pub struct ThreadPool {
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: u32) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
@ -1078,7 +1078,7 @@ impl ThreadPool {
|
||||
}
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1164,7 +1164,7 @@ pub struct ThreadPool {
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
@ -1178,7 +1178,7 @@ impl ThreadPool {
|
||||
workers
|
||||
}
|
||||
}
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
@ -1250,7 +1250,7 @@ hold anything for now:
|
||||
Filename: src/lib.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
use std::sync::mpsc;
|
||||
|
||||
pub struct ThreadPool {
|
||||
@ -1261,7 +1261,7 @@ pub struct ThreadPool {
|
||||
struct Job;
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
@ -1278,7 +1278,7 @@ impl ThreadPool {
|
||||
sender,
|
||||
}
|
||||
}
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
@ -1298,7 +1298,7 @@ Filename: src/lib.rs
|
||||
|
||||
```
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
@ -1315,10 +1315,10 @@ impl ThreadPool {
|
||||
sender,
|
||||
}
|
||||
}
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: mpsc::Receiver<Job>) -> Worker {
|
||||
@ -1382,10 +1382,10 @@ Filename: src/lib.rs
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
@ -1405,12 +1405,12 @@ impl ThreadPool {
|
||||
}
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -1433,12 +1433,12 @@ this is such a case! Take a look at Listing 20-19:
|
||||
Filename: src/lib.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
type Job = Box<FnOnce() + Send + 'static>;
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
where
|
||||
@ -1450,7 +1450,7 @@ impl ThreadPool {
|
||||
}
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
```
|
||||
|
||||
Listing 20-19: Creating a `Job` type alias for a `Box` that holds each closure,
|
||||
@ -1474,7 +1474,7 @@ change shown in Listing 20-20 to `Worker::new`:
|
||||
Filename: src/lib.rs
|
||||
|
||||
```
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||
@ -1575,7 +1575,7 @@ impl<F: FnOnce()> FnBox for F {
|
||||
|
||||
type Job = Box<FnBox + Send + 'static>;
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||
@ -1793,7 +1793,7 @@ Filename: src/lib.rs
|
||||
```
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
Worker {
|
||||
id,
|
||||
@ -1865,16 +1865,16 @@ pub struct ThreadPool {
|
||||
sender: mpsc::Sender<Message>,
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl ThreadPool {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
@ -1887,7 +1887,7 @@ impl ThreadPool {
|
||||
}
|
||||
}
|
||||
|
||||
// ...snip...
|
||||
// --snip--
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) ->
|
||||
|
@ -67,11 +67,11 @@
|
||||
|
||||
## Thinking in Rust
|
||||
|
||||
- [Functional Language Features in Rust](ch13-00-functional-features.md)
|
||||
- [Closures](ch13-01-closures.md)
|
||||
- [Iterators](ch13-02-iterators.md)
|
||||
- [Improving our I/O Project](ch13-03-improving-our-io-project.md)
|
||||
- [Performance](ch13-04-performance.md)
|
||||
- [Functional Language Features: Iterators and Closures](ch13-00-functional-features.md)
|
||||
- [Closures: Anonymous Functions that Can Capture Their Environment](ch13-01-closures.md)
|
||||
- [Processing a Series of Items with Iterators](ch13-02-iterators.md)
|
||||
- [Improving Our I/O Project](ch13-03-improving-our-io-project.md)
|
||||
- [Comparing Performance: Loops vs. Iterators](ch13-04-performance.md)
|
||||
|
||||
- [More about Cargo and Crates.io](ch14-00-more-about-cargo.md)
|
||||
- [Customizing Builds with Release Profiles](ch14-01-release-profiles.md)
|
||||
@ -123,8 +123,8 @@
|
||||
|
||||
- [Appendix](appendix-00.md)
|
||||
- [A - Keywords](appendix-01-keywords.md)
|
||||
- [B - Operators](appendix-02-operators.md)
|
||||
- [C - Derivable Traits]()
|
||||
- [D - Macros]()
|
||||
- [E - Translations]()
|
||||
- [F - Newest Features](appendix-07-newest-features.md)
|
||||
- [B - Operators and Symbols](appendix-02-operators.md)
|
||||
- [C - Derivable Traits](appendix-03-derivable-traits.md)
|
||||
- [D - Macros](appendix-04-macros.md)
|
||||
- [E - Translations](appendix-05-translation.md)
|
||||
- [F - Newest Features](appendix-06-newest-features.md)
|
||||
|
@ -17,7 +17,7 @@ or lifetimes.
|
||||
* `else` - fallback for `if` and `if let` control flow constructs
|
||||
* `enum` - defining an enumeration
|
||||
* `extern` - external crate, function, and variable linkage
|
||||
* `false` - boolean false literal
|
||||
* `false` - Boolean false literal
|
||||
* `fn` - function definition and function pointer type
|
||||
* `for` - iterator loop, part of trait impl syntax, and higher-ranked lifetime
|
||||
syntax
|
||||
@ -39,7 +39,7 @@ or lifetimes.
|
||||
* `struct` - structure definition
|
||||
* `super` - parent module of the current module
|
||||
* `trait` - trait definition
|
||||
* `true` - boolean true literal
|
||||
* `true` - Boolean true literal
|
||||
* `type` - type alias and associated type definition
|
||||
* `unsafe` - denotes unsafe code, functions, traits, and implementations
|
||||
* `use` - import symbols into scope
|
||||
|
@ -1,195 +1,154 @@
|
||||
## Appendix B: Operators
|
||||
## Appendix B: Operators and Symbols
|
||||
|
||||
### Unary operator expressions
|
||||
### Operators
|
||||
|
||||
Rust defines the following unary operators. They are all written as prefix
|
||||
operators, before the expression they apply to.
|
||||
The following lists the operators in Rust, an example of how the operator would
|
||||
appear in context, a short explanation, and whether that operator is
|
||||
overloadable. If an operator is overloadable, the relevant trait to use to
|
||||
overload that operator is listed.
|
||||
|
||||
* `-`
|
||||
: Negation. Signed integer types and floating-point types support negation. It
|
||||
is an error to apply negation to unsigned types; for example, the compiler
|
||||
rejects `-1u32`.
|
||||
* `*`
|
||||
: Dereference. When applied to a pointer, it denotes the pointed-to location.
|
||||
For pointers to mutable locations, the resulting value can be assigned to.
|
||||
On non-pointer types, it calls the `deref` method of the `std::ops::Deref`
|
||||
trait, or the `deref_mut` method of the `std::ops::DerefMut` trait (if
|
||||
implemented by the type and required for an outer expression that will or
|
||||
could mutate the dereference), and produces the result of dereferencing the
|
||||
`&` or `&mut` borrowed pointer returned from the overload method.
|
||||
* `!`
|
||||
: Logical negation. On the boolean type, this flips between `true` and
|
||||
`false`. On integer types, this inverts the individual bits in the
|
||||
two’s complement representation of the value.
|
||||
* `&` and `&mut`
|
||||
: Borrowing. When applied to a value, these operators produce a
|
||||
reference (pointer) to that value. The value is also placed into
|
||||
a borrowed state for the duration of the reference. For a shared
|
||||
borrow (`&`), this implies that the value may not be mutated, but
|
||||
it may be read or shared again. For a mutable borrow (`&mut`), the
|
||||
value may not be accessed in any way until the borrow expires.
|
||||
* `!` (`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...expr`) *in an expression*: inclusive range expression.
|
||||
* `...` (`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`).
|
||||
* `<=` (`var <= expr`): less-than or equal-to comparison. Overloadable (`PartialOrd`).
|
||||
* `=` (`var = expr`, `ident = type`): assignment/equivalence.
|
||||
* `==` (`var == expr`): equality comparison. Overloadable (`PartialEq`).
|
||||
* `=>` (`pat => expr`): part of match arm syntax.
|
||||
* `>` (`expr > expr`): greater-than comparison. Overloadable (`PartialOrd`).
|
||||
* `>=` (`var >= 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.
|
||||
|
||||
### Binary operator expressions
|
||||
### Non-operator Symbols
|
||||
|
||||
Binary operators expressions are given in order of operator precedence.
|
||||
#### Standalone Syntax
|
||||
|
||||
#### Arithmetic operators
|
||||
* `'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.
|
||||
|
||||
Binary arithmetic expressions are syntactic sugar for calls to built-in traits,
|
||||
defined in the `std::ops` module of the `std` library. This means arithmetic
|
||||
operators can be overridden for user-defined types. The default meaning of the
|
||||
operators on standard types is given here.
|
||||
#### Path-related Syntax
|
||||
|
||||
* `+`
|
||||
: Addition and array/string concatenation.
|
||||
Calls the `add` method on the `std::ops::Add` trait.
|
||||
* `-`
|
||||
: Subtraction.
|
||||
Calls the `sub` method on the `std::ops::Sub` trait.
|
||||
* `*`
|
||||
: Multiplication.
|
||||
Calls the `mul` method on the `std::ops::Mul` trait.
|
||||
* `/`
|
||||
: Quotient.
|
||||
Calls the `div` method on the `std::ops::Div` trait.
|
||||
* `%`
|
||||
: Remainder.
|
||||
Calls the `rem` method on the `std::ops::Rem` trait.
|
||||
* `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.
|
||||
|
||||
Note that Rust does not have a built-in operator for exponential (power)
|
||||
calculation; see the `pow` method on the numeric types.
|
||||
#### Generics
|
||||
|
||||
#### Bitwise operators
|
||||
* `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.
|
||||
|
||||
Like the arithmetic operators, bitwise operators are syntactic sugar for calls
|
||||
to methods of built-in traits. This means bitwise operators can be overridden
|
||||
for user-defined types. The default meaning of the operators on standard types
|
||||
is given here. Bitwise `&`, `|` and `^` applied to boolean arguments are
|
||||
equivalent to logical `&&`, `||` and `!=` evaluated in non-lazy fashion.
|
||||
#### Trait Bound Constraints
|
||||
|
||||
* `&`
|
||||
: Bitwise AND.
|
||||
Calls the `bitand` method of the `std::ops::BitAnd` trait.
|
||||
* `|`
|
||||
: Bitwise inclusive OR.
|
||||
Calls the `bitor` method of the `std::ops::BitOr` trait.
|
||||
* `^`
|
||||
: Bitwise exclusive OR.
|
||||
Calls the `bitxor` method of the `std::ops::BitXor` trait.
|
||||
* `<<`
|
||||
: Left shift.
|
||||
Calls the `shl` method of the `std::ops::Shl` trait.
|
||||
* `>>`
|
||||
: Right shift (arithmetic).
|
||||
Calls the `shr` method of the `std::ops::Shr` trait.
|
||||
* `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.
|
||||
|
||||
#### Lazy boolean operators
|
||||
#### Macros and Attributes
|
||||
|
||||
The operators `||` and `&&` may be applied to operands of boolean type. The
|
||||
`||` operator denotes logical ‘or’, and the `&&` operator denotes logical
|
||||
‘and’. They differ from `|` and `&` in that the right-hand operand is only
|
||||
evaluated when the left-hand operand does not already determine the result of
|
||||
the expression. That is, `||` only evaluates its right-hand operand when the
|
||||
left-hand operand evaluates to `false`, and `&&` only when it evaluates to
|
||||
`true`.
|
||||
* `#[meta]`: outer attribute.
|
||||
* `#![meta]`: inner attribute.
|
||||
* `$ident`: macro substitution.
|
||||
* `$ident:kind`: macro capture.
|
||||
* `$(…)…`: macro repetition.
|
||||
|
||||
#### Comparison operators
|
||||
#### Comments
|
||||
|
||||
Comparison operators are, like the arithmetic operators and bitwise operators,
|
||||
syntactic sugar for calls to built-in traits. This means that comparison
|
||||
operators can be overridden for user-defined types. The default meaning of the
|
||||
operators on standard types is given here.
|
||||
* `//`: line comment.
|
||||
* `//!`: inner line doc comment.
|
||||
* `///`: outer line doc comment.
|
||||
* `/*…*/`: block comment.
|
||||
* `/*!…*/`: inner block doc comment.
|
||||
* `/**…*/`: outer block doc comment.
|
||||
|
||||
* `==`
|
||||
: Equal to.
|
||||
Calls the `eq` method on the `std::cmp::PartialEq` trait.
|
||||
* `!=`
|
||||
: Unequal to.
|
||||
Calls the `ne` method on the `std::cmp::PartialEq` trait.
|
||||
* `<`
|
||||
: Less than.
|
||||
Calls the `lt` method on the `std::cmp::PartialOrd` trait.
|
||||
* `>`
|
||||
: Greater than.
|
||||
Calls the `gt` method on the `std::cmp::PartialOrd` trait.
|
||||
* `<=`
|
||||
: Less than or equal.
|
||||
Calls the `le` method on the `std::cmp::PartialOrd` trait.
|
||||
* `>=`
|
||||
: Greater than or equal.
|
||||
Calls the `ge` method on the `std::cmp::PartialOrd` trait.
|
||||
#### Tuples
|
||||
|
||||
#### Type cast expressions
|
||||
* `()`: 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.
|
||||
|
||||
A type cast expression is denoted with the binary operator `as`.
|
||||
#### Curly Brackets
|
||||
|
||||
Executing an `as` expression casts the value on the left-hand side to the type
|
||||
on the right-hand side.
|
||||
* `{…}`: block expression.
|
||||
* `Type {…}`: `struct` literal.
|
||||
|
||||
An example of an `as` expression:
|
||||
#### Square Brackets
|
||||
|
||||
```rust
|
||||
# fn sum(values: &[f64]) -> f64 { 0.0 }
|
||||
# fn len(values: &[f64]) -> i32 { 0 }
|
||||
|
||||
fn average(values: &[f64]) -> f64 {
|
||||
let sum: f64 = sum(values);
|
||||
let size: f64 = len(values) as f64;
|
||||
sum / size
|
||||
}
|
||||
```
|
||||
|
||||
Some of the conversions which can be done through the `as` operator
|
||||
can also be done implicitly at various points in the program, such as
|
||||
argument passing and assignment to a `let` binding with an explicit
|
||||
type. Implicit conversions are limited to “harmless” conversions that
|
||||
do not lose information and which have minimal or no risk of
|
||||
surprising side-effects on the dynamic execution semantics.
|
||||
|
||||
#### Assignment expressions
|
||||
|
||||
An *assignment expression* consists of a pattern followed by an equals
|
||||
sign (`=`) and an expression.
|
||||
|
||||
Evaluating an assignment expression either copies or
|
||||
moves its right-hand operand to its left-hand
|
||||
operand.
|
||||
|
||||
```
|
||||
# let mut x = 0;
|
||||
# let y = 0;
|
||||
x = y;
|
||||
```
|
||||
|
||||
#### Compound assignment expressions
|
||||
|
||||
The `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>` operators may be
|
||||
composed with the `=` operator. The expression `lval OP= val` is equivalent to
|
||||
`lval = lval OP val`. For example, `x = x + 1` may be written as `x += 1`.
|
||||
|
||||
Any such expression always has the `unit` type.
|
||||
|
||||
#### Operator precedence
|
||||
|
||||
The precedence of Rust operators is ordered as follows, going from strong to
|
||||
weak. Binary Operators at the same precedence level are evaluated in the order
|
||||
given by their associativity.
|
||||
|
||||
|
||||
| Operator | Associativity |
|
||||
|-----------------------------|---------------------|
|
||||
| `?` | |
|
||||
| Unary `-` `*` `!` `&` `&mut` | |
|
||||
| `as` `:` | left to right |
|
||||
| `*` `/` `%` | left to right |
|
||||
| `+` `-` | left to right |
|
||||
| `<<` `>>` | left to right |
|
||||
| `&` | left to right |
|
||||
| `^` | left to right |
|
||||
| <code>|</code> | left to right |
|
||||
| `==` `!=` `<` `>` `<=` `>=` | Require parentheses |
|
||||
| `&&` | left to right |
|
||||
| <code>||</code> | left to right |
|
||||
| `..` `...` | Require parentheses |
|
||||
| `<-` | right to left |
|
||||
| `=` `+=` `-=` `*=` `/=` `%=` <br> `&=` <code>|=</code> `^=` `<<=` `>>=` | right to left |
|
||||
* `[…]`: 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”.
|
||||
|
212
src/doc/book/second-edition/src/appendix-03-derivable-traits.md
Normal file
212
src/doc/book/second-edition/src/appendix-03-derivable-traits.md
Normal file
@ -0,0 +1,212 @@
|
||||
# C - Derivable Traits
|
||||
|
||||
In various places in the book, we discussed the `derive` attribute that is
|
||||
applied to a struct or enum. This attribute generates code that implements a
|
||||
trait on the annotated type with a default implementation. In this example, the
|
||||
`#[derive(Debug)]` attribute implements the `Debug` trait for the `Point`
|
||||
struct:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
```
|
||||
|
||||
The code that the compiler generates for the implementation of `Debug` is
|
||||
similar to this code:
|
||||
|
||||
```rust
|
||||
# struct Point {
|
||||
# x: i32,
|
||||
# y: i32,
|
||||
# }
|
||||
#
|
||||
impl ::std::fmt::Debug for Point {
|
||||
fn fmt(&self, __arg_0: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
match *self {
|
||||
Point { x: ref __self_0_0, y: ref __self_0_1 } => {
|
||||
let mut builder = __arg_0.debug_struct("Point");
|
||||
let _ = builder.field("x", &&(*__self_0_0));
|
||||
let _ = builder.field("y", &&(*__self_0_1));
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The generated code implements sensible default behavior for the `Debug` trait’s
|
||||
`fmt` function: a `match` expression destructures a `Point` instance into its
|
||||
field values. Then it builds up a string containing the struct’s name and each
|
||||
field’s name and value. This means we’re able to use debug formatting on a
|
||||
`Point` instance to see what value each field has.
|
||||
|
||||
The generated code isn’t particularly easy to read because it’s only for the
|
||||
compiler to consume, rather than for programmers to read! The `derive`
|
||||
attribute and the default implementation of `Debug` has saved us all of the
|
||||
work of writing this code for every struct or enum that we want to be able to
|
||||
print using debug formatting.
|
||||
|
||||
The `derive` attribute has default implementations for the following traits
|
||||
provided by the standard library. If you want different behavior than what the
|
||||
`derive` attribute provides, consult the standard library documentation for
|
||||
each trait for the details needed for manual implementation of the traits.
|
||||
|
||||
## Standard Library Traits that Can Be Derived
|
||||
|
||||
The following sections list 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
|
||||
|
||||
### `Debug` for Programmer Output
|
||||
|
||||
The `Debug` trait enables debug formatting in format strings, indicated by
|
||||
adding `:?` within `{}` placeholders.
|
||||
|
||||
The `Debug` trait signifies that instances of a type may be printed by
|
||||
programmers in order to debug their programs by inspecting an instance of a
|
||||
type at a particular point in a program’s execution.
|
||||
|
||||
An example of when `Debug` is required is the `assert_eq!` macro, which prints
|
||||
the values of the instances given as arguments if the equality assertion fails
|
||||
so that programmers can see why the two instances weren’t equal.
|
||||
|
||||
### `PartialEq` and `Eq` for Equality Comparisons
|
||||
|
||||
The `PartialEq` trait signifies that instances of a type can be compared to
|
||||
each other for equality, and enables use of the `==` and `!=` operators.
|
||||
|
||||
Deriving `PartialEq` implements the `eq` method. When derived on structs, two
|
||||
instances are equal 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.
|
||||
|
||||
An example of when `PartialEq` is required is the `assert_eq!` macro, which
|
||||
needs to be able to compare two instances of a type for equality.
|
||||
|
||||
The `Eq` trait doesn’t have any methods. It only signals 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`. An example of types that
|
||||
implements `PartialEq` but that cannot implement `Eq` are floating point number
|
||||
types: the implementation of floating point numbers says that two instances of
|
||||
the not-a-number value, `NaN`, are not equal to each other.
|
||||
|
||||
An example of when `Eq` is required is for keys in a `HashMap` so that the
|
||||
`HashMap` can tell whether two keys are the same.
|
||||
|
||||
### `PartialOrd` and `Ord` for Ordering Comparisons
|
||||
|
||||
The `PartialOrd` trait signifies that instances of a type can be compared to
|
||||
each other to see which is larger than the other for sorting purposes. A type
|
||||
that implements `PartialOrd` may be used with the `<`, `>`, `<=`, and `>=`
|
||||
operators. The `PartialOrd` trait can only be applied to types that also
|
||||
implement `PartialEq`.
|
||||
|
||||
Deriving `PartialOrd` implements the `partial_cmp` method, which returns an
|
||||
`Option<Ordering>` that may be `None` if comparing the given values does not
|
||||
produce an ordering. When derived on structs, two instances of the struct are
|
||||
compared 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 greater than the variants listed
|
||||
later.
|
||||
|
||||
An example of when `PartialOrd` is required is the `gen_range` method in the
|
||||
`rand` crate that generates a random value in the range specified by a low
|
||||
value and a high value.
|
||||
|
||||
The `Ord` trait signifies that for any two value of the annotated type, a valid
|
||||
ordering exists. The `Ord` trait implements the `cmp` method, which returns an
|
||||
`Ordering` rather than an `Option<Ordering>` because a valid ordering will
|
||||
always be possible. The `Ord` trait can only be applied to types that also
|
||||
implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When derived
|
||||
on structs and enums, `cmp` behaves the same way as the derived implementation
|
||||
for `partial_cmp` does with `PartialOrd`.
|
||||
|
||||
An example of when `Ord` is required is when storing values in a `BTreeSet<T>`,
|
||||
a data structure that stores data based on the sort order of the values.
|
||||
|
||||
### `Clone` and `Copy` for Duplicating Values
|
||||
|
||||
The `Clone` trait signifies there is a way to explicitly create a duplicate of
|
||||
a value, and the duplication process might involve running arbitrary code.
|
||||
Deriving `Clone` implements the `clone` method. When derived, the
|
||||
implementation of `clone` for the whole type calls `clone` on each of the parts
|
||||
of the type, so all of the fields or values in the type must also implement
|
||||
`Clone` to derive `Clone`.
|
||||
|
||||
An example of when `Clone` is required is when calling the `to_vec` method on a
|
||||
slice containing instances of some type. The slice doesn’t own the instances
|
||||
but the vector returned from `to_vec` will need to own its instances, so the
|
||||
implementation of `to_vec` calls `clone` on each item. Thus, the type stored in
|
||||
the slice must implement `Clone`.
|
||||
|
||||
The `Copy` trait signifies that a value can be duplicated by only copying bits;
|
||||
no other code is necessary. 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. You can derive `Copy` on any type whose
|
||||
parts all implement `Copy`. The `Copy` trait can only be applied to types that
|
||||
also implement `Clone`, as a type that implements `Copy` has a trivial
|
||||
implementation of `Clone`, doing the same thing as `Copy`.
|
||||
|
||||
`Copy` is rarely required; when types implement `Copy`, there are optimizations
|
||||
that can be applied and the code becomes nicer because you don’t have to call
|
||||
`clone`. Everything possible with `Copy` can also be accomplished with `Clone`,
|
||||
but the code might be slower or have to use `clone` in places.
|
||||
|
||||
### `Hash` for Mapping a Value to a Value of Fixed Size
|
||||
|
||||
The `Hash` trait signifies there is a way to take an instance of a type that
|
||||
takes up an arbitrary amount of size and map that instance to a value of fixed
|
||||
size by using a hash function. Deriving `Hash` implements the `hash` method.
|
||||
When derived, the implementation of `hash` for the whole type combines the
|
||||
result of calling `hash` on each of the parts of the type, so all of the fields
|
||||
or values in the type must also implement `Hash` to derive `Hash`.
|
||||
|
||||
An example of when `Hash` is required is for keys in a `HashMap` so that the
|
||||
`HashMap` can store data efficiently.
|
||||
|
||||
### `Default` for Default Values
|
||||
|
||||
The `Default` trait signifies there is a way to create a default value for a
|
||||
type. Deriving `Default` implements the `default` method. When derived, the
|
||||
implementation of `Default` for the whole type calls the `default` method on
|
||||
each of the parts of the type, so all of the fields or values in the type must
|
||||
also implement `Default` to derive `Default.`
|
||||
|
||||
A common use of `Default::default` is 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 use the default values for the rest by using `..Default::default()`.
|
||||
|
||||
An example of when `Default` is required is 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>`.
|
||||
|
||||
## Standard Library Traits that Can’t Be Derived
|
||||
|
||||
The rest of the traits defined in the standard library can’t be implemented on
|
||||
your types using `derive`. These traits don’t have a sensible default behavior
|
||||
they could have, so you are required to implement them in the way that makes
|
||||
sense for what you are trying to accomplish with your code.
|
||||
|
||||
An example of a trait that can’t be derived is `Display`, which handles
|
||||
formatting of a type for end users of your programs. You should 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 into your application, so you must provide it.
|
||||
|
||||
## Making Custom Traits Derivable
|
||||
|
||||
The above list is not comprehensive, however: libraries can implement `derive`
|
||||
for their own types! 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 the next appendix, “Macros.”
|
484
src/doc/book/second-edition/src/appendix-04-macros.md
Normal file
484
src/doc/book/second-edition/src/appendix-04-macros.md
Normal file
@ -0,0 +1,484 @@
|
||||
# D - Macros
|
||||
|
||||
We’ve used macros, such as `println!`, throughout this book. This appendix will
|
||||
explain:
|
||||
|
||||
- What macros are and how they differ from functions
|
||||
- How to define a declarative macro to do metaprogramming
|
||||
- How to define a procedural macro to create custom `derive` traits
|
||||
|
||||
Macros are covered in an appendix because they’re still evolving. They have
|
||||
changed and will change more than the rest of the language and standard library
|
||||
since Rust 1.0, so this section will likely get out of date more than the rest
|
||||
of this book. The code shown here will still continue to work due to Rust’s
|
||||
stability guarantees, but there may be additional capabilities or easier ways
|
||||
to write macros that aren’t available at the time of this publication.
|
||||
|
||||
## Macros are More Flexible and Complex than Functions
|
||||
|
||||
Fundamentally, macros are a way of writing code that writes other code, which
|
||||
is known as *metaprogramming*. In the previous appendix, we discussed the
|
||||
`derive` attribute, which generates an implementation of various traits for
|
||||
you. We’ve also used the `println!` and `vec!` macros. All of these macros
|
||||
*expand* to produce more code than what you’ve written in your source code.
|
||||
|
||||
Metaprogramming is useful to reduce 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, as we discussed in Chapter 1.
|
||||
A function signature has to declare the number and type of parameters the
|
||||
function has. Macros can take a variable number of parameters: we can call
|
||||
`println!("hello")` with one argument, or `println!("hello {}", name)` with two
|
||||
arguments. Also, macros are expanded before the compiler interprets the meaning
|
||||
of the code, so a macro can, for example, implement a trait on a given type,
|
||||
whereas a function can’t because a function gets called at runtime and a trait
|
||||
needs to be implemented at compile time.
|
||||
|
||||
The downside to implementing a macro rather than a function is that macro
|
||||
definitions are more complex than function definitions. You’re writing Rust
|
||||
code that writes Rust code, and 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 a crate, when bringing an external
|
||||
crate into the scope of your project, you have to explicitly bring the macros
|
||||
into the scope of your project as well with the `#[macro_use]` annotation. This
|
||||
example would bring all the macros defined in the `serde` crate into the scope
|
||||
of the current crate:
|
||||
|
||||
```rust,ignore
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
```
|
||||
|
||||
If `extern crate` also brought macros into scope by default, you wouldn’t be
|
||||
allowed to use two crates that happened to define macros with the same name. In
|
||||
practice this conflict doesn’t come up much, but the more crates you use, the
|
||||
more likely it is.
|
||||
|
||||
One last important difference between macros and functions: macros must be
|
||||
defined or brought into scope before they’re called in a file. Unlike
|
||||
functions, where we can define a function at the bottom of a file yet call it
|
||||
at the top, we always have to define macros before we’re able to call them.
|
||||
|
||||
## Declarative Macros with `macro_rules!` for General Metaprogramming
|
||||
|
||||
The first form of macros in Rust, and the one that’s most widely used, is
|
||||
called *declarative macros*. These are also sometimes referred to as *macros by
|
||||
example*, *`macro_rules!` macros*, or just plain *macros*. At their core,
|
||||
declarative macros allow you to write something similar to a Rust `match`
|
||||
expression. As discussed in Chapter 6, `match` expressions are control
|
||||
structures that take an expression, compare the resulting value of the
|
||||
expression to patterns, and then choose the code specified with the matching
|
||||
pattern when the program runs. Macros also have a value that is compared to
|
||||
patterns that have code associated with them, but the value is the literal Rust
|
||||
code passed to the macro, the patterns match the structure of that source code,
|
||||
and the code associated with each pattern is the code that is generated to
|
||||
replace 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 that holds
|
||||
particular values. For example, this macro creates a new vector with three
|
||||
integers inside:
|
||||
|
||||
```rust
|
||||
let v: Vec<u32> = vec![1, 2, 3];
|
||||
```
|
||||
|
||||
We can also use `vec!` to make a vector of two integers or a vector of five
|
||||
string slices. Because we don’t know the number or type of values, we can’t
|
||||
define a function that is able to create a new vector with the given elements
|
||||
like `vec!` can.
|
||||
|
||||
Let’s take a look at a slightly simplified definition of the `vec!` macro:
|
||||
|
||||
```rust
|
||||
#[macro_export]
|
||||
macro_rules! vec {
|
||||
( $( $x:expr ),* ) => {
|
||||
{
|
||||
let mut temp_vec = Vec::new();
|
||||
$(
|
||||
temp_vec.push($x);
|
||||
)*
|
||||
temp_vec
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
> Note: the actual definition of the `vec!` macro in the standard library also
|
||||
> has code to pre-allocate the correct amount of memory up-front. That code
|
||||
> is an optimization that we’ve chosen not to include here for simplicity.
|
||||
|
||||
The `#[macro_export]` annotation indicates that this macro should be made
|
||||
available when other crates import the crate in which we’re defining this
|
||||
macro. Without this annotation, even if someone depending on this crate uses
|
||||
the `#[macro_use]` annotation, this macro would not be brought into scope.
|
||||
|
||||
Macro definitions start with `macro_rules!` and the name of the macro we’re
|
||||
defining without the exclamation mark, which in this case is `vec`. This is
|
||||
followed by curly brackets denoting the body of the macro definition.
|
||||
|
||||
Inside the body is a structure similar to the structure of a `match`
|
||||
expression. This macro definition has one arm with the pattern `( $( $x:expr
|
||||
),* )`, followed by `=>` and the block of code associated with this pattern. If
|
||||
this pattern matches, then the block of code will be emitted. Given that this
|
||||
is the only pattern in this macro, there’s only one valid way to match; any
|
||||
other will be an error. More complex macros will have more than one arm.
|
||||
|
||||
The pattern syntax valid in macro definitions is different than the pattern
|
||||
syntax covered in Chapter 18 because the patterns are for matching against Rust
|
||||
code structure rather than values. Let’s walk through what the pieces of the
|
||||
pattern used here mean; for the full macro pattern syntax, see [the reference].
|
||||
|
||||
[the reference]: ../../reference/macros.html
|
||||
|
||||
The `$x:expr` part of the pattern matches any Rust expression and gives the
|
||||
expression the name `$x`. The `*` specifies that the pattern matches zero or
|
||||
more of whatever precedes the `*`. In this case, `*` is preceded by `$(),` so
|
||||
this pattern matches zero or more of whatever is inside the parentheses,
|
||||
delimited by a comma. When we call this macro with `vec![1, 2, 3];`, the
|
||||
pattern matches the three expressions `1`, `2`, and `3`.
|
||||
|
||||
In the body of the code associated with this arm, 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` in the code associated with the
|
||||
arm 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:
|
||||
|
||||
```rust,ignore
|
||||
let mut temp_vec = Vec::new();
|
||||
temp_vec.push(1);
|
||||
temp_vec.push(2);
|
||||
temp_vec.push(3);
|
||||
temp_vec
|
||||
```
|
||||
|
||||
We’ve defined a macro that can take any number of arguments of any type and can
|
||||
generate code to create a vector containing the specified elements.
|
||||
|
||||
Given that most Rust programmers will *use* macros more than *write* macros,
|
||||
that’s all we’ll discuss about `macro_rules!` in this book. To learn more about
|
||||
how to write macros, consult the online documentation or other resources such
|
||||
as [The Little Book of Rust Macros][tlborm].
|
||||
|
||||
[tlborm]: https://danielkeep.github.io/tlborm/book/index.html
|
||||
|
||||
## Procedural Macros for Custom `derive`
|
||||
|
||||
The second form of macros is called *procedural macros* because they’re more
|
||||
like functions (which are a type of procedure). Procedural macros accept some
|
||||
Rust code as an input, operate on that code, and produce some Rust code as an
|
||||
output, rather than matching against patterns and replacing the code with other
|
||||
code as declarative macros do. Today, the only thing you can define procedural
|
||||
macros for is to allow your traits to be implemented on a type by specifying
|
||||
the trait name in a `derive` annotation.
|
||||
|
||||
Let’s create a crate named `hello-world` that defines a trait named
|
||||
`HelloWorld` with one associated function named `hello_world`. Rather than
|
||||
making users of our crate implement the `HelloWorld` trait for each of their
|
||||
types, we’d like users to be able to annotate their type with
|
||||
`#[derive(HelloWorld)]` to get a default implementation of the `hello_world`
|
||||
function associated with their type. The default implementation will print
|
||||
`Hello world, 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 that looks like Listing A4-1 using our crate:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
extern crate hello_world;
|
||||
#[macro_use]
|
||||
extern crate hello_world_derive;
|
||||
|
||||
use hello_world::HelloWorld;
|
||||
|
||||
#[derive(HelloWorld)]
|
||||
struct Pancakes;
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing A4-1: The code a user of our crate will be able
|
||||
to write when we’ve written the procedural macro</span>
|
||||
|
||||
This code will print `Hello world, my name is Pancakes!` when we’re done. Let’s
|
||||
get started!
|
||||
|
||||
Let’s make a new library crate:
|
||||
|
||||
```text
|
||||
$ cargo new hello-world
|
||||
```
|
||||
|
||||
First, we’ll define the `HelloWorld` trait and associated function:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
pub trait HelloWorld {
|
||||
fn hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
At this point, a user of our crate could implement the trait themselves to
|
||||
achieve the functionality we wanted to enable, like so:
|
||||
|
||||
```rust,ignore
|
||||
extern crate hello_world;
|
||||
|
||||
use hello_world::HelloWorld;
|
||||
|
||||
struct Pancakes;
|
||||
|
||||
impl HelloWorld for Pancakes {
|
||||
fn hello_world() {
|
||||
println!("Hello world, my name is Pancakes!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Pancakes::hello_world();
|
||||
}
|
||||
```
|
||||
|
||||
However, they would need to write out the implementation block for each type
|
||||
they wanted to be able to use with `hello_world`; we’d like to make using our
|
||||
trait more convenient for other programmers by saving them this work.
|
||||
|
||||
Additionally, we can’t provide a default implementation for the `hello_world`
|
||||
function that has the behavior we want of printing out the name of the type the
|
||||
trait is implemented on: Rust doesn’t have reflection capabilities, so we 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
|
||||
|
||||
The next step is to define the procedural macro. At the moment, procedural
|
||||
macros need to be in their own crate. Eventually, this restriction may be
|
||||
lifted, but for now, it’s required. As such, there’s a convention: for a crate
|
||||
named `foo`, a custom derive procedural macro crate is called `foo-derive`.
|
||||
Let’s start a new crate called `hello-world-derive` inside our `hello-world`
|
||||
project:
|
||||
|
||||
```text
|
||||
$ cargo new hello-world-derive
|
||||
```
|
||||
|
||||
We’ve chosen to create the procedural macro crate within the directory of our
|
||||
`hello-world` crate because the two crates are tightly related: if we change
|
||||
the trait definition in `hello-world`, we’ll have to change the implementation
|
||||
of the procedural macro in `hello-world-derive` as well. The two crates will
|
||||
need to be published separately, and programmers using these crates will need
|
||||
to add both as dependencies and bring them both into scope. It’s possible to
|
||||
have the `hello-world` crate use `hello-world-derive` as a dependency and
|
||||
re-export the procedural macro code, but structuring the project this way makes
|
||||
it possible for programmers to easily decide they only want to use
|
||||
`hello-world` if they don’t want the `derive` functionality.
|
||||
|
||||
We need to declare that the `hello-world-derive` crate is a procedural macro
|
||||
crate. We also need to add dependencies on the `syn` and `quote` crates to get
|
||||
useful functionality for operating on Rust code. To do these two things, add
|
||||
the following to the *Cargo.toml* for `hello-world-derive`:
|
||||
|
||||
<span class="filename">Filename: hello-world-derive/Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.11.11"
|
||||
quote = "0.3.15"
|
||||
```
|
||||
|
||||
To start defining the procedural macro, place the code from Listing A4-2 in
|
||||
*src/lib.rs* for the `hello-world-derive` crate. Note that this won’t compile
|
||||
until we add a definition for the `impl_hello_world` function. We’ve split the
|
||||
code into functions in this way because the code in Listing A4-2 will be the
|
||||
same for almost every procedural macro crate; it’s code that makes writing a
|
||||
procedural macro more convenient. What you choose to do in the place where the
|
||||
`impl_hello_world` function is called will be different and depend on the
|
||||
purpose of your procedural macro.
|
||||
|
||||
<span class="filename">Filename: hello-world-derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_derive(HelloWorld)]
|
||||
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let s = input.to_string();
|
||||
|
||||
// Parse the string representation
|
||||
let ast = syn::parse_derive_input(&s).unwrap();
|
||||
|
||||
// Build the impl
|
||||
let gen = impl_hello_world(&ast);
|
||||
|
||||
// Return the generated impl
|
||||
gen.parse().unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing A4-2: Code that most procedural macro crates will
|
||||
need to have for processing Rust code</span>
|
||||
|
||||
We have introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The
|
||||
`proc_macro` crate comes with Rust, so we didn’t need to add that to the
|
||||
dependencies in *Cargo.toml*. The `proc_macro` crate allows us to convert Rust
|
||||
code into a string containing that Rust code. The `syn` crate parses Rust code
|
||||
from a string into a data structure that we can perform operations on. The
|
||||
`quote` crate takes `syn` data structures and turns them back into Rust code.
|
||||
These crates make it much simpler to parse any sort of Rust code we might want
|
||||
to handle: writing a full parser for Rust code is no simple task.
|
||||
|
||||
[`syn`]: https://crates.io/crates/syn
|
||||
[`quote`]: https://crates.io/crates/quote
|
||||
|
||||
The `hello_world_derive` function is the code that will get called when a user
|
||||
of our library specifies the `#[derive(HelloWorld)]` annotation on a type
|
||||
because we’ve annotated the `hello_world_derive` function here with
|
||||
`proc_macro_derive` and specified the same name, `HelloWorld`. This name
|
||||
matches our trait named `HelloWorld`; that’s the convention most procedural
|
||||
macros follow.
|
||||
|
||||
The first thing this function does is convert 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 `HelloWorld`. In the example in
|
||||
Listing A4-1, `s` will have the `String` value `struct Pancakes;` because
|
||||
that’s the Rust code we added the `#[derive(HelloWorld)]` annotation to.
|
||||
|
||||
At the moment, the only thing you can do with a `TokenStream` is convert it to
|
||||
a string. A richer API will exist in the future.
|
||||
|
||||
What we really need is 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.
|
||||
Here’s the relevant parts of the `DeriveInput` struct we get from parsing the
|
||||
string `struct Pancakes;`:
|
||||
|
||||
```rust,ignore
|
||||
DeriveInput {
|
||||
// --snip--
|
||||
|
||||
ident: Ident(
|
||||
"Pancakes"
|
||||
),
|
||||
body: Struct(
|
||||
Unit
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The fields of this struct show that the Rust code we’ve parsed is a unit struct
|
||||
with the `ident` (identifier, meaning the name) of `Pancakes`. There are more
|
||||
fields on this struct for describing all sorts of Rust code; check the [`syn`
|
||||
API docs for `DeriveInput`][syn-docs] for more information.
|
||||
|
||||
[syn-docs]: https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html
|
||||
|
||||
We haven’t defined the `impl_hello_world` function; that’s where we’ll build
|
||||
the new Rust code we want to include. Before we get to that, the last part of
|
||||
this `hello_world_derive` function is using the `quote` crate’s `parse`
|
||||
function to turn the output of the `impl_hello_world` function back into a
|
||||
`TokenStream`. The returned `TokenStream` is added to the code that users of
|
||||
our crate write so that when they compile their crate, they get extra
|
||||
functionality we provide.
|
||||
|
||||
You may have noticed that we’re calling `unwrap` to panic if the calls to the
|
||||
`parse_derive_input` or `parse` functions fail because they’re unable to parse
|
||||
the `TokenStream` or generate a `TokenStream`. Panicking on errors is necessary
|
||||
in procedural macro code because `proc_macro_derive` functions must return
|
||||
`TokenStream` rather than `Result` in order to conform to the procedural macro
|
||||
API. We’ve chosen to keep this example simple by using `unwrap`; in production
|
||||
code you should provide more specific error messages about what went wrong by
|
||||
using `expect` or `panic!`.
|
||||
|
||||
Now that we have the code to turn the annotated Rust code from a `TokenStream`
|
||||
into a `String` and into a `DeriveInput` instance, let’s write the code that
|
||||
will generate the code implementing the `HelloWorld` trait on the annotated
|
||||
type:
|
||||
|
||||
<span class="filename">Filename: hello-world-derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let name = &ast.ident;
|
||||
quote! {
|
||||
impl HelloWorld for #name {
|
||||
fn hello_world() {
|
||||
println!("Hello, World! My name is {}", stringify!(#name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We are able to get an `Ident` struct instance containing the name (identifier)
|
||||
of the annotated type using `ast.ident`. With the code from Listing A4-1,
|
||||
`name` will be `Ident("Pancakes")`.
|
||||
|
||||
The `quote!` macro from the `quote` crate lets us write up the Rust code that
|
||||
we wish to return and convert it into `quote::Tokens`. The `quote!` macro lets
|
||||
us use some really cool templating mechanics; we can write `#name` and `quote!`
|
||||
will replace it with the value in the variable named `name`. You can even do
|
||||
some repetition similar to the way regular macros work. Check out [the `quote`
|
||||
crate’s docs][quote-docs] for a thorough introduction.
|
||||
|
||||
[quote-docs]: https://docs.rs/quote
|
||||
|
||||
What we want to do for our procedural macro is generate an implementation of
|
||||
our `HelloWorld` trait for the type the user of our crate has annotated, which
|
||||
we can get by using `#name`. The trait implementation has one function,
|
||||
`hello_world`, and the function body contains the functionality we want to
|
||||
provide: printing `Hello, World! My name is` and then the name of the type the
|
||||
user of our crate has annotated. 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 `#name` would be an expression that
|
||||
we would want to print out literally, and `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-world`
|
||||
and `hello-world-derive`. Let’s hook these crates up to the code in Listing
|
||||
A4-1 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-world`
|
||||
and `hello-world-derive` as dependencies in the `pancakes` crate’s
|
||||
*Cargo.toml*. If you’ve chosen to publish your versions of `hello-world` and
|
||||
`hello-world-derive` to *https://crates.io* they would be regular dependencies;
|
||||
if not, you can specify them as `path` dependencies as follows:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
hello_world = { path = "../hello-world" }
|
||||
hello_world_derive = { path = "../hello-world/hello-world-derive" }
|
||||
```
|
||||
|
||||
Put the code from Listing A4-1 into *src/main.rs*, and executing `cargo run`
|
||||
should print `Hello, World! My name is Pancakes`! The implementation of the
|
||||
`HelloWorld` trait from the procedural macro was included without the
|
||||
`pancakes` crate needing to implement it; the `#[derive(HelloWorld)]` took care
|
||||
of adding the trait implementation.
|
||||
|
||||
## The Future of Macros
|
||||
|
||||
In the future, we’ll be expanding both declarative and procedural macros. A
|
||||
better declarative macro system will be used with the `macro` keyword, and
|
||||
we’ll add more types of procedural macros, for more powerful tasks than only
|
||||
`derive`. These systems are still under development at the time of publication;
|
||||
please consult the online Rust documentation for the latest information.
|
@ -15,3 +15,5 @@ For resources in languages other than English. Most are still in progress; see
|
||||
- [한국어](https://github.com/rinthel/rust-lang-book-ko)
|
||||
- [日本語](https://github.com/hazama-yuinyan/book)
|
||||
- [Français](https://github.com/quadrifoglio/rust-book-fr)
|
||||
- [Polski](https://github.com/paytchoo/book-pl)
|
||||
- [עברית](https://github.com/idanmel/rust-book-heb)
|
@ -27,8 +27,8 @@ your password. If it all goes well, you’ll see this appear:
|
||||
Rust is installed now. Great!
|
||||
```
|
||||
|
||||
Of course, if you disapprove of the `curl | sh` pattern, you can download, inspect
|
||||
and run the script however you like.
|
||||
Of course, if you distrust using `curl URL | sh` to install software, you can download,
|
||||
inspect, and run the script however you like.
|
||||
|
||||
The installation script automatically adds Rust to your system PATH after your next login.
|
||||
If you want to start using Rust right away, run the following command in your shell:
|
||||
|
@ -149,6 +149,7 @@ On Windows, you’d enter:
|
||||
```cmd
|
||||
> dir /B %= the /B option says to only show the file names =%
|
||||
main.exe
|
||||
main.pdb
|
||||
main.rs
|
||||
```
|
||||
|
||||
@ -316,6 +317,7 @@ program through Cargo! To do so, enter the following commands:
|
||||
```text
|
||||
$ cargo build
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs
|
||||
```
|
||||
|
||||
This should have created an executable file in *target/debug/hello_cargo* (or
|
||||
@ -350,6 +352,7 @@ and then run:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||
Running `target/debug/hello_cargo`
|
||||
Hello, world!
|
||||
```
|
||||
@ -363,6 +366,7 @@ this:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
|
||||
Running `target/debug/hello_cargo`
|
||||
Hello, world!
|
||||
```
|
||||
|
@ -36,7 +36,7 @@ You’ll see different type annotations as we discuss the various data types.
|
||||
### Scalar Types
|
||||
|
||||
A *scalar* type represents a single value. Rust has four primary scalar types:
|
||||
integers, floating-point numbers, booleans, and characters. You’ll likely
|
||||
integers, floating-point numbers, Booleans, and characters. You’ll likely
|
||||
recognize these from other programming languages, but let’s jump into how they
|
||||
work in Rust.
|
||||
|
||||
@ -156,8 +156,8 @@ list of all operators that Rust provides.
|
||||
|
||||
#### The Boolean Type
|
||||
|
||||
As in most other programming languages, a boolean type in Rust has two possible
|
||||
values: `true` and `false`. The boolean type in Rust is specified using `bool`.
|
||||
As in most other programming languages, a Boolean type in Rust has two possible
|
||||
values: `true` and `false`. The Boolean type in Rust is specified using `bool`.
|
||||
For example:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
@ -170,7 +170,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
The main way to consume boolean values is through conditionals, such as an `if`
|
||||
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.
|
||||
|
||||
|
@ -102,9 +102,9 @@ error[E0308]: mismatched types
|
||||
```
|
||||
|
||||
The error indicates that Rust expected a `bool` but got an integer. Rust will
|
||||
not automatically try to convert non-boolean types to a boolean, unlike
|
||||
not automatically try to convert non-Boolean types to a Boolean, unlike
|
||||
languages such as Ruby and JavaScript. You must be explicit and always provide
|
||||
`if` with a `boolean` as its condition. If we want the `if` code block to run
|
||||
`if` with a Boolean as its condition. If we want the `if` code block to run
|
||||
only when a number is not equal to `0`, for example, we can change the `if`
|
||||
expression to the following:
|
||||
|
||||
|
@ -428,7 +428,7 @@ be sure, but as a general rule, any group of simple scalar values can be
|
||||
`Copy`. Here are some of the types that are `Copy`:
|
||||
|
||||
* All the integer types, like `u32`.
|
||||
* The boolean type, `bool`, with values `true` and `false`.
|
||||
* The Boolean type, `bool`, with values `true` and `false`.
|
||||
* The character type, `char`.
|
||||
* All the floating point types, like `f64`.
|
||||
* Tuples, but only if they contain types that are also `Copy`. `(i32, i32)` is
|
||||
|
@ -161,7 +161,7 @@ parameter will be by looking at the code that calls the method:
|
||||
read `rect2` (rather than write, which would mean we’d need a mutable borrow),
|
||||
and we want `main` to retain ownership of `rect2` so we can use it again after
|
||||
calling the `can_hold` method. The return value of `can_hold` will be a
|
||||
boolean, and the implementation will check whether the width and height of
|
||||
Boolean, and the implementation will check whether the width and height of
|
||||
`self` are both greater than the width and height of the other `Rectangle`,
|
||||
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||
Listing 5-13, shown in Listing 5-15:
|
||||
|
@ -5,7 +5,7 @@ us to compare a value against a series of patterns and then execute code based
|
||||
on which pattern matches. Patterns can be made up of literal values, variable
|
||||
names, wildcards, and many other things; Chapter 18 covers all the different
|
||||
kinds of patterns and what they do. The power of `match` comes from the
|
||||
expressiveness of the patterns and the compiler checks that make sure all
|
||||
expressiveness of the patterns and the compiler checks that all
|
||||
possible cases are handled.
|
||||
|
||||
Think of a `match` expression kind of like a coin sorting machine: coins slide
|
||||
@ -43,7 +43,7 @@ the variants of the enum as its patterns.</span>
|
||||
Let’s break down the `match` in the `value_in_cents` function. First, we list
|
||||
the `match` keyword followed by an expression, which in this case is the value
|
||||
`coin`. This seems very similar to an expression used with `if`, but there’s a
|
||||
big difference: with `if`, the expression needs to return a boolean value.
|
||||
big difference: with `if`, the expression needs to return a Boolean value.
|
||||
Here, it can be any type. The type of `coin` in this example is the `Coin` enum
|
||||
that we defined in Listing 6-3.
|
||||
|
||||
|
@ -204,7 +204,7 @@ These would be good reasons to separate the `client`, `network`, and `server`
|
||||
modules from *src/lib.rs* and place them into their own files.
|
||||
|
||||
First, replace the `client` module code with only the declaration of the
|
||||
`client` module, so that your *src/lib.rs* looks like the following:
|
||||
`client` module, so that your *src/lib.rs* looks like code shown in Listing 7-4:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -222,6 +222,8 @@ mod network {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-4: Extracting the contents of the `client` module but leaving the declaration in *src/lib.rs*</span>
|
||||
|
||||
We’re still *declaring* the `client` module here, but by replacing the block
|
||||
with a semicolon, we’re telling Rust to look in another location for the code
|
||||
defined within the scope of the `client` module. In other words, the line `mod
|
||||
@ -345,7 +347,7 @@ fn connect() {
|
||||
}
|
||||
```
|
||||
|
||||
When we try to `cargo build`, we’ll get the error shown in Listing 7-4:
|
||||
When we try to `cargo build`, we’ll get the error shown in Listing 7-5:
|
||||
|
||||
```text
|
||||
$ cargo build
|
||||
@ -368,14 +370,14 @@ note: ... or maybe `use` the module `server` instead of possibly redeclaring it
|
||||
| ^^^^^^
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-4: Error when trying to extract the `server`
|
||||
<span class="caption">Listing 7-5: Error when trying to extract the `server`
|
||||
submodule into *src/server.rs*</span>
|
||||
|
||||
The error says we `cannot declare a new module at this location` and is
|
||||
pointing to the `mod server;` line in *src/network.rs*. So *src/network.rs* is
|
||||
different than *src/lib.rs* somehow: keep reading to understand why.
|
||||
|
||||
The note in the middle of Listing 7-4 is actually very helpful because it
|
||||
The note in the middle of Listing 7-5 is actually very helpful because it
|
||||
points out something we haven’t yet talked about doing:
|
||||
|
||||
```text
|
||||
|
@ -1,6 +1,6 @@
|
||||
## Controlling Visibility with `pub`
|
||||
|
||||
We resolved the error messages shown in Listing 7-4 by moving the `network` and
|
||||
We resolved the error messages shown in Listing 7-5 by moving the `network` and
|
||||
`network::server` code into the *src/network/mod.rs* and
|
||||
*src/network/server.rs* files, respectively. At that point, `cargo build` was
|
||||
able to build our project, but we still get warning messages about the
|
||||
@ -241,7 +241,7 @@ Overall, these are the rules for item visibility:
|
||||
### Privacy Examples
|
||||
|
||||
Let’s look at a few more privacy examples to get some practice. Create a new
|
||||
library project and enter the code in Listing 7-5 into your new project’s
|
||||
library project and enter the code in Listing 7-6 into your new project’s
|
||||
*src/lib.rs*:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
@ -267,7 +267,7 @@ fn try_me() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-5: Examples of private and public functions,
|
||||
<span class="caption">Listing 7-6: Examples of private and public functions,
|
||||
some of which are incorrect</span>
|
||||
|
||||
Before you try to compile this code, make a guess about which lines in the
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
We’ve covered how to call functions defined within a module using the module
|
||||
name as part of the call, as in the call to the `nested_modules` function shown
|
||||
here in Listing 7-6:
|
||||
here in Listing 7-7:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -20,7 +20,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 7-6: Calling a function by fully specifying its
|
||||
<span class="caption">Listing 7-7: Calling a function by fully specifying its
|
||||
enclosing module’s path</span>
|
||||
|
||||
As you can see, referring to the fully qualified name can get quite lengthy.
|
||||
@ -166,7 +166,7 @@ communicator
|
||||
|
||||
Tests are for exercising the code within our library, so let’s try to call our
|
||||
`client::connect` function from this `it_works` function, even though we won’t
|
||||
be checking any functionality right now. This won't work yet:
|
||||
be checking any functionality right now. This won’t work yet:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -256,7 +256,7 @@ $ cargo test
|
||||
running 1 test
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
@ -118,7 +118,7 @@ argument, which gives us an `Option<&T>`.
|
||||
|
||||
The reason Rust has two ways to reference an element is 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
|
||||
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:
|
||||
|
||||
@ -154,7 +154,7 @@ 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 we 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
|
||||
the first element in a vector and try to add an element to the end, which won't
|
||||
the first element in a vector and try to add an element to the end, which won’t
|
||||
work:
|
||||
|
||||
```rust,ignore
|
||||
|
@ -16,7 +16,7 @@ location beyond the end of an array.
|
||||
|
||||
Most languages don’t distinguish between these two kinds of errors and handle
|
||||
both in the same way using mechanisms like exceptions. Rust doesn’t have
|
||||
exceptions. Instead, it has the value `Result<T, E>` for recoverable errors and
|
||||
exceptions. Instead, it has the type `Result<T, E>` for recoverable errors and
|
||||
the `panic!` macro that stops execution when it encounters unrecoverable
|
||||
errors. This chapter covers calling `panic!` first and then talks about
|
||||
returning `Result<T, E>` values. Additionally, we’ll explore considerations to
|
||||
|
@ -42,15 +42,14 @@ $ cargo run
|
||||
Compiling panic v0.1.0 (file:///projects/panic)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs
|
||||
Running `target/debug/panic`
|
||||
thread 'main' panicked at 'crash and burn', src/main.rs:2
|
||||
thread 'main' panicked at 'crash and burn', src/main.rs:2:4
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
||||
```
|
||||
|
||||
The call to `panic!` causes the error message contained in the last three
|
||||
lines. The first line shows our panic message and the place in our source code
|
||||
where the panic occurred: *src/main.rs:2* indicates that it’s the second line
|
||||
of our *src/main.rs* file.
|
||||
where the panic occurred: *src/main.rs:2:4* indicates that it’s the second
|
||||
line, fourth character of our *src/main.rs* file.
|
||||
|
||||
In this case, the line indicated is part of our code, and if we go to that
|
||||
line, we see the `panic!` macro call. In other cases, the `panic!` call might
|
||||
@ -74,17 +73,18 @@ element by index in a vector:
|
||||
fn main() {
|
||||
let v = vec![1, 2, 3];
|
||||
|
||||
v[100];
|
||||
v[99];
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-1: Attempting to access an element beyond the
|
||||
end of a vector, which will cause a `panic!`</span>
|
||||
|
||||
Here, we’re attempting to access the hundredth element of our vector, but it
|
||||
has only three elements. In this situation, Rust will panic. Using `[]` is
|
||||
supposed to return an element, but if you pass an invalid index, there’s no
|
||||
element that Rust could return here that would be correct.
|
||||
Here, we’re attempting to access the hundredth element of our vector (which is
|
||||
at index 99 because indexing starts at zero), but it has only three elements.
|
||||
In this situation, Rust will panic. Using `[]` is supposed to return an
|
||||
element, but if you pass an invalid index, there’s no element that Rust could
|
||||
return here that would be correct.
|
||||
|
||||
Other languages, like C, will attempt to give you exactly what you asked for in
|
||||
this situation, even though it isn’t what you want: you’ll get whatever is at
|
||||
@ -104,15 +104,15 @@ $ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs
|
||||
Running `target/debug/panic`
|
||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
|
||||
100', /stable-dist-rustc/build/src/libcollections/vec.rs:1362
|
||||
99', /checkout/src/liballoc/vec.rs:1555:10
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
||||
```
|
||||
|
||||
This error points at a file we didn’t write, *libcollections/vec.rs*. That’s
|
||||
the implementation of `Vec<T>` in the standard library. The code that gets run
|
||||
when we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is
|
||||
where the `panic!` is actually happening.
|
||||
This error points at a file we didn’t write, *vec.rs*. That’s the
|
||||
implementation of `Vec<T>` in the standard library. The code that gets run when
|
||||
we use `[]` on our vector `v` is in *vec.rs*, and that is where the `panic!` is
|
||||
actually happening.
|
||||
|
||||
The next note line tells us that we can set the `RUST_BACKTRACE` environment
|
||||
variable to get a backtrace of exactly what happened to cause the error. A
|
||||
@ -129,40 +129,42 @@ Listing 9-2 shows output similar to what you’ll see:
|
||||
$ RUST_BACKTRACE=1 cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||
Running `target/debug/panic`
|
||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 100', /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /checkout/src/liballoc/vec.rs:1555:10
|
||||
stack backtrace:
|
||||
1: 0x560ed90ec04c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
|
||||
at /stable-dist-rustc/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
|
||||
2: 0x560ed90ee03e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:351
|
||||
3: 0x560ed90edc44 - std::panicking::default_hook::h1670459d2f3f8843
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:367
|
||||
4: 0x560ed90ee41b - std::panicking::rust_panic_with_hook::hcf0ddb069e7abcd7
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:555
|
||||
5: 0x560ed90ee2b4 - std::panicking::begin_panic::hd6eb68e27bdf6140
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:517
|
||||
6: 0x560ed90ee1d9 - std::panicking::begin_panic_fmt::abcd5965948b877f8
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:501
|
||||
7: 0x560ed90ee167 - rust_begin_unwind
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:477
|
||||
8: 0x560ed911401d - core::panicking::panic_fmt::hc0f6d7b2c300cdd9
|
||||
at /stable-dist-rustc/build/src/libcore/panicking.rs:69
|
||||
9: 0x560ed9113fc8 - core::panicking::panic_bounds_check::h02a4af86d01b3e96
|
||||
at /stable-dist-rustc/build/src/libcore/panicking.rs:56
|
||||
10: 0x560ed90e71c5 - <collections::vec::Vec<T> as core::ops::Index<usize>>::index::h98abcd4e2a74c41
|
||||
at /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
||||
11: 0x560ed90e727a - panic::main::h5d6b77c20526bc35
|
||||
at /home/you/projects/panic/src/main.rs:4
|
||||
12: 0x560ed90f5d6a - __rust_maybe_catch_panic
|
||||
at /stable-dist-rustc/build/src/libpanic_unwind/lib.rs:98
|
||||
13: 0x560ed90ee926 - std::rt::lang_start::hd7c880a37a646e81
|
||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:436
|
||||
at /stable-dist-rustc/build/src/libstd/panic.rs:361
|
||||
at /stable-dist-rustc/build/src/libstd/rt.rs:57
|
||||
14: 0x560ed90e7302 - main
|
||||
15: 0x7f0d53f16400 - __libc_start_main
|
||||
16: 0x560ed90e6659 - _start
|
||||
17: 0x0 - <unknown>
|
||||
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
|
||||
at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
|
||||
1: std::sys_common::backtrace::_print
|
||||
at /checkout/src/libstd/sys_common/backtrace.rs:71
|
||||
2: std::panicking::default_hook::{{closure}}
|
||||
at /checkout/src/libstd/sys_common/backtrace.rs:60
|
||||
at /checkout/src/libstd/panicking.rs:381
|
||||
3: std::panicking::default_hook
|
||||
at /checkout/src/libstd/panicking.rs:397
|
||||
4: std::panicking::rust_panic_with_hook
|
||||
at /checkout/src/libstd/panicking.rs:611
|
||||
5: std::panicking::begin_panic
|
||||
at /checkout/src/libstd/panicking.rs:572
|
||||
6: std::panicking::begin_panic_fmt
|
||||
at /checkout/src/libstd/panicking.rs:522
|
||||
7: rust_begin_unwind
|
||||
at /checkout/src/libstd/panicking.rs:498
|
||||
8: core::panicking::panic_fmt
|
||||
at /checkout/src/libcore/panicking.rs:71
|
||||
9: core::panicking::panic_bounds_check
|
||||
at /checkout/src/libcore/panicking.rs:58
|
||||
10: <alloc::vec::Vec<T> as core::ops::index::Index<usize>>::index
|
||||
at /checkout/src/liballoc/vec.rs:1555
|
||||
11: panic::main
|
||||
at src/main.rs:4
|
||||
12: __rust_maybe_catch_panic
|
||||
at /checkout/src/libpanic_unwind/lib.rs:99
|
||||
13: std::rt::lang_start
|
||||
at /checkout/src/libstd/panicking.rs:459
|
||||
at /checkout/src/libstd/panic.rs:361
|
||||
at /checkout/src/libstd/rt.rs:61
|
||||
14: main
|
||||
15: __libc_start_main
|
||||
16: <unknown>
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-2: The backtrace generated by a call to
|
||||
@ -180,7 +182,7 @@ want our program to panic, the location pointed to by the first line mentioning
|
||||
a file we wrote is where we should start investigating to figure out how we got
|
||||
to this location with values that caused the panic. In Listing 9-1 where we
|
||||
deliberately wrote code that would panic in order to demonstrate how to use
|
||||
backtraces, the way to fix the panic is to not request an element at index 100
|
||||
backtraces, the way to fix the panic is to not request an element at index 99
|
||||
from a vector that only contains three items. When your code panics in the
|
||||
future, you’ll need to figure out what action the code is taking with what
|
||||
values that causes the panic and what the code should do instead.
|
||||
|
@ -6,9 +6,9 @@ interpret and respond to. For example, if we try to open a file and that
|
||||
operation fails because the file doesn’t exist, we might want to create the
|
||||
file instead of terminating the process.
|
||||
|
||||
Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result`
|
||||
Type][handle_failure]<!-- ignore -->” section that the `Result` enum is defined
|
||||
as having two variants, `Ok` and `Err`, as follows:
|
||||
Recall from “[Handling Potential Failure with the `Result`
|
||||
Type][handle_failure]<!-- ignore -->” in Chapter 2 that the `Result` enum is
|
||||
defined as having two variants, `Ok` and `Err`, as follows:
|
||||
|
||||
[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type
|
||||
|
||||
@ -66,7 +66,7 @@ error[E0308]: mismatched types
|
||||
`std::result::Result`
|
||||
|
|
||||
= note: expected type `u32`
|
||||
= note: found type `std::result::Result<std::fs::File, std::io::Error>`
|
||||
found type `std::result::Result<std::fs::File, std::io::Error>`
|
||||
```
|
||||
|
||||
This tells us the return type of the `File::open` function is a `Result<T, E>`.
|
||||
@ -233,7 +233,7 @@ the `panic!` call that the `unwrap` method makes:
|
||||
```text
|
||||
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
|
||||
repr: Os { code: 2, message: "No such file or directory" } }',
|
||||
/stable-dist-rustc/build/src/libcore/result.rs:868
|
||||
src/libcore/result.rs:906:4
|
||||
```
|
||||
|
||||
Another method, `expect`, which is similar to `unwrap`, lets us also choose the
|
||||
@ -258,8 +258,7 @@ will be the parameter that we pass to `expect`, rather than the default
|
||||
|
||||
```text
|
||||
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
2, message: "No such file or directory" } }',
|
||||
/stable-dist-rustc/build/src/libcore/result.rs:868
|
||||
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
|
||||
```
|
||||
|
||||
Because this error message starts with the text we specified, `Failed to open
|
||||
@ -384,18 +383,16 @@ 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.
|
||||
|
||||
The one difference between the `match` expression from Listing 9-6 and what the
|
||||
question mark operator does is that when using the question mark operator,
|
||||
error values go through the `from` function defined in the `From` trait in the
|
||||
standard library. Many error types implement the `from` function to convert an
|
||||
error of one type into an error of another type. When used by the question mark
|
||||
operator, the call to the `from` function converts the error type that the
|
||||
question mark operator gets into the error type defined in the return type of
|
||||
the current function that we’re using `?` in. This is useful when parts of a
|
||||
function might fail for many different reasons, but the function returns one
|
||||
error type that represents all the ways the function might fail. As long as
|
||||
each error type implements the `from` function to define how to convert itself
|
||||
to the returned error type, the question mark operator takes care of the
|
||||
There is a difference between what the `match` expression from Listing 9-6 and
|
||||
the question mark operator do: error values used with `?` 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 the question mark calls the
|
||||
`from` function, the error type received is converted into the error type
|
||||
defined in the return type of the current function. This is useful when a
|
||||
function returns one error type to represent all the ways a function might
|
||||
fail, even if parts might fail for many different reasons. As long as each
|
||||
error type implements the `from` function to define how to convert itself to
|
||||
the returned error type, the question mark operator takes care of the
|
||||
conversion automatically.
|
||||
|
||||
In the context of Listing 9-7, the `?` at the end of the `File::open` call will
|
||||
@ -458,14 +455,14 @@ fn main() {
|
||||
When we compile this code, we get the following error message:
|
||||
|
||||
```text
|
||||
error[E0277]: the `?` operator can only be used in a function that returns
|
||||
`Result` (or another type that implements `std::ops::Try`)
|
||||
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
|
||||
--> src/main.rs:4:13
|
||||
|
|
||||
4 | let f = File::open("hello.txt")?;
|
||||
| ------------------------
|
||||
| |
|
||||
| cannot use the `?` operator in a function that returns `()`
|
||||
| the `?` operator can only be used in a function that returns
|
||||
`Result` (or another type that implements `std::ops::Try`)
|
||||
| in this macro invocation
|
||||
|
|
||||
= help: the trait `std::ops::Try` is not implemented for `()`
|
||||
|
@ -138,7 +138,7 @@ number being in range, like so:
|
||||
|
||||
```rust,ignore
|
||||
loop {
|
||||
// snip
|
||||
// --snip--
|
||||
|
||||
let guess: i32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
@ -151,7 +151,7 @@ loop {
|
||||
}
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
// snip
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -413,8 +413,6 @@ and `Copy` traits, like `i32` and `char`:
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
use std::cmp::PartialOrd;
|
||||
|
||||
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
||||
let mut largest = list[0];
|
||||
|
||||
@ -505,7 +503,7 @@ similar to this code:
|
||||
|
||||
```rust,ignore
|
||||
impl<T: Display> ToString for T {
|
||||
// ...snip...
|
||||
// --snip--
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1,33 +1,33 @@
|
||||
# Writing Automated Tests
|
||||
|
||||
> Program testing can be a very effective way to show the presence of bugs, but
|
||||
> it is hopelessly inadequate for showing their absence.
|
||||
> Edsger W. Dijkstra, “The Humble Programmer” (1972)
|
||||
|
||||
Correctness in our programs means that our code does what we intend for it to
|
||||
do. Rust is a programming language that cares a lot about correctness, but
|
||||
correctness is a complex topic and isn’t easy to prove. Rust’s type system
|
||||
In his 1972 essay, “The Humble Programmer,” Edsger W. Dijkstra said that
|
||||
“Program testing can be a very effective way to show the presence of bugs, but
|
||||
it is hopelessly inadequate for showing their absence.” That doesn’t mean we
|
||||
shouldn’t try to test as much as we can! Correctness in our programs is the
|
||||
extent to which our code does what we intend it to do. Rust is a programming
|
||||
language designed with a high degree of concern about the correctness of
|
||||
programs, but correctness is complex and not easy to prove. Rust’s type system
|
||||
shoulders a huge part of this burden, but the type system cannot catch every
|
||||
kind of incorrectness. As such, Rust includes support for writing software
|
||||
tests within the language itself.
|
||||
kind of incorrectness. As such, Rust includes support for writing automated
|
||||
software tests within the language.
|
||||
|
||||
As an example, say we write a function called `add_two` that adds two to
|
||||
whatever number is passed to it. This function’s signature accepts an integer
|
||||
as a parameter and returns an integer as a result. When we implement and
|
||||
compile that function, Rust will do all the type checking and borrow checking
|
||||
that we’ve seen so far to make sure that, for instance, we aren’t passing a
|
||||
`String` value or an invalid reference to this function. What Rust *can’t*
|
||||
check is that this function will do precisely what we intend: return the
|
||||
parameter plus two, rather than, say, the parameter plus 10 or the parameter
|
||||
compile that function, Rust does all the type checking and borrow checking that
|
||||
you’ve learned so far to ensure that, for instance, we aren’t passing a
|
||||
`String` value or an invalid reference to this function. But Rust *can’t* check
|
||||
that this function will do precisely what we intend, which is return the
|
||||
parameter plus two rather than, say, the parameter plus 10 or the parameter
|
||||
minus 50! That’s where tests come in.
|
||||
|
||||
We can write tests that assert, for example, that when we pass `3` to the
|
||||
`add_two` function, we get `5` back. We can run these tests whenever we make
|
||||
changes to our code to make sure any existing correct behavior has not changed.
|
||||
`add_two` function, the returned value is `5`. We can run these tests whenever
|
||||
we make changes to our code to make sure any existing correct behavior has not
|
||||
changed.
|
||||
|
||||
Testing is a complex skill, and we cannot hope to cover everything about how to
|
||||
write good tests in one chapter of a book, so here we’ll just discuss the
|
||||
mechanics of Rust’s testing facilities. We’ll talk about the annotations and
|
||||
macros available to you when writing your tests, the default behavior and
|
||||
options provided for running your tests, and how to organize tests into unit
|
||||
tests and integration tests.
|
||||
Testing is a complex skill: although we can’t cover every detail about how to
|
||||
write good tests in one chapter, we’ll discuss the mechanics of Rust’s testing
|
||||
facilities. We’ll talk about the annotations and macros available to you when
|
||||
writing your tests, the default behavior and options provided for running your
|
||||
tests, and how to organize tests into unit tests and integration tests.
|
||||
|
@ -1,32 +1,37 @@
|
||||
## How to Write Tests
|
||||
|
||||
Tests are Rust functions that verify that the non-test code is functioning in
|
||||
the expected manner. The bodies of test functions typically perform some setup,
|
||||
run the code we want to test, then assert whether the results are what we
|
||||
expect. Let’s look at the features Rust provides specifically for writing
|
||||
tests: the `test` attribute, a few macros, and the `should_panic` attribute.
|
||||
the expected manner. The bodies of test functions typically perform these three
|
||||
actions:
|
||||
|
||||
1. Set up any needed data or state
|
||||
2. Run the code we want to test
|
||||
3. Assert the results are what we expect
|
||||
|
||||
Let’s look at the features Rust provides specifically for writing tests that
|
||||
take these actions, which include the `test` attribute, a few macros, and the
|
||||
`should_panic` attribute.
|
||||
|
||||
### The Anatomy of a Test Function
|
||||
|
||||
At its simplest, a test in Rust is a function that’s annotated with the `test`
|
||||
attribute. Attributes are metadata about pieces of Rust code: the `derive`
|
||||
attribute that we used with structs in Chapter 5 is one example. To make a
|
||||
function into a test function, we add `#[test]` on the line before `fn`. When
|
||||
we run our tests with the `cargo test` command, Rust will build a test runner
|
||||
binary that runs the functions annotated with the `test` attribute and reports
|
||||
on whether each test function passes or fails.
|
||||
attribute. Attributes are metadata about pieces of Rust code; one example is
|
||||
the `derive` attribute we used with structs in Chapter 5. To change a function
|
||||
into a test function, we add `#[test]` on the line before `fn`. When we run our
|
||||
tests with the `cargo test` command, Rust builds a test runner binary that runs
|
||||
the functions annotated with the `test` attribute and reports on whether each
|
||||
test function passes or fails.
|
||||
|
||||
We saw in Chapter 7 that when you make a new library project with Cargo, a test
|
||||
module with a test function in it is automatically generated for us. This is to
|
||||
help us get started writing our tests so we don’t have to go look up the
|
||||
exact structure and syntax of test functions every time we start a new project.
|
||||
We can add as many additional test functions and as many test modules as we
|
||||
want, though!
|
||||
In Chapter 7, we saw that when we make a new library project with Cargo, a test
|
||||
module with a test function in it is automatically generated for us. This
|
||||
module helps us start writing our tests so we don’t have to look up the exact
|
||||
structure and syntax of test functions every time we start a new project. We
|
||||
can add as many additional test functions and as many test modules as we want!
|
||||
|
||||
We’re going to explore some aspects of how tests work by experimenting with the
|
||||
template test generated for us, without actually testing any code. Then we’ll
|
||||
write some real-world tests that call some code that we’ve written and assert
|
||||
that its behavior is correct.
|
||||
We’ll explore some aspects of how tests work by experimenting with the template
|
||||
test generated for us without actually testing any code. Then we’ll write some
|
||||
real-world tests that call some code that we’ve written and assert that its
|
||||
behavior is correct.
|
||||
|
||||
Let’s create a new library project called `adder`:
|
||||
|
||||
@ -36,8 +41,8 @@ $ cargo new adder
|
||||
$ cd adder
|
||||
```
|
||||
|
||||
The contents of the `src/lib.rs` file in your adder library should be as
|
||||
follows:
|
||||
The contents of the *src/lib.rs* file in your adder library should look like
|
||||
Listing 11-1:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -52,21 +57,21 @@ mod tests {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-1: The test module and function generated
|
||||
automatically for us by `cargo new`</span>
|
||||
automatically by `cargo new`</span>
|
||||
|
||||
For now, let’s ignore the top two lines and focus on the function to see how it
|
||||
works. Note the `#[test]` annotation before the `fn` line: this attribute
|
||||
indicates this is a test function, so that the test runner knows to treat this
|
||||
indicates this is a test function, so the test runner knows to treat this
|
||||
function as a test. We could also have non-test functions in the `tests` module
|
||||
to help set up common scenarios or perform common operations, so we need to
|
||||
indicate which functions are tests with the `#[test]` attribute.
|
||||
indicate which functions are tests by using the `#[test]` attribute.
|
||||
|
||||
The function body uses the `assert_eq!` macro to assert that 2 + 2 equals 4.
|
||||
This assertion serves as an example of the format for a typical test. Let’s run
|
||||
it and see that this test passes.
|
||||
it to see that this test passes.
|
||||
|
||||
The `cargo test` command runs all tests we have in our project, as shown in
|
||||
Listing 11-2:
|
||||
The `cargo test` command runs all tests in our project, as shown in Listing
|
||||
11-2:
|
||||
|
||||
```text
|
||||
$ cargo test
|
||||
@ -77,41 +82,44 @@ $ cargo test
|
||||
running 1 test
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-2: The output from running the one
|
||||
automatically generated test</span>
|
||||
<span class="caption">Listing 11-2: The output from running the automatically
|
||||
generated test</span>
|
||||
|
||||
Cargo compiled and ran our test. After the `Compiling`, `Finished`, and
|
||||
`Running` lines, we see the line `running 1 test`. The next line shows the name
|
||||
Cargo compiled and ran the test. After the `Compiling`, `Finished`, and
|
||||
`Running` lines is the line `running 1 test`. The next line shows the name
|
||||
of the generated test function, called `it_works`, and the result of running
|
||||
that test, `ok`. Then we see the overall summary of running the tests: `test
|
||||
result: ok.` means all the tests passed. `1 passed; 0 failed` adds up the
|
||||
number of tests that passed or failed.
|
||||
that test, `ok`. The overall summary of running the tests appears next. The
|
||||
text `test result: ok.` means that all the tests passed, and the portion that
|
||||
reads `1 passed; 0 failed` totals the number of tests that passed or failed.
|
||||
|
||||
We don’t have any tests we’ve marked as ignored, so the summary says `0
|
||||
ignored`. We’re going to talk about ignoring tests in the next section on
|
||||
different ways to run tests. The `0 measured` statistic is for benchmark tests
|
||||
that measure performance. Benchmark tests are, as of this writing, only
|
||||
available in nightly Rust. See Chapter 1 for more information about nightly
|
||||
Rust.
|
||||
Because we don’t have any tests we’ve marked as ignored, the summary shows `0
|
||||
ignored`. We also haven’t filtered the tests being run, so the end of the
|
||||
summary shows `0 filtered out`. We’ll talk about ignoring and filtering out
|
||||
tests in the next section, “Controlling How Tests Are Run.”
|
||||
|
||||
The next part of the test output that starts with `Doc-tests adder` is for the
|
||||
results of any documentation tests. We don’t have any documentation tests yet,
|
||||
but Rust can compile any code examples that appear in our API documentation.
|
||||
This feature helps us keep our docs and our code in sync! We’ll be talking
|
||||
about how to write documentation tests in the “Documentation Comments” section
|
||||
of Chapter 14. We’re going to ignore the `Doc-tests` output for now.
|
||||
The `0 measured` statistic is for benchmark tests that measure performance.
|
||||
Benchmark tests are, as of this writing, only available in nightly Rust. See
|
||||
Chapter 1 for more information about nightly Rust.
|
||||
|
||||
Let’s change the name of our test and see how that changes the test output.
|
||||
Give the `it_works` function a different name, such as `exploration`, like so:
|
||||
The next part of the test output, which starts with `Doc-tests adder`, is for
|
||||
the results of any documentation tests. We don’t have any documentation tests
|
||||
yet, but Rust can compile any code examples that appear in our API
|
||||
documentation. This feature helps us keep our docs and our code in sync! We’ll
|
||||
discuss how to write documentation tests in the “Documentation Comments”
|
||||
section of Chapter 14. For now, we’ll ignore the `Doc-tests` output.
|
||||
|
||||
Let’s change the name of our test to see how that changes the test output.
|
||||
Change the `it_works` function to a different name, such as `exploration`, like
|
||||
so:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -125,22 +133,22 @@ mod tests {
|
||||
}
|
||||
```
|
||||
|
||||
And run `cargo test` again. In the output, we’ll now see `exploration` instead
|
||||
of `it_works`:
|
||||
Then run `cargo test` again. The output now shows `exploration` instead of
|
||||
`it_works`:
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
test tests::exploration ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Let’s add another test, but this time we’ll make a test that fails! Tests fail
|
||||
when something in the test function panics. Each test is run in a new thread,
|
||||
and when the main thread sees that a test thread has died, the test is marked
|
||||
as failed. We talked about the simplest way to cause a panic in Chapter 9: call
|
||||
the `panic!` macro! Type in the new test so that your `src/lib.rs` now looks
|
||||
like Listing 11-3:
|
||||
as failed. We talked about the simplest way to cause a panic in Chapter 9,
|
||||
which is to call the `panic!` macro. Enter the new test, `another`, so your
|
||||
*src/lib.rs* file looks like Listing 11-3:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -159,10 +167,10 @@ mod tests {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-3: Adding a second test; one that will fail
|
||||
since we call the `panic!` macro</span>
|
||||
<span class="caption">Listing 11-3: Adding a second test that will fail because
|
||||
we call the `panic!` macro</span>
|
||||
|
||||
And run the tests again with `cargo test`. The output should look like Listing
|
||||
Run the tests again using `cargo test`. The output should look like Listing
|
||||
11-4, which shows that our `exploration` test passed and `another` failed:
|
||||
|
||||
```text
|
||||
@ -173,13 +181,13 @@ test tests::another ... FAILED
|
||||
failures:
|
||||
|
||||
---- tests::another stdout ----
|
||||
thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:9
|
||||
thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
|
||||
failures:
|
||||
tests::another
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
|
||||
error: test failed
|
||||
```
|
||||
@ -187,34 +195,35 @@ error: test failed
|
||||
<span class="caption">Listing 11-4: Test results when one test passes and one
|
||||
test fails</span>
|
||||
|
||||
Instead of `ok`, the line `test tests::another` says `FAILED`. We have two new
|
||||
sections between the individual results and the summary: the first section
|
||||
displays the detailed reason for the test failures. In this case, `another`
|
||||
failed because it `panicked at 'Make this test fail'`, which happened on
|
||||
*src/lib.rs* line 9. The next section lists just the names of all the failing
|
||||
tests, which is useful when there are lots of tests and lots of detailed
|
||||
failing test output. We can use the name of a failing test to run just that
|
||||
test in order to more easily debug it; we’ll talk more about ways to run tests
|
||||
in the next section.
|
||||
Instead of `ok`, the line `test tests::another` shows `FAILED`. Two new
|
||||
sections appear between the individual results and the summary: the first
|
||||
section displays the detailed reason for each test failure. In this case,
|
||||
`another` failed because it `panicked at 'Make this test fail'`, which happened
|
||||
on line 10 in the *src/lib.rs* file. The next section lists just the names of
|
||||
all the failing tests, which is useful when there are lots of tests and lots of
|
||||
detailed failing test output. We can use the name of a failing test to run just
|
||||
that test to more easily debug it; we’ll talk more about ways to run tests in
|
||||
the “Controlling How Tests Are Run” section.
|
||||
|
||||
Finally, we have the summary line: overall, our test result is `FAILED`. We had
|
||||
1 test pass and 1 test fail.
|
||||
The summary line displays at the end: overall, our test result is `FAILED`.
|
||||
We had one test pass and one test fail.
|
||||
|
||||
Now that we’ve seen what the test results look like in different scenarios,
|
||||
Now that you’ve seen what the test results look like in different scenarios,
|
||||
let’s look at some macros other than `panic!` that are useful in tests.
|
||||
|
||||
### Checking Results with the `assert!` Macro
|
||||
|
||||
The `assert!` macro, provided by the standard library, is useful when you want
|
||||
to ensure that some condition in a test evaluates to `true`. We give the
|
||||
`assert!` macro an argument that evaluates to a boolean. If the value is `true`,
|
||||
`assert!` does nothing and the test passes. If the value is `false`, `assert!`
|
||||
calls the `panic!` macro, which causes the test to fail. This is one macro that
|
||||
helps us check that our code is functioning in the way we intend.
|
||||
`assert!` macro an argument that evaluates to a Boolean. If the value is
|
||||
`true`, `assert!` does nothing and the test passes. If the value is `false`,
|
||||
the `assert!` macro calls the `panic!` macro, which causes the test to fail.
|
||||
Using the `assert!` macro helps us check that our code is functioning in the
|
||||
way we intend.
|
||||
|
||||
Remember all the way back in Chapter 5, Listing 5-9, where we had a `Rectangle`
|
||||
struct and a `can_hold` method, repeated here in Listing 11-5. Let’s put this
|
||||
code in *src/lib.rs* and write some tests for it using the `assert!` macro.
|
||||
In Chapter 5, Listing 5-15, we used a `Rectangle` struct and a `can_hold`
|
||||
method, which are repeated here in Listing 11-5. Let’s put this code in the
|
||||
*src/lib.rs* file and write some tests for it using the `assert!` macro.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -232,11 +241,11 @@ impl Rectangle {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-5: The `Rectangle` struct and its `can_hold`
|
||||
method from Chapter 5</span>
|
||||
<span class="caption">Listing 11-5: Using the `Rectangle` struct and its
|
||||
`can_hold` method from Chapter 5</span>
|
||||
|
||||
The `can_hold` method returns a boolean, which means it’s a perfect use case
|
||||
for the `assert!` macro. In Listing 11-6, let’s write a test that exercises the
|
||||
The `can_hold` method returns a Boolean, which means it’s a perfect use case
|
||||
for the `assert!` macro. In Listing 11-6, we write a test that exercises the
|
||||
`can_hold` method by creating a `Rectangle` instance that has a length of 8 and
|
||||
a width of 7, and asserting that it can hold another `Rectangle` instance that
|
||||
has a length of 5 and a width of 1:
|
||||
@ -259,25 +268,25 @@ mod tests {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-6: A test for `can_hold` that checks that a
|
||||
larger rectangle indeed holds a smaller rectangle</span>
|
||||
larger rectangle can indeed hold a smaller rectangle</span>
|
||||
|
||||
Note that we’ve added a new line inside the `tests` module: `use super::*;`.
|
||||
The `tests` module is a regular module that follows the usual visibility rules
|
||||
we covered in Chapter 7. Because we’re in an inner module, we need to bring the
|
||||
code under test in the outer module into the scope of the inner module. We’ve
|
||||
chosen to use a glob here so that anything we define in the outer module is
|
||||
available to this `tests` module.
|
||||
Note that we’ve added a new line inside the `tests` module: the `use super::*;`
|
||||
line. The `tests` module is a regular module that follows the usual visibility
|
||||
rules we covered in Chapter 7 in the “Privacy Rules” section. Because the
|
||||
`tests` module is an inner module, we need to bring the code under test in the
|
||||
outer module into the scope of the inner module. We use a glob here so anything
|
||||
we define in the outer module is available to this `tests` module.
|
||||
|
||||
We’ve named our test `larger_can_hold_smaller`, and we’ve created the two
|
||||
`Rectangle` instances that we need. Then we called the `assert!` macro and
|
||||
passed it the result of calling `larger.can_hold(&smaller)`. This expression is
|
||||
supposed to return `true`, so our test should pass. Let’s find out!
|
||||
passed it the result of calling `larger.can_hold(&smaller)`. This expression
|
||||
is supposed to return `true`, so our test should pass. Let’s find out!
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
test tests::larger_can_hold_smaller ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
It does pass! Let’s add another test, this time asserting that a smaller
|
||||
@ -292,10 +301,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn larger_can_hold_smaller() {
|
||||
let larger = Rectangle { length: 8, width: 7 };
|
||||
let smaller = Rectangle { length: 5, width: 1 };
|
||||
|
||||
assert!(larger.can_hold(&smaller));
|
||||
// --snip--
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -309,28 +315,29 @@ mod tests {
|
||||
```
|
||||
|
||||
Because the correct result of the `can_hold` function in this case is `false`,
|
||||
we need to negate that result before we pass it to the `assert!` macro. This
|
||||
way, our test will pass if `can_hold` returns `false`:
|
||||
we need to negate that result before we pass it to the `assert!` macro. As a
|
||||
result, our test will pass if `can_hold` returns `false`:
|
||||
|
||||
```text
|
||||
running 2 tests
|
||||
test tests::smaller_cannot_hold_larger ... ok
|
||||
test tests::larger_can_hold_smaller ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Two passing tests! Now let’s see what happens to our test results if we
|
||||
Two tests that pass! Now let’s see what happens to our test results when we
|
||||
introduce a bug in our code. Let’s change the implementation of the `can_hold`
|
||||
method to have a less-than sign when it compares the lengths where it’s
|
||||
supposed to have a greater-than sign:
|
||||
method by replacing the greater-than sign with a less-than sign when it
|
||||
compares the lengths:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
pub struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
}
|
||||
# #[derive(Debug)]
|
||||
# pub struct Rectangle {
|
||||
# length: u32,
|
||||
# width: u32,
|
||||
# }
|
||||
// --snip--
|
||||
|
||||
impl Rectangle {
|
||||
pub fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
@ -339,7 +346,7 @@ impl Rectangle {
|
||||
}
|
||||
```
|
||||
|
||||
Running the tests now produces:
|
||||
Running the tests now produces the following:
|
||||
|
||||
```text
|
||||
running 2 tests
|
||||
@ -350,35 +357,35 @@ failures:
|
||||
|
||||
---- tests::larger_can_hold_smaller stdout ----
|
||||
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
|
||||
larger.can_hold(&smaller)', src/lib.rs:22
|
||||
larger.can_hold(&smaller)', src/lib.rs:22:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
|
||||
failures:
|
||||
tests::larger_can_hold_smaller
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Our tests caught the bug! Since `larger.length` is 8 and `smaller.length` is 5,
|
||||
the comparison of the lengths in `can_hold` now returns `false` since 8 is not
|
||||
Our tests caught the bug! Because `larger.length` is 8 and `smaller.length` is
|
||||
5, the comparison of the lengths in `can_hold` now returns `false`: 8 is not
|
||||
less than 5.
|
||||
|
||||
### Testing Equality with the `assert_eq!` and `assert_ne!` Macros
|
||||
|
||||
A common way to test functionality is to take the result of the code under test
|
||||
and the value we expect the code to return and check that they’re equal. We
|
||||
A common way to test functionality is to compare the result of the code under
|
||||
test to the value we expect the code to return to make sure they’re equal. We
|
||||
could do this using the `assert!` macro and passing it an expression using the
|
||||
`==` operator. However, this is such a common test that the standard library
|
||||
provides a pair of macros to perform this test more conveniently: `assert_eq!`
|
||||
and `assert_ne!`. These macros compare two arguments for equality or
|
||||
inequality, respectively. They’ll also print out the two values if the
|
||||
assertion fails, so that it’s easier to see *why* the test failed, while the
|
||||
`assert!` macro only tells us that it got a `false` value for the `==`
|
||||
provides a pair of macros—`assert_eq!` and `assert_ne!`—to perform this test
|
||||
more conveniently. These macros compare two arguments for equality or
|
||||
inequality, respectively. They’ll also print the two values if the assertion
|
||||
fails, which makes it easier to see *why* the test failed; conversely, the
|
||||
`assert!` macro only indicates that it got a `false` value for the `==`
|
||||
expression, not the values that lead to the `false` value.
|
||||
|
||||
In Listing 11-7, let’s write a function named `add_two` that adds two to its
|
||||
parameter and returns the result. Then let’s test this function using the
|
||||
`assert_eq!` macro:
|
||||
In Listing 11-7, we write a function named `add_two` that adds `2` to its
|
||||
parameter and returns the result. Then we test this function using the
|
||||
`assert_eq!` macro.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -407,16 +414,16 @@ Let’s check that it passes!
|
||||
running 1 test
|
||||
test tests::it_adds_two ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
The first argument we gave to the `assert_eq!` macro, 4, is equal to the result
|
||||
of calling `add_two(2)`. We see a line for this test that says `test
|
||||
The first argument we gave to the `assert_eq!` macro, `4`, is equal to the
|
||||
result of calling `add_two(2)`. The line for this test is `test
|
||||
tests::it_adds_two ... ok`, and the `ok` text indicates that our test passed!
|
||||
|
||||
Let’s introduce a bug into our code to see what it looks like when a test that
|
||||
uses `assert_eq!` fails. Change the implementation of the `add_two` function to
|
||||
instead add 3:
|
||||
instead add `3`:
|
||||
|
||||
```rust
|
||||
pub fn add_two(a: i32) -> i32 {
|
||||
@ -424,7 +431,7 @@ pub fn add_two(a: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
And run the tests again:
|
||||
Run the tests again:
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
@ -433,62 +440,64 @@ test tests::it_adds_two ... FAILED
|
||||
failures:
|
||||
|
||||
---- tests::it_adds_two stdout ----
|
||||
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left ==
|
||||
right)` (left: `4`, right: `5`)', src/lib.rs:11
|
||||
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
|
||||
left: `4`,
|
||||
right: `5`', src/lib.rs:11:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
|
||||
failures:
|
||||
tests::it_adds_two
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Our test caught the bug! The `it_adds_two` test failed with the message ``
|
||||
assertion failed: `(left == right)` (left: `4`, right: `5`) ``. This message is
|
||||
useful and helps us get started debugging: it says the `left` argument to
|
||||
`assert_eq!` was 4, but the `right` argument, where we had `add_two(2)`, was 5.
|
||||
Our test caught the bug! The `it_adds_two` test failed, displaying the message
|
||||
`` assertion failed: `(left == right)` `` and showing that `left` was `4` and
|
||||
`right` was `5`. This message is useful and helps us start debugging: it means
|
||||
the `left` argument to `assert_eq!` was `4`, but the `right` argument, where we
|
||||
had `add_two(2)`, was `5`.
|
||||
|
||||
Note that in some languages and test frameworks, the parameters to the
|
||||
functions that assert two values are equal are called `expected` and `actual`
|
||||
functions that assert two values are equal are called `expected` and `actual`,
|
||||
and the order in which we specify the arguments matters. However, in Rust,
|
||||
they’re called `left` and `right` instead, and the order in which we specify
|
||||
the value we expect and the value that the code under test produces doesn’t
|
||||
matter. We could write the assertion in this test as
|
||||
`assert_eq!(add_two(2), 4)`, which would result in a failure message that says
|
||||
`` assertion failed: `(left == right)` (left: `5`, right: `4`) ``.
|
||||
they’re called `left` and `right`, and the order in which we specify the value
|
||||
we expect and the value that the code under test produces doesn’t matter. We
|
||||
could write the assertion in this test as `assert_eq!(add_two(2), 4)`, which
|
||||
would result in a failure message that displays `` assertion failed: `(left ==
|
||||
right)` `` and that `left` was `5` and `right` was `4`.
|
||||
|
||||
The `assert_ne!` macro will pass if the two values we give to it are not equal
|
||||
and fail if they are equal. This macro is most useful for cases when we’re not
|
||||
sure exactly what a value *will* be, but we know what the value definitely
|
||||
*won’t* be, if our code is functioning as we intend. For example, if we have a
|
||||
function that is guaranteed to change its input in some way, but the way in
|
||||
which the input is changed depends on the day of the week that we run our
|
||||
tests, the best thing to assert might be that the output of the function is not
|
||||
equal to the input.
|
||||
The `assert_ne!` macro will pass if the two values we give it are not equal and
|
||||
fail if they’re equal. This macro is most useful for cases when we’re not sure
|
||||
what a value *will* be, but we know what the value definitely *won’t* be if our
|
||||
code is functioning as we intend. For example, if we’re testing a function that
|
||||
is guaranteed to change its input in some way, but the way in which the input
|
||||
is changed depends on the day of the week that we run our tests, the best thing
|
||||
to assert might be that the output of the function is not equal to the input.
|
||||
|
||||
Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators
|
||||
`==` and `!=`, respectively. When the assertions fail, these macros print their
|
||||
arguments using debug formatting, which means the values being compared must
|
||||
implement the `PartialEq` and `Debug` traits. All of the primitive types and
|
||||
most of the standard library types implement these traits. For structs and
|
||||
enums that you define, you’ll need to implement `PartialEq` in order to be able
|
||||
to assert that values of those types are equal or not equal. You’ll need to
|
||||
implement `Debug` in order to be able to print out the values in the case that
|
||||
the assertion fails. Because both of these traits are derivable traits, as we
|
||||
mentioned in Chapter 5, this is usually as straightforward as adding the
|
||||
`#[derive(PartialEq, Debug)]` annotation to your struct or enum definition. See
|
||||
Appendix C for more details about these and other derivable traits.
|
||||
implement the `PartialEq` and `Debug` traits. All the primitive types and most
|
||||
of the standard library types implement these traits. For structs and enums
|
||||
that you define, you’ll need to implement `PartialEq` to assert that values of
|
||||
those types are equal or not equal. You’ll need to implement `Debug` to print
|
||||
out the values when the assertion fails. Because both traits are derivable
|
||||
traits, as mentioned in Listing 5-12 in Chapter 5, this is usually as
|
||||
straightforward as adding the `#[derive(PartialEq, Debug)]` annotation to your
|
||||
struct or enum definition. See Appendix C for more details about these and
|
||||
other derivable traits.
|
||||
|
||||
### Custom Failure Messages
|
||||
### Adding Custom Failure Messages
|
||||
|
||||
We can also add a custom message to be printed with the failure message as
|
||||
optional arguments to `assert!`, `assert_eq!`, and `assert_ne!`. Any arguments
|
||||
specified after the one required argument to `assert!` or the two required
|
||||
arguments to `assert_eq!` and `assert_ne!` are passed along to the `format!`
|
||||
macro that we talked about in Chapter 8, so you can pass a format string that
|
||||
contains `{}` placeholders and values to go in the placeholders. Custom
|
||||
messages are useful in order to document what an assertion means, so that when
|
||||
the test fails, we have a better idea of what the problem is with the code.
|
||||
optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any
|
||||
arguments specified after the one required argument to `assert!` or the two
|
||||
required arguments to `assert_eq!` and `assert_ne!` are passed along to the
|
||||
`format!` macro (discussed in Chapter 8 in the “Concatenation with the `+`
|
||||
Operator or the `format!` Macro” section), so you can pass a format string that
|
||||
contains `{}` placeholders and values to go in those placeholders. Custom
|
||||
messages are useful to document what an assertion means; when a test fails,
|
||||
we’ll have a better idea of what the problem is with the code.
|
||||
|
||||
For example, let’s say we have a function that greets people by name, and we
|
||||
want to test that the name we pass into the function appears in the output:
|
||||
@ -516,11 +525,11 @@ The requirements for this program haven’t been agreed upon yet, and we’re
|
||||
pretty sure the `Hello` text at the beginning of the greeting will change. We
|
||||
decided we don’t want to have to update the test for the name when that
|
||||
happens, so instead of checking for exact equality to the value returned from
|
||||
the `greeting` function, we’re just going to assert that the output contains
|
||||
the text of the input parameter.
|
||||
the `greeting` function, we’ll just assert that the output contains the text of
|
||||
the input parameter.
|
||||
|
||||
Let’s introduce a bug into this code to see what this test failure looks like,
|
||||
by changing `greeting` to not include `name`:
|
||||
Let’s introduce a bug into this code by changing `greeting` to not include
|
||||
`name` to see what this test failure looks like:
|
||||
|
||||
```rust
|
||||
pub fn greeting(name: &str) -> String {
|
||||
@ -528,7 +537,7 @@ pub fn greeting(name: &str) -> String {
|
||||
}
|
||||
```
|
||||
|
||||
Running this test produces:
|
||||
Running this test produces the following:
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
@ -538,18 +547,18 @@ failures:
|
||||
|
||||
---- tests::greeting_contains_name stdout ----
|
||||
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
||||
result.contains("Carol")', src/lib.rs:12
|
||||
result.contains("Carol")', src/lib.rs:12:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
|
||||
failures:
|
||||
tests::greeting_contains_name
|
||||
```
|
||||
|
||||
This just tells us that the assertion failed and which line the assertion is
|
||||
on. A more useful failure message in this case would print the value we did get
|
||||
from the `greeting` function. Let’s change the test function to have a custom
|
||||
failure message made from a format string with a placeholder filled in with the
|
||||
actual value we got from the `greeting` function:
|
||||
This result just indicates that the assertion failed and which line the
|
||||
assertion is on. A more useful failure message in this case would print the
|
||||
value we got from the `greeting` function. Let’s change the test function,
|
||||
giving it a custom failure message made from a format string with a placeholder
|
||||
filled in with the actual value we got from the `greeting` function:
|
||||
|
||||
```rust,ignore
|
||||
#[test]
|
||||
@ -562,12 +571,12 @@ fn greeting_contains_name() {
|
||||
}
|
||||
```
|
||||
|
||||
Now if we run the test again, we’ll get a much more informative error message:
|
||||
Now when we run the test, we’ll get a more informative error message:
|
||||
|
||||
```text
|
||||
---- tests::greeting_contains_name stdout ----
|
||||
thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain
|
||||
name, value was `Hello`', src/lib.rs:12
|
||||
thread 'tests::greeting_contains_name' panicked at 'Greeting did not
|
||||
contain name, value was `Hello!`', src/lib.rs:12:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
```
|
||||
|
||||
@ -578,18 +587,18 @@ debug what happened instead of what we were expecting to happen.
|
||||
|
||||
In addition to checking that our code returns the correct values we expect,
|
||||
it’s also important to check that our code handles error conditions as we
|
||||
expect. For example, consider the `Guess` type that we created in Chapter 9 in
|
||||
Listing 9-8. Other code that uses `Guess` is depending on the guarantee that
|
||||
`Guess` instances will only contain values between 1 and 100. We can write a
|
||||
test that ensures that attempting to create a `Guess` instance with a value
|
||||
outside that range panics.
|
||||
expect. For example, consider the `Guess` type that we created in Chapter 9,
|
||||
Listing 9-9. Other code that uses `Guess` depends on the guarantee that `Guess`
|
||||
instances will only contain values between 1 and 100. We can write a test that
|
||||
ensures that attempting to create a `Guess` instance with a value outside that
|
||||
range panics.
|
||||
|
||||
We can do this by adding another attribute, `should_panic`, to our test
|
||||
function. This attribute makes a test pass if the code inside the function
|
||||
panics, and the test will fail if the code inside the function doesn’t panic.
|
||||
We do this by adding another attribute, `should_panic`, to our test function.
|
||||
This attribute makes a test pass if the code inside the function panics; the
|
||||
test will fail if the code inside the function doesn’t panic.
|
||||
|
||||
Listing 11-8 shows how we’d write a test that checks the error conditions of
|
||||
`Guess::new` happen when we expect:
|
||||
Listing 11-8 shows a test that checks that the error conditions of `Guess::new`
|
||||
happen when we expect:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -625,18 +634,18 @@ mod tests {
|
||||
<span class="caption">Listing 11-8: Testing that a condition will cause a
|
||||
`panic!`</span>
|
||||
|
||||
The `#[should_panic]` attribute goes after the `#[test]` attribute and before
|
||||
the test function it applies to. Let’s see what it looks like when this test
|
||||
We place the `#[should_panic]` attribute after the `#[test]` attribute and
|
||||
before the test function it applies to. Let’s look at the result when this test
|
||||
passes:
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
test tests::greater_than_100 ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Looks good! Now let’s introduce a bug in our code, by removing the condition
|
||||
Looks good! Now let’s introduce a bug in our code by removing the condition
|
||||
that the `new` function will panic if the value is greater than 100:
|
||||
|
||||
```rust
|
||||
@ -644,6 +653,8 @@ that the `new` function will panic if the value is greater than 100:
|
||||
# value: u32,
|
||||
# }
|
||||
#
|
||||
// --snip--
|
||||
|
||||
impl Guess {
|
||||
pub fn new(value: u32) -> Guess {
|
||||
if value < 1 {
|
||||
@ -657,7 +668,7 @@ impl Guess {
|
||||
}
|
||||
```
|
||||
|
||||
If we run the test from Listing 11-8, it will fail:
|
||||
When we run the test in Listing 11-8, it will fail:
|
||||
|
||||
```text
|
||||
running 1 test
|
||||
@ -668,15 +679,14 @@ failures:
|
||||
failures:
|
||||
tests::greater_than_100
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
We don’t get a very helpful message in this case, but once we look at the test
|
||||
function, we can see that it’s annotated with `#[should_panic]`. The failure we
|
||||
got means that the code in the function, `Guess::new(200)`, did not cause a
|
||||
panic.
|
||||
We don’t get a very helpful message in this case, but when we look at the test
|
||||
function, we see that it’s annotated with `#[should_panic]`. The failure we got
|
||||
means that the code in the test function did not cause a panic.
|
||||
|
||||
`should_panic` tests can be imprecise, however, because they only tell us that
|
||||
Tests that use `should_panic` can be imprecise because they only indicate that
|
||||
the code has caused some panic. A `should_panic` test would pass even if the
|
||||
test panics for a different reason than the one we were expecting to happen. To
|
||||
make `should_panic` tests more precise, we can add an optional `expected`
|
||||
@ -688,9 +698,11 @@ different messages depending on whether the value was too small or too large:
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
pub struct Guess {
|
||||
value: u32,
|
||||
}
|
||||
# pub struct Guess {
|
||||
# value: u32,
|
||||
# }
|
||||
#
|
||||
// --snip--
|
||||
|
||||
impl Guess {
|
||||
pub fn new(value: u32) -> Guess {
|
||||
@ -723,14 +735,15 @@ mod tests {
|
||||
<span class="caption">Listing 11-9: Testing that a condition will cause a
|
||||
`panic!` with a particular panic message</span>
|
||||
|
||||
This test will pass, because the value we put in the `expected` parameter of
|
||||
the `should_panic` attribute is a substring of the message that the
|
||||
`Guess::new` function panics with. We could have specified the whole panic
|
||||
message that we expect, which in this case would be `Guess value must be less
|
||||
than or equal to 100, got 200.` It depends on how much of the panic message is
|
||||
unique or dynamic and how precise you want your test to be. In this case, a
|
||||
substring of the panic message is enough to ensure that the code in the
|
||||
function that gets run is the `else if value > 100` case.
|
||||
This test will pass because the value we put in the `should_panic` attribute’s
|
||||
`expected` parameter is a substring of the message that the `Guess::new`
|
||||
function panics with. We could have specified the entire panic message that we
|
||||
expect, which in this case would be `Guess value must be less than or equal to
|
||||
100, got 200.` What you choose to specify in the expected parameter for
|
||||
`should_panic` depends on how much of the panic message is unique or dynamic
|
||||
and how precise you want your test to be. In this case, a substring of the
|
||||
panic message is enough to ensure that the code in the test function executes
|
||||
the `else if value > 100` case.
|
||||
|
||||
To see what happens when a `should_panic` test with an `expected` message
|
||||
fails, let’s again introduce a bug into our code by swapping the bodies of the
|
||||
@ -753,8 +766,8 @@ test tests::greater_than_100 ... FAILED
|
||||
failures:
|
||||
|
||||
---- tests::greater_than_100 stdout ----
|
||||
thread 'tests::greater_than_100' panicked at 'Guess value must be greater
|
||||
than or equal to 1, got 200.', src/lib.rs:10
|
||||
thread 'tests::greater_than_100' panicked at 'Guess value must be
|
||||
greater than or equal to 1, got 200.', src/lib.rs:11:12
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
note: Panic did not include expected string 'Guess value must be less than or
|
||||
equal to 100'
|
||||
@ -762,15 +775,15 @@ equal to 100'
|
||||
failures:
|
||||
tests::greater_than_100
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
The failure message indicates that this test did indeed panic as we expected,
|
||||
but the panic message did not include expected string `'Guess value must be
|
||||
less than or equal to 100'`. We can see the panic message that we did get,
|
||||
which in this case was `Guess value must be greater than or equal to 1, got
|
||||
200.` We could then start figuring out where our bug was!
|
||||
but the panic message did not include the expected string `'Guess value must be
|
||||
less than or equal to 100'`. The panic message that we did get in this case was
|
||||
`Guess value must be greater than or equal to 1, got 200.` Now we can start
|
||||
figuring out where our bug is!
|
||||
|
||||
Now that we’ve gone over ways to write tests, let’s look at what is happening
|
||||
when we run our tests and talk about the different options we can use with
|
||||
`cargo test`.
|
||||
Now that you know several ways to write tests, let’s look at what is happening
|
||||
when we run our tests and explore the different options we can use with `cargo
|
||||
test`.
|
||||
|
@ -1,65 +1,63 @@
|
||||
## Controlling How Tests are Run
|
||||
## Controlling How Tests Are Run
|
||||
|
||||
Just as `cargo run` compiles your code and then runs the resulting binary,
|
||||
`cargo test` compiles your code in test mode and runs the resulting test
|
||||
binary. There are options you can use to change the default behavior of `cargo
|
||||
test`. For example, the default behavior of the binary produced by `cargo test`
|
||||
is to run all the tests in parallel and capture output generated during test
|
||||
runs, preventing it from being displayed to make it easier to read the output
|
||||
related to the test results. You can change this default behavior by specifying
|
||||
command line options.
|
||||
binary. You can specify command line options to change the default behavior of
|
||||
`cargo test`. For example, the default behavior of the binary produced by
|
||||
`cargo test` is to run all the tests in parallel and capture output generated
|
||||
during test runs, preventing the output from being displayed and making it
|
||||
easier to read the output related to the test results.
|
||||
|
||||
Some command line options can be passed to `cargo test`, and some need to be
|
||||
passed instead to the resulting test binary. To separate these two types of
|
||||
arguments, you list the arguments that go to `cargo test`, then the separator
|
||||
`--`, and then the arguments that go to the test binary. Running `cargo test
|
||||
--help` will tell you about the options that go with `cargo test`, and running
|
||||
`cargo test -- --help` will tell you about the options that go after the
|
||||
separator `--`.
|
||||
Some command line options go to `cargo test` and some go to the resulting test
|
||||
binary. To separate these two types of arguments, you list the arguments that
|
||||
go to `cargo test` followed by the separator `--` and then the arguments that
|
||||
go to the test binary. Running `cargo test --help` displays the options you can
|
||||
use with `cargo test`, and running `cargo test -- --help` displays the options
|
||||
you can use after the separator `--`.
|
||||
|
||||
### Running Tests in Parallel or Consecutively
|
||||
|
||||
When multiple tests are run, by default they run in parallel using threads.
|
||||
This means the tests will finish running faster, so that we can get faster
|
||||
feedback on whether or not our code is working. Since the tests are running at
|
||||
the same time, you should take care that your tests do not depend on each other
|
||||
or on any shared state, including a shared environment such as the current
|
||||
working directory or environment variables.
|
||||
When you run multiple tests, by default they run in parallel using threads.
|
||||
This means the tests will finish running faster so you can get feedback quicker
|
||||
on whether or not your code is working. Because the tests are running at the
|
||||
same time, make sure your tests don’t depend on each other or on any shared
|
||||
state, including a shared environment, such as the current working directory or
|
||||
environment variables.
|
||||
|
||||
For example, say each of your tests runs some code that creates a file on disk
|
||||
named `test-output.txt` and writes some data to that file. Then each test reads
|
||||
named *test-output.txt* and writes some data to that file. Then each test reads
|
||||
the data in that file and asserts that the file contains a particular value,
|
||||
which is different in each test. Because the tests are all run at the same
|
||||
time, one test might overwrite the file between when another test writes and
|
||||
reads the file. The second test will then fail, not because the code is
|
||||
incorrect, but because the tests have interfered with each other while running
|
||||
in parallel. One solution would be to make sure each test writes to a different
|
||||
file; another solution is to run the tests one at a time.
|
||||
which is different in each test. Because the tests run at the same time, one
|
||||
test might overwrite the file between when another test writes and reads the
|
||||
file. The second test will then fail, not because the code is incorrect, but
|
||||
because the tests have interfered with each other while running in parallel.
|
||||
One solution is to make sure each test writes to a different file; another
|
||||
solution is to run the tests one at a time.
|
||||
|
||||
If you don’t want to run the tests in parallel, or if you want more
|
||||
fine-grained control over the number of threads used, you can send the
|
||||
`--test-threads` flag and the number of threads you want to use to the test
|
||||
binary. For example:
|
||||
If you don’t want to run the tests in parallel or if you want more fine-grained
|
||||
control over the number of threads used, you can send the `--test-threads` flag
|
||||
and the number of threads you want to use to the test binary. Take a look at
|
||||
the following example:
|
||||
|
||||
```text
|
||||
$ cargo test -- --test-threads=1
|
||||
```
|
||||
|
||||
We set the number of test threads to 1, telling the program not to use any
|
||||
parallelism. This will take longer than running them in parallel, but the tests
|
||||
won’t be potentially interfering with each other if they share state.
|
||||
We set the number of test threads to `1`, telling the program not to use any
|
||||
parallelism. Running the tests using one thread will take longer than running
|
||||
them in parallel, but the tests won’t interfere with each other if they share
|
||||
state.
|
||||
|
||||
### Showing Function Output
|
||||
|
||||
By default, if a test passes, Rust’s test library captures anything printed to
|
||||
standard output. For example, if we call `println!` in a test and the test
|
||||
passes, we won’t see the `println!` output in the terminal: we’ll only see the
|
||||
line that says the test passed. If a test fails, we’ll see whatever was printed
|
||||
to standard output with the rest of the failure message.
|
||||
line that indicates the test passed. If a test fails, we’ll see whatever was
|
||||
printed to standard output with the rest of the failure message.
|
||||
|
||||
For example, Listing 11-10 has a silly function that prints out the value of
|
||||
its parameter and then returns 10. We then have a test that passes and a test
|
||||
that fails:
|
||||
As an example, Listing 11-10 has a silly function that prints the value of its
|
||||
parameter and returns 10, as well as a test that passes and a test that fails.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -90,7 +88,7 @@ mod tests {
|
||||
<span class="caption">Listing 11-10: Tests for a function that calls
|
||||
`println!`</span>
|
||||
|
||||
The output we’ll see when we run these tests with `cargo test` is:
|
||||
When we run these tests with `cargo test`, we’ll see the following output:
|
||||
|
||||
```text
|
||||
running 2 tests
|
||||
@ -101,38 +99,40 @@ failures:
|
||||
|
||||
---- tests::this_test_will_fail stdout ----
|
||||
I got the value 8
|
||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left ==
|
||||
right)` (left: `5`, right: `10`)', src/lib.rs:19
|
||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
||||
left: `5`,
|
||||
right: `10`', src/lib.rs:19:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
|
||||
failures:
|
||||
tests::this_test_will_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Note that nowhere in this output do we see `I got the value 4`, which is what
|
||||
gets printed when the test that passes runs. That output has been captured. The
|
||||
is printed when the test that passes runs. That output has been captured. The
|
||||
output from the test that failed, `I got the value 8`, appears in the section
|
||||
of the test summary output that also shows the cause of the test failure.
|
||||
of the test summary output, which also shows the cause of the test failure.
|
||||
|
||||
If we want to be able to see printed values for passing tests as well, the
|
||||
output capture behavior can be disabled by using the `--nocapture` flag:
|
||||
If we want to see printed values for passing tests as well, we can disable the
|
||||
output capture behavior by using the `--nocapture` flag:
|
||||
|
||||
```text
|
||||
$ cargo test -- --nocapture
|
||||
```
|
||||
|
||||
Running the tests from Listing 11-10 again with the `--nocapture` flag now
|
||||
shows:
|
||||
When we run the tests in Listing 11-10 again with the `--nocapture` flag, we
|
||||
see the following output:
|
||||
|
||||
```text
|
||||
running 2 tests
|
||||
I got the value 4
|
||||
I got the value 8
|
||||
test tests::this_test_will_pass ... ok
|
||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left ==
|
||||
right)` (left: `5`, right: `10`)', src/lib.rs:19
|
||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
||||
left: `5`,
|
||||
right: `10`', src/lib.rs:19:8
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
test tests::this_test_will_fail ... FAILED
|
||||
|
||||
@ -141,13 +141,13 @@ failures:
|
||||
failures:
|
||||
tests::this_test_will_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
Note that the output for the tests and the test results is interleaved; this is
|
||||
because the tests are running in parallel as we talked about in the previous
|
||||
section. Try using both the `--test-threads=1` option and the `--nocapture`
|
||||
flag and see what the output looks like then!
|
||||
Note that the output for the tests and the test results are interleaved; the
|
||||
reason is that the tests are running in parallel, as we talked about in the
|
||||
previous section. Try using the `--test-threads=1` option and the `--nocapture`
|
||||
flag, and see what the output looks like then!
|
||||
|
||||
### Running a Subset of Tests by Name
|
||||
|
||||
@ -157,7 +157,7 @@ that code. You can choose which tests to run by passing `cargo test` the name
|
||||
or names of the test(s) you want to run as an argument.
|
||||
|
||||
To demonstrate how to run a subset of tests, we’ll create three tests for our
|
||||
`add_two` function as shown in Listing 11-11 and choose which ones to run:
|
||||
`add_two` function, as shown in Listing 11-11, and choose which ones to run:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -187,10 +187,11 @@ mod tests {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 11-11: Three tests with a variety of names</span>
|
||||
<span class="caption">Listing 11-11: Three tests with three different
|
||||
names</span>
|
||||
|
||||
If we run the tests without passing any arguments, as we’ve already seen, all
|
||||
the tests will run in parallel:
|
||||
If we run the tests without passing any arguments, as we saw earlier, all the
|
||||
tests will run in parallel:
|
||||
|
||||
```text
|
||||
running 3 tests
|
||||
@ -198,7 +199,7 @@ test tests::add_two_and_two ... ok
|
||||
test tests::add_three_and_two ... ok
|
||||
test tests::one_hundred ... ok
|
||||
|
||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
#### Running Single Tests
|
||||
@ -213,17 +214,21 @@ $ cargo test one_hundred
|
||||
running 1 test
|
||||
test tests::one_hundred ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
|
||||
```
|
||||
|
||||
We can’t specify the names of multiple tests in this way, only the first value
|
||||
given to `cargo test` will be used.
|
||||
Only the test with the name `one_hundred` ran; the other two tests didn’t match
|
||||
that name. The test output lets us know we had more tests than what this
|
||||
command ran by displaying `2 filtered out` at the end of the summary line.
|
||||
|
||||
We can’t specify the names of multiple tests in this way; only the first value
|
||||
given to `cargo test` will be used. But there is a way to run multiple tests.
|
||||
|
||||
#### Filtering to Run Multiple Tests
|
||||
|
||||
However, we can specify part of a test name, and any test whose name matches
|
||||
that value will get run. For example, since two of our tests’ names contain
|
||||
`add`, we can run those two by running `cargo test add`:
|
||||
We can specify part of a test name, and any test whose name matches that value
|
||||
will be run. For example, because two of our tests’ names contain `add`, we can
|
||||
run those two by running `cargo test add`:
|
||||
|
||||
```text
|
||||
$ cargo test add
|
||||
@ -234,19 +239,21 @@ running 2 tests
|
||||
test tests::add_two_and_two ... ok
|
||||
test tests::add_three_and_two ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
|
||||
```
|
||||
|
||||
This ran all tests with `add` in the name. Also note that the module in which
|
||||
tests appear becomes part of the test’s name, so we can run all the tests in a
|
||||
module by filtering on the module’s name.
|
||||
This command ran all tests with `add` in the name name and filtered out the
|
||||
test named `one_hundred`. Also note that the module in which tests appear
|
||||
becomes part of the test’s name, so we can run all the tests in a module by
|
||||
filtering on the module’s name.
|
||||
|
||||
### Ignore Some Tests Unless Specifically Requested
|
||||
### Ignoring Some Tests Unless Specifically Requested
|
||||
|
||||
Sometimes a few specific tests can be very time-consuming to execute, so you
|
||||
might want to exclude them during most runs of `cargo test`. Rather than
|
||||
listing as arguments all tests you do want to run, we can instead annotate the
|
||||
time consuming tests with the `ignore` attribute to exclude them:
|
||||
listing as arguments all tests you do want to run, you can instead annotate the
|
||||
time-consuming tests using the `ignore` attribute to exclude them, as shown
|
||||
here:
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -263,9 +270,8 @@ fn expensive_test() {
|
||||
}
|
||||
```
|
||||
|
||||
We add the `#[ignore]` line to the test we want to exclude, after `#[test]`.
|
||||
Now if we run our tests, we’ll see `it_works` runs, but `expensive_test` does
|
||||
not:
|
||||
After `#[test]` we add the `#[ignore]` line to the test we want to exclude. Now
|
||||
when we run our tests, `it_works` runs, but `expensive_test` doesn’t:
|
||||
|
||||
```text
|
||||
$ cargo test
|
||||
@ -277,17 +283,11 @@ running 2 tests
|
||||
test expensive_test ... ignored
|
||||
test it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
`expensive_test` is listed as `ignored`. If we want to run only the ignored
|
||||
tests, we can ask for them to be run with `cargo test -- --ignored`:
|
||||
The `expensive_test` function is listed as `ignored`. If we want to run only
|
||||
the ignored tests, we can use `cargo test -- --ignored`:
|
||||
|
||||
```text
|
||||
$ cargo test -- --ignored
|
||||
@ -297,10 +297,10 @@ $ cargo test -- --ignored
|
||||
running 1 test
|
||||
test expensive_test ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
|
||||
```
|
||||
|
||||
By controlling which tests run, you can make sure your `cargo test` results
|
||||
will be fast. When you’re at a point that it makes sense to check the results
|
||||
of the `ignored` tests and you have time to wait for the results, you can
|
||||
choose to run `cargo test -- --ignored` instead.
|
||||
will be fast. When you’re at a point where it makes sense to check the results
|
||||
of the `ignored` tests and you have time to wait for the results, you can run
|
||||
`cargo test -- --ignored` instead.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user