mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-05 11:58:46 +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).
|
||||
@ -446,14 +468,14 @@ failed to run: ~/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo build --ma
|
||||
If you haven't used the `[patch]`
|
||||
section of `Cargo.toml` before, there is [some relevant documentation about it
|
||||
in the cargo docs](http://doc.crates.io/manifest.html#the-patch-section). In
|
||||
addition to that, you should read the
|
||||
addition to that, you should read the
|
||||
[Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies)
|
||||
section of the documentation as well.
|
||||
|
||||
Specifically, the following [section in Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#testing-a-bugfix) reveals what the problem is:
|
||||
|
||||
> Next up we need to ensure that our lock file is updated to use this new version of uuid so our project uses the locally checked out copy instead of one from crates.io. The way [patch] works is that it'll load the dependency at ../path/to/uuid and then whenever crates.io is queried for versions of uuid it'll also return the local version.
|
||||
>
|
||||
>
|
||||
> This means that the version number of the local checkout is significant and will affect whether the patch is used. Our manifest declared uuid = "1.0" which means we'll only resolve to >= 1.0.0, < 2.0.0, and Cargo's greedy resolution algorithm also means that we'll resolve to the maximum version within that range. Typically this doesn't matter as the version of the git repository will already be greater or match the maximum version published on crates.io, but it's important to keep this in mind!
|
||||
|
||||
This says that when we updated the submodule, the version number in our
|
||||
|
@ -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 {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
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 $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($path).default_condition(builder.build.config.extended)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure($name {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for Clippy {
|
||||
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)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Clippy {
|
||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||
target: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
||||
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\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if !status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status);
|
||||
}
|
||||
status.success()
|
||||
}
|
||||
|
||||
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
|
||||
4 | let first = &v[0];
|
||||
| - immutable borrow occurs here
|
||||
5 |
|
||||
6 | v.push(6);
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
6 | v.push(6);
|
||||
| ^ mutable borrow occurs here
|
||||
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
|
||||
@ -349,36 +356,36 @@ test tests::larger_can_hold_smaller ... FAILED
|
||||
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
|
||||
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
|
||||
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
|
||||
@ -537,19 +546,19 @@ test tests::greeting_contains_name ... FAILED
|
||||
failures:
|
||||
|
||||
---- tests::greeting_contains_name stdout ----
|
||||
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
||||
result.contains("Carol")', src/lib.rs:12
|
||||
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
||||
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
|
||||
@ -100,39 +98,41 @@ test tests::this_test_will_fail ... FAILED
|
||||
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
|
||||
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: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