mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-06 12:09:03 +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:
|
configuration used in the build process. Some options to note:
|
||||||
|
|
||||||
#### `[llvm]`:
|
#### `[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
|
- `ccache = true` - Use ccache when building llvm
|
||||||
|
|
||||||
#### `[build]`:
|
#### `[build]`:
|
||||||
- `compiler-docs = true` - Build compiler documentation
|
- `compiler-docs = true` - Build compiler documentation
|
||||||
|
|
||||||
#### `[rust]`:
|
#### `[rust]`:
|
||||||
- `debuginfo = true` - Build a compiler with debuginfo
|
- `debuginfo = true` - Build a compiler with debuginfo. Makes building rustc slower, but then you can use a debugger to debug `rustc`.
|
||||||
- `optimize = false` - Disable optimizations to speed up compilation of stage1 rust
|
- `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
|
For more options, the `config.toml` file contains commented out defaults, with
|
||||||
descriptions of what each option will do.
|
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
|
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.)
|
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]: #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
|
Speaking of tests, Rust has a comprehensive test suite. More information about
|
||||||
it can be found
|
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]: #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)
|
* [clippy](https://github.com/rust-lang-nursery/rust-clippy)
|
||||||
* [miri](https://github.com/solson/miri)
|
* [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
|
We allow breakage of these tools in the nightly channel. Maintainers of these
|
||||||
a pull request against the broken project asking to put the fix on a branch.
|
projects will be notified of the breakages and should fix them as soon as
|
||||||
Then you can disable the tool building via `src/tools/toolstate.toml`.
|
possible.
|
||||||
Once the branch containing your fix is likely to be merged, you can point
|
|
||||||
the affected submodule at this branch.
|
|
||||||
|
|
||||||
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
|
git add path/to/submodule
|
||||||
```
|
```
|
||||||
|
|
||||||
outside the 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
|
`./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`
|
there, you may wish to set `submodules = false` in the `config.toml`
|
||||||
to prevent `x.py` from resetting to the original branch.
|
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]: #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
|
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.
|
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)
|
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 you mark the tools as "broken",
|
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
|
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
|
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
|
and the tools are working again, you go back in the compiler and update the tools
|
||||||
from "broken".
|
so they can be distributed again.
|
||||||
|
|
||||||
This should avoid a bunch of synchronization dances and is also much easier on contributors as
|
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.
|
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
|
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
|
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.
|
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",
|
5. Wait for your PR to merge.
|
||||||
that will disable building it on CI. See the documentation in that file for the exact
|
6. Wait for a nightly
|
||||||
configuration values you can use.
|
7. (optional) Help land your PR on the upstream repository now that your changes are in nightly.
|
||||||
6. Commit the changes to `src/tools/toolstate.toml`, **do not update submodules in your commit**,
|
8. (optional) Send a PR to rust-lang/rust updating the submodule.
|
||||||
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.
|
|
||||||
|
|
||||||
#### Updating submodules
|
#### Updating submodules
|
||||||
[updating-submodules]: #updating-submodules
|
[updating-submodules]: #updating-submodules
|
||||||
|
|
||||||
These instructions are specific to updating `rustfmt`, however they may apply
|
These instructions are specific to updating `rustfmt`, however they may apply
|
||||||
to the other submodules as well. Please help by improving these instructions
|
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
|
To update the `rustfmt` submodule, start by running the appropriate
|
||||||
[`git submodule` command](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
|
[`git submodule` command](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
|
||||||
|
@ -129,6 +129,9 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.
|
|||||||
python x.py build
|
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]: #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
|
Language
|
||||||
--------
|
--------
|
||||||
- [`non_snake_case` lint now allows extern no-mangle functions][44966]
|
- [`non_snake_case` lint now allows extern no-mangle functions][44966]
|
||||||
- [Now accepts underscores in unicode escapes][43716]
|
- [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;`
|
- [`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]
|
- [types that impl `Drop` are now allowed in `const` and `static` types][44456]
|
||||||
|
|
||||||
@ -45,8 +269,8 @@ Cargo
|
|||||||
Misc
|
Misc
|
||||||
----
|
----
|
||||||
- [`libbacktrace` is now available on Apple platforms.][44251]
|
- [`libbacktrace` is now available on Apple platforms.][44251]
|
||||||
- [Stabilised the `compile_fail` attribute for code fences.][43949] This now
|
- [Stabilised the `compile_fail` attribute for code fences in doc-comments.][43949]
|
||||||
lets you specify that a given code example will fail to compile.
|
This now lets you specify that a given code example will fail to compile.
|
||||||
|
|
||||||
Compatibility Notes
|
Compatibility Notes
|
||||||
-------------------
|
-------------------
|
||||||
@ -624,7 +848,7 @@ Misc
|
|||||||
----
|
----
|
||||||
|
|
||||||
- [rustdoc can now use pulldown-cmark with the `--enable-commonmark` flag][40338]
|
- [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]
|
- [Rust now uses the official cross compiler for NetBSD][40612]
|
||||||
- [rustdoc now accepts `#` at the start of files][40828]
|
- [rustdoc now accepts `#` at the start of files][40828]
|
||||||
- [Fixed jemalloc support for musl][41168]
|
- [Fixed jemalloc support for musl][41168]
|
||||||
@ -1658,7 +1882,7 @@ Diagnostics
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
* [Replace macro backtraces with labeled local uses][35702]
|
* [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]
|
* [Buffer unix and lock windows to prevent message interleaving][35975]
|
||||||
* [Update lifetime errors to specifically note temporaries][36171]
|
* [Update lifetime errors to specifically note temporaries][36171]
|
||||||
* [Special case a few colors for Windows][36178]
|
* [Special case a few colors for Windows][36178]
|
||||||
@ -1966,7 +2190,7 @@ Language
|
|||||||
useful](https://github.com/rust-lang/rust/pull/34908)
|
useful](https://github.com/rust-lang/rust/pull/34908)
|
||||||
* [`macro_rules!` `stmt` matchers correctly consume the entire contents when
|
* [`macro_rules!` `stmt` matchers correctly consume the entire contents when
|
||||||
inside non-braces invocations](https://github.com/rust-lang/rust/pull/34886)
|
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)
|
`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)
|
* [`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)
|
* [`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!`
|
* [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)
|
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.
|
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).
|
* [`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.
|
This causes breakage in some corner cases.
|
||||||
@ -3348,7 +3572,7 @@ Libraries
|
|||||||
* `FromStr` is [implemented for `SockAddrV4` and `SockAddrV6`][1.5s].
|
* `FromStr` is [implemented for `SockAddrV4` and `SockAddrV6`][1.5s].
|
||||||
* There are now `From` conversions [between floating point
|
* There are now `From` conversions [between floating point
|
||||||
types][1.5f] where the conversions are lossless.
|
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.
|
the conversions are lossless.
|
||||||
* [`fs::Metadata` implements `Clone`][1.5fs].
|
* [`fs::Metadata` implements `Clone`][1.5fs].
|
||||||
* The `parse` method [accepts a leading "+" when parsing
|
* The `parse` method [accepts a leading "+" when parsing
|
||||||
@ -3548,7 +3772,7 @@ Libraries
|
|||||||
* [`IntoIterator` is implemented for references to `Option` and
|
* [`IntoIterator` is implemented for references to `Option` and
|
||||||
`Result`][into2].
|
`Result`][into2].
|
||||||
* [`HashMap` and `HashSet` implement `Extend<&T>` where `T:
|
* [`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.
|
breakage in rare situations.
|
||||||
* [`BinaryHeap` implements `Debug`][bh2].
|
* [`BinaryHeap` implements `Debug`][bh2].
|
||||||
* [`Borrow` and `BorrowMut` are implemented for fixed-size
|
* [`Borrow` and `BorrowMut` are implemented for fixed-size
|
||||||
@ -3559,7 +3783,7 @@ Libraries
|
|||||||
* `&mut T` where `T: std::fmt::Write` [also implements
|
* `&mut T` where `T: std::fmt::Write` [also implements
|
||||||
`std::fmt::Write`][mutw].
|
`std::fmt::Write`][mutw].
|
||||||
* [A stable regression in `VecDeque::push_back` and other
|
* [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].
|
was fixed][vd].
|
||||||
* [Function pointers implement traits for up to 12 parameters][fp2].
|
* [Function pointers implement traits for up to 12 parameters][fp2].
|
||||||
|
|
||||||
@ -3746,7 +3970,7 @@ Libraries
|
|||||||
[better for long data][sh].
|
[better for long data][sh].
|
||||||
* [`AtomicPtr`] implements [`Send`].
|
* [`AtomicPtr`] implements [`Send`].
|
||||||
* The [`read_to_end`] implementations for [`Stdin`] and [`File`]
|
* 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].
|
performance][rte].
|
||||||
* Lifetime parameters of foreign functions [are now resolved
|
* Lifetime parameters of foreign functions [are now resolved
|
||||||
properly][f].
|
properly][f].
|
||||||
@ -3875,7 +4099,7 @@ Highlights
|
|||||||
* This is the first release with [experimental support for linking
|
* This is the first release with [experimental support for linking
|
||||||
with the MSVC linker and lib C on Windows (instead of using the GNU
|
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
|
variants via MinGW)][win]. It is yet recommended only for the most
|
||||||
intrepid Rusticians.
|
intrepid Rustaceans.
|
||||||
* Benchmark compilations are showing a 30% improvement in
|
* Benchmark compilations are showing a 30% improvement in
|
||||||
bootstrapping over 1.1.
|
bootstrapping over 1.1.
|
||||||
|
|
||||||
@ -4741,7 +4965,7 @@ Version 0.11.0 (2014-07-02)
|
|||||||
* Libraries
|
* Libraries
|
||||||
* The standard library is now a "facade" over a number of underlying
|
* The standard library is now a "facade" over a number of underlying
|
||||||
libraries. This means that development on the standard library should
|
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.
|
all dependencies.
|
||||||
* A new library, libcore, lives under the standard library's facade
|
* A new library, libcore, lives under the standard library's facade
|
||||||
which is Rust's "0-assumption" library, suitable for embedded and
|
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
|
# 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
|
# 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
|
# 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
|
# on them will not work unless the user opts in to building them. By default the
|
||||||
# experimental LLVM targets include WebAssembly for the
|
# `WebAssembly` target is enabled when compiling LLVM from scratch.
|
||||||
# wasm32-experimental-emscripten Rust target.
|
#experimental-targets = "WebAssembly"
|
||||||
#experimental-targets = ""
|
|
||||||
|
|
||||||
# Cap the number of parallel linker invocations when compiling LLVM.
|
# Cap the number of parallel linker invocations when compiling LLVM.
|
||||||
# This can be useful when building LLVM with debug info, which significantly
|
# 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.
|
# As a side-effect also generates MIR for all libraries.
|
||||||
#test-miri = false
|
#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
|
# 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/rustdoc",
|
||||||
"tools/rls",
|
"tools/rls",
|
||||||
"tools/rustfmt",
|
"tools/rustfmt",
|
||||||
|
"tools/miri",
|
||||||
# FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude
|
# 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/borrow_error",
|
||||||
"tools/rls/test_data/common",
|
"tools/rls/test_data/common",
|
||||||
|
"tools/rls/test_data/deglob",
|
||||||
"tools/rls/test_data/features",
|
"tools/rls/test_data/features",
|
||||||
"tools/rls/test_data/find_all_refs_no_cfg_test",
|
"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/find_impls",
|
||||||
"tools/rls/test_data/infer_bin",
|
"tools/rls/test_data/infer_bin",
|
||||||
"tools/rls/test_data/infer_custom_bin",
|
"tools/rls/test_data/infer_custom_bin",
|
||||||
"tools/rls/test_data/infer_lib",
|
"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/workspace_symbol",
|
||||||
"tools/rls/test_data/deglob",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Curiously, compiletest will segfault if compiled with opt-level=3 on 64-bit
|
# 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"
|
serde_json = "1.0.2"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
|
time = "0.1"
|
||||||
|
@ -175,7 +175,7 @@ fn main() {
|
|||||||
if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
|
if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
|
||||||
cmd.arg("-C").arg(format!("codegen-units={}", s));
|
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");
|
cmd.arg("-Ccodegen-units=16").arg("-Zthinlto");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,8 @@ fn main() {
|
|||||||
if env::var("RUSTC_SAVE_ANALYSIS") == Ok("api".to_string()) {
|
if env::var("RUSTC_SAVE_ANALYSIS") == Ok("api".to_string()) {
|
||||||
cmd.arg("-Zsave-analysis");
|
cmd.arg("-Zsave-analysis");
|
||||||
cmd.env("RUST_SAVE_ANALYSIS_CONFIG",
|
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}");
|
\"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
|
// When running miri tests, we need to generate MIR for all libraries
|
||||||
if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
|
if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
|
||||||
cmd.arg("-Zalways-encode-mir");
|
cmd.arg("-Zalways-encode-mir");
|
||||||
|
if stage != "0" {
|
||||||
|
cmd.arg("-Zmiri");
|
||||||
|
}
|
||||||
cmd.arg("-Zmir-emit-validate=1");
|
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") {
|
let color = match env::var("RUSTC_COLOR") {
|
||||||
Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
|
Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
|
@ -57,6 +57,10 @@ fn main() {
|
|||||||
// This "unstable-options" can be removed when `--crate-version` is stabilized
|
// This "unstable-options" can be removed when `--crate-version` is stabilized
|
||||||
cmd.arg("-Z").arg("unstable-options")
|
cmd.arg("-Z").arg("unstable-options")
|
||||||
.arg("--crate-version").arg(version);
|
.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() {
|
std::process::exit(match cmd.status() {
|
||||||
|
@ -484,8 +484,8 @@ impl<'a> Builder<'a> {
|
|||||||
} else {
|
} else {
|
||||||
PathBuf::from("/path/to/nowhere/rustdoc/not/required")
|
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 {
|
if let Some(n) = self.config.rust_codegen_units {
|
||||||
cargo.env("RUSTC_CODEGEN_UNITS", n.to_string());
|
cargo.env("RUSTC_CODEGEN_UNITS", n.to_string());
|
||||||
}
|
}
|
||||||
@ -500,9 +500,10 @@ impl<'a> Builder<'a> {
|
|||||||
if mode != Mode::Tool {
|
if mode != Mode::Tool {
|
||||||
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
// Tools don't get debuginfo right now, e.g. cargo and rls don't
|
||||||
// get compiled with debuginfo.
|
// get compiled with debuginfo.
|
||||||
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
|
// Adding debuginfo increases their sizes by a factor of 3-4.
|
||||||
.env("RUSTC_DEBUGINFO_LINES", self.config.rust_debuginfo_lines.to_string())
|
cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
|
||||||
.env("RUSTC_FORCE_UNSTABLE", "1");
|
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
|
// Currently the compiler depends on crates from crates.io, and
|
||||||
// then other crates can depend on the compiler (e.g. proc-macro
|
// then other crates can depend on the compiler (e.g. proc-macro
|
||||||
@ -625,9 +626,7 @@ impl<'a> Builder<'a> {
|
|||||||
cargo.arg("--release");
|
cargo.arg("--release");
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode != Mode::Libstd && // FIXME(#45320)
|
if self.config.rust_codegen_units.is_none() &&
|
||||||
mode != Mode::Libtest && // FIXME(#45511)
|
|
||||||
self.config.rust_codegen_units.is_none() &&
|
|
||||||
self.build.is_rust_llvm(compiler.host)
|
self.build.is_rust_llvm(compiler.host)
|
||||||
{
|
{
|
||||||
cargo.env("RUSTC_THINLTO", "1");
|
cargo.env("RUSTC_THINLTO", "1");
|
||||||
|
@ -24,12 +24,7 @@ use Build;
|
|||||||
use config::Config;
|
use config::Config;
|
||||||
|
|
||||||
// The version number
|
// The version number
|
||||||
pub const CFG_RELEASE_NUM: &str = "1.23.0";
|
pub const CFG_RELEASE_NUM: &str = "1.24.1";
|
||||||
|
|
||||||
// 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 struct GitInfo {
|
pub struct GitInfo {
|
||||||
inner: Option<Info>,
|
inner: Option<Info>,
|
||||||
|
@ -23,7 +23,7 @@ use std::path::{PathBuf, Path};
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use build_helper::{self, output, BuildExpectation};
|
use build_helper::{self, output};
|
||||||
|
|
||||||
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
|
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
|
||||||
use cache::{INTERNER, Interned};
|
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.fail_fast {
|
||||||
if !build.try_run(cmd, expect) {
|
if !build.try_run(cmd) {
|
||||||
let mut failures = build.delayed_failures.borrow_mut();
|
let mut failures = build.delayed_failures.borrow_mut();
|
||||||
failures.push(format!("{:?}", cmd));
|
failures.push(format!("{:?}", cmd));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
build.run_expecting(cmd, expect);
|
build.run(cmd);
|
||||||
}
|
}
|
||||||
}
|
true
|
||||||
|
|
||||||
fn try_run(build: &Build, cmd: &mut Command) {
|
|
||||||
try_run_expecting(build, cmd, BuildExpectation::None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_run_quiet(build: &Build, cmd: &mut Command) {
|
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);
|
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||||
|
|
||||||
try_run_expecting(
|
if try_run(build, &mut cargo) {
|
||||||
build,
|
build.save_toolstate("rls", ToolState::TestPass);
|
||||||
&mut cargo,
|
}
|
||||||
builder.build.config.toolstate.rls.passes(ToolState::Testing),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,16 +301,15 @@ impl Step for Rustfmt {
|
|||||||
|
|
||||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||||
|
|
||||||
try_run_expecting(
|
if try_run(build, &mut cargo) {
|
||||||
build,
|
build.save_toolstate("rustfmt", ToolState::TestPass);
|
||||||
&mut cargo,
|
}
|
||||||
builder.build.config.toolstate.rustfmt.passes(ToolState::Testing),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Miri {
|
pub struct Miri {
|
||||||
|
stage: u32,
|
||||||
host: Interned<String>,
|
host: Interned<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +325,7 @@ impl Step for Miri {
|
|||||||
|
|
||||||
fn make_run(run: RunConfig) {
|
fn make_run(run: RunConfig) {
|
||||||
run.builder.ensure(Miri {
|
run.builder.ensure(Miri {
|
||||||
|
stage: run.builder.top_stage,
|
||||||
host: run.target,
|
host: run.target,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -337,8 +333,9 @@ impl Step for Miri {
|
|||||||
/// Runs `cargo test` for miri.
|
/// Runs `cargo test` for miri.
|
||||||
fn run(self, builder: &Builder) {
|
fn run(self, builder: &Builder) {
|
||||||
let build = builder.build;
|
let build = builder.build;
|
||||||
|
let stage = self.stage;
|
||||||
let host = self.host;
|
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 }) {
|
if let Some(miri) = builder.ensure(tool::Miri { compiler, target: self.host }) {
|
||||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
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);
|
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||||
|
|
||||||
try_run_expecting(
|
if try_run(build, &mut cargo) {
|
||||||
build,
|
build.save_toolstate("miri", ToolState::TestPass);
|
||||||
&mut cargo,
|
}
|
||||||
builder.build.config.toolstate.miri.passes(ToolState::Testing),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("failed to test miri: could not build");
|
eprintln!("failed to test miri: could not build");
|
||||||
}
|
}
|
||||||
@ -411,11 +406,9 @@ impl Step for Clippy {
|
|||||||
|
|
||||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||||
|
|
||||||
try_run_expecting(
|
if try_run(build, &mut cargo) {
|
||||||
build,
|
build.save_toolstate("clippy-driver", ToolState::TestPass);
|
||||||
&mut cargo,
|
}
|
||||||
builder.build.config.toolstate.clippy.passes(ToolState::Testing),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("failed to test clippy: could not build");
|
eprintln!("failed to test clippy: could not build");
|
||||||
}
|
}
|
||||||
@ -575,6 +568,11 @@ static HOST_COMPILETESTS: &[Test] = &[
|
|||||||
mode: "compile-fail",
|
mode: "compile-fail",
|
||||||
suite: "compile-fail-fulldeps",
|
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/run-make", mode: "run-make", suite: "run-make" },
|
||||||
Test { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" },
|
Test { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" },
|
||||||
|
|
||||||
@ -756,6 +754,7 @@ impl Step for Compiletest {
|
|||||||
if build.config.rust_debuginfo_tests {
|
if build.config.rust_debuginfo_tests {
|
||||||
flags.push("-g".to_string());
|
flags.push("-g".to_string());
|
||||||
}
|
}
|
||||||
|
flags.push("-Zmiri -Zunstable-options".to_string());
|
||||||
|
|
||||||
if let Some(linker) = build.linker(target) {
|
if let Some(linker) = build.linker(target) {
|
||||||
cmd.arg("--linker").arg(linker);
|
cmd.arg("--linker").arg(linker);
|
||||||
@ -981,7 +980,8 @@ impl Step for ErrorIndex {
|
|||||||
build.run(builder.tool_cmd(Tool::ErrorIndex)
|
build.run(builder.tool_cmd(Tool::ErrorIndex)
|
||||||
.arg("markdown")
|
.arg("markdown")
|
||||||
.arg(&output)
|
.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);
|
markdown_test(builder, compiler, &output);
|
||||||
}
|
}
|
||||||
@ -1245,6 +1245,17 @@ impl Step for Crate {
|
|||||||
if target.contains("emscripten") {
|
if target.contains("emscripten") {
|
||||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||||
build.config.nodejs.as_ref().expect("nodejs not configured"));
|
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) {
|
} else if build.remote_tested(target) {
|
||||||
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
|
||||||
format!("{} run",
|
format!("{} run",
|
||||||
|
@ -550,6 +550,7 @@ pub fn rustc_cargo(build: &Build,
|
|||||||
// Building with a static libstdc++ is only supported on linux right now,
|
// Building with a static libstdc++ is only supported on linux right now,
|
||||||
// not for MSVC or macOS
|
// not for MSVC or macOS
|
||||||
if build.config.llvm_static_stdcpp &&
|
if build.config.llvm_static_stdcpp &&
|
||||||
|
!target.contains("freebsd") &&
|
||||||
!target.contains("windows") &&
|
!target.contains("windows") &&
|
||||||
!target.contains("apple") {
|
!target.contains("apple") {
|
||||||
cargo.env("LLVM_STATIC_STDCPP",
|
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 {
|
if let Some(ref s) = build.config.rustc_default_linker {
|
||||||
cargo.env("CFG_DEFAULT_LINKER", s);
|
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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -27,7 +27,6 @@ use util::exe;
|
|||||||
use cache::{INTERNER, Interned};
|
use cache::{INTERNER, Interned};
|
||||||
use flags::Flags;
|
use flags::Flags;
|
||||||
pub use flags::Subcommand;
|
pub use flags::Subcommand;
|
||||||
use toolstate::ToolStates;
|
|
||||||
|
|
||||||
/// Global configuration for the entire build and/or bootstrap.
|
/// Global configuration for the entire build and/or bootstrap.
|
||||||
///
|
///
|
||||||
@ -76,7 +75,7 @@ pub struct Config {
|
|||||||
pub llvm_static_stdcpp: bool,
|
pub llvm_static_stdcpp: bool,
|
||||||
pub llvm_link_shared: bool,
|
pub llvm_link_shared: bool,
|
||||||
pub llvm_targets: Option<String>,
|
pub llvm_targets: Option<String>,
|
||||||
pub llvm_experimental_targets: Option<String>,
|
pub llvm_experimental_targets: String,
|
||||||
pub llvm_link_jobs: Option<u32>,
|
pub llvm_link_jobs: Option<u32>,
|
||||||
|
|
||||||
// rust codegen options
|
// rust codegen options
|
||||||
@ -87,6 +86,7 @@ pub struct Config {
|
|||||||
pub rust_debuginfo_lines: bool,
|
pub rust_debuginfo_lines: bool,
|
||||||
pub rust_debuginfo_only_std: bool,
|
pub rust_debuginfo_only_std: bool,
|
||||||
pub rust_rpath: bool,
|
pub rust_rpath: bool,
|
||||||
|
pub rustc_parallel_queries: bool,
|
||||||
pub rustc_default_linker: Option<String>,
|
pub rustc_default_linker: Option<String>,
|
||||||
pub rust_optimize_tests: bool,
|
pub rust_optimize_tests: bool,
|
||||||
pub rust_debuginfo_tests: bool,
|
pub rust_debuginfo_tests: bool,
|
||||||
@ -112,6 +112,8 @@ pub struct Config {
|
|||||||
pub channel: String,
|
pub channel: String,
|
||||||
pub quiet_tests: bool,
|
pub quiet_tests: bool,
|
||||||
pub test_miri: bool,
|
pub test_miri: bool,
|
||||||
|
pub save_toolstates: Option<PathBuf>,
|
||||||
|
|
||||||
// Fallback musl-root for all targets
|
// Fallback musl-root for all targets
|
||||||
pub musl_root: Option<PathBuf>,
|
pub musl_root: Option<PathBuf>,
|
||||||
pub prefix: 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.
|
// These are either the stage0 downloaded binaries or the locally installed ones.
|
||||||
pub initial_cargo: PathBuf,
|
pub initial_cargo: PathBuf,
|
||||||
pub initial_rustc: PathBuf,
|
pub initial_rustc: PathBuf,
|
||||||
|
|
||||||
pub toolstate: ToolStates,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Per-target configuration stored in the global configuration structure.
|
/// Per-target configuration stored in the global configuration structure.
|
||||||
@ -264,6 +264,7 @@ struct Rust {
|
|||||||
debuginfo: Option<bool>,
|
debuginfo: Option<bool>,
|
||||||
debuginfo_lines: Option<bool>,
|
debuginfo_lines: Option<bool>,
|
||||||
debuginfo_only_std: Option<bool>,
|
debuginfo_only_std: Option<bool>,
|
||||||
|
experimental_parallel_queries: Option<bool>,
|
||||||
debug_jemalloc: Option<bool>,
|
debug_jemalloc: Option<bool>,
|
||||||
use_jemalloc: Option<bool>,
|
use_jemalloc: Option<bool>,
|
||||||
backtrace: Option<bool>,
|
backtrace: Option<bool>,
|
||||||
@ -279,6 +280,7 @@ struct Rust {
|
|||||||
dist_src: Option<bool>,
|
dist_src: Option<bool>,
|
||||||
quiet_tests: Option<bool>,
|
quiet_tests: Option<bool>,
|
||||||
test_miri: Option<bool>,
|
test_miri: Option<bool>,
|
||||||
|
save_toolstates: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TOML representation of how each build target is configured.
|
/// TOML representation of how each build target is configured.
|
||||||
@ -343,18 +345,6 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}).unwrap_or_else(|| TomlConfig::default());
|
}).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());
|
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, build.build.clone().map(|x| INTERNER.intern_string(x)));
|
||||||
set(&mut config.build, flags.build);
|
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_static_stdcpp, llvm.static_libstdcpp);
|
||||||
set(&mut config.llvm_link_shared, llvm.link_shared);
|
set(&mut config.llvm_link_shared, llvm.link_shared);
|
||||||
config.llvm_targets = llvm.targets.clone();
|
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;
|
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.rust_dist_src, rust.dist_src);
|
||||||
set(&mut config.quiet_tests, rust.quiet_tests);
|
set(&mut config.quiet_tests, rust.quiet_tests);
|
||||||
set(&mut config.test_miri, rust.test_miri);
|
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.rustc_default_linker = rust.default_linker.clone();
|
||||||
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
|
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
|
||||||
|
config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from);
|
||||||
|
|
||||||
match rust.codegen_units {
|
match rust.codegen_units {
|
||||||
Some(0) => config.rust_codegen_units = Some(num_cpus::get() as u32),
|
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-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
|
||||||
o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
|
o("debuginfo-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")
|
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("prefix", "install.prefix", "set installation prefix")
|
||||||
v("localstatedir", "install.localstatedir", "local state directory")
|
v("localstatedir", "install.localstatedir", "local state directory")
|
||||||
|
@ -28,11 +28,12 @@ use build_helper::output;
|
|||||||
|
|
||||||
use {Build, Compiler, Mode};
|
use {Build, Compiler, Mode};
|
||||||
use channel;
|
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 builder::{Builder, RunConfig, ShouldRun, Step};
|
||||||
use compile;
|
use compile;
|
||||||
use tool::{self, Tool};
|
use tool::{self, Tool};
|
||||||
use cache::{INTERNER, Interned};
|
use cache::{INTERNER, Interned};
|
||||||
|
use time;
|
||||||
|
|
||||||
pub fn pkgname(build: &Build, component: &str) -> String {
|
pub fn pkgname(build: &Build, component: &str) -> String {
|
||||||
if component == "cargo" {
|
if component == "cargo" {
|
||||||
@ -434,7 +435,21 @@ impl Step for Rustc {
|
|||||||
|
|
||||||
// Man pages
|
// Man pages
|
||||||
t!(fs::create_dir_all(image.join("share/man/man1")));
|
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
|
// Debugger scripts
|
||||||
builder.ensure(DebuggerScripts {
|
builder.ensure(DebuggerScripts {
|
||||||
@ -489,6 +504,7 @@ impl Step for DebuggerScripts {
|
|||||||
install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"),
|
install(&build.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"),
|
||||||
0o755);
|
0o755);
|
||||||
|
|
||||||
|
cp_debugger_script("natvis/intrinsic.natvis");
|
||||||
cp_debugger_script("natvis/liballoc.natvis");
|
cp_debugger_script("natvis/liballoc.natvis");
|
||||||
cp_debugger_script("natvis/libcore.natvis");
|
cp_debugger_script("natvis/libcore.natvis");
|
||||||
} else {
|
} else {
|
||||||
@ -1061,11 +1077,6 @@ impl Step for Rls {
|
|||||||
let target = self.target;
|
let target = self.target;
|
||||||
assert!(build.config.extended);
|
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);
|
println!("Dist RLS stage{} ({})", stage, target);
|
||||||
let src = build.src.join("src/tools/rls");
|
let src = build.src.join("src/tools/rls");
|
||||||
let release_num = build.release_num("rls");
|
let release_num = build.release_num("rls");
|
||||||
@ -1083,7 +1094,8 @@ impl Step for Rls {
|
|||||||
let rls = builder.ensure(tool::Rls {
|
let rls = builder.ensure(tool::Rls {
|
||||||
compiler: builder.compiler(stage, build.build),
|
compiler: builder.compiler(stage, build.build),
|
||||||
target
|
target
|
||||||
}).expect("Rls to build: toolstate is testing");
|
}).or_else(|| { println!("Unable to build RLS, skipping dist"); None })?;
|
||||||
|
|
||||||
install(&rls, &image.join("bin"), 0o755);
|
install(&rls, &image.join("bin"), 0o755);
|
||||||
let doc = image.join("share/doc/rls");
|
let doc = image.join("share/doc/rls");
|
||||||
install(&src.join("README.md"), &doc, 0o644);
|
install(&src.join("README.md"), &doc, 0o644);
|
||||||
@ -1147,11 +1159,6 @@ impl Step for Rustfmt {
|
|||||||
let target = self.target;
|
let target = self.target;
|
||||||
assert!(build.config.extended);
|
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);
|
println!("Dist Rustfmt stage{} ({})", stage, target);
|
||||||
let src = build.src.join("src/tools/rustfmt");
|
let src = build.src.join("src/tools/rustfmt");
|
||||||
let release_num = build.release_num("rustfmt");
|
let release_num = build.release_num("rustfmt");
|
||||||
@ -1167,8 +1174,14 @@ impl Step for Rustfmt {
|
|||||||
let rustfmt = builder.ensure(tool::Rustfmt {
|
let rustfmt = builder.ensure(tool::Rustfmt {
|
||||||
compiler: builder.compiler(stage, build.build),
|
compiler: builder.compiler(stage, build.build),
|
||||||
target
|
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(&rustfmt, &image.join("bin"), 0o755);
|
||||||
|
install(&cargofmt, &image.join("bin"), 0o755);
|
||||||
let doc = image.join("share/doc/rustfmt");
|
let doc = image.join("share/doc/rustfmt");
|
||||||
install(&src.join("README.md"), &doc, 0o644);
|
install(&src.join("README.md"), &doc, 0o644);
|
||||||
install(&src.join("LICENSE-MIT"), &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())
|
cmd.env("CFG_RELEASE_INFO", build.rust_version())
|
||||||
.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM)
|
.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM)
|
||||||
.env("CFG_RELEASE", build.rust_release())
|
.env("CFG_RELEASE", build.rust_release())
|
||||||
.env("CFG_PRERELEASE_VERSION", channel::CFG_PRERELEASE_VERSION)
|
|
||||||
.env("CFG_VER_MAJOR", parts.next().unwrap())
|
.env("CFG_VER_MAJOR", parts.next().unwrap())
|
||||||
.env("CFG_VER_MINOR", parts.next().unwrap())
|
.env("CFG_VER_MINOR", parts.next().unwrap())
|
||||||
.env("CFG_VER_PATCH", 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"));
|
index.arg(out.join("error-index.html"));
|
||||||
|
|
||||||
// FIXME: shouldn't have to pass this env var
|
// 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);
|
build.run(&mut index);
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ pub unsafe fn setup(build: &mut Build) {
|
|||||||
0, FALSE, DUPLICATE_SAME_ACCESS);
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||||
|
|
||||||
// If this failed, well at least we tried! An example of DuplicateHandle
|
// 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
|
// build system (e.g. the `python2` package in MSYS instead of
|
||||||
// `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
|
// `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
|
// 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 getopts;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::{RefCell, Cell};
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
@ -143,8 +144,7 @@ use std::path::{PathBuf, Path};
|
|||||||
use std::process::{self, Command};
|
use std::process::{self, Command};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime,
|
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime};
|
||||||
BuildExpectation};
|
|
||||||
|
|
||||||
use util::{exe, libdir, OutputFolder, CiEnv};
|
use util::{exe, libdir, OutputFolder, CiEnv};
|
||||||
|
|
||||||
@ -190,6 +190,7 @@ mod job {
|
|||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
use flags::Subcommand;
|
use flags::Subcommand;
|
||||||
use cache::{Interned, INTERNER};
|
use cache::{Interned, INTERNER};
|
||||||
|
use toolstate::ToolState;
|
||||||
|
|
||||||
/// A structure representing a Rust compiler.
|
/// A structure representing a Rust compiler.
|
||||||
///
|
///
|
||||||
@ -250,6 +251,7 @@ pub struct Build {
|
|||||||
is_sudo: bool,
|
is_sudo: bool,
|
||||||
ci_env: CiEnv,
|
ci_env: CiEnv,
|
||||||
delayed_failures: RefCell<Vec<String>>,
|
delayed_failures: RefCell<Vec<String>>,
|
||||||
|
prerelease_version: Cell<Option<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -335,6 +337,7 @@ impl Build {
|
|||||||
is_sudo,
|
is_sudo,
|
||||||
ci_env: CiEnv::current(),
|
ci_env: CiEnv::current(),
|
||||||
delayed_failures: RefCell::new(Vec::new()),
|
delayed_failures: RefCell::new(Vec::new()),
|
||||||
|
prerelease_version: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,31 +571,24 @@ impl Build {
|
|||||||
.join(libdir(&self.config.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.
|
/// Runs a command, printing out nice contextual information if it fails.
|
||||||
fn run(&self, cmd: &mut Command) {
|
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.
|
/// Runs a command, printing out nice contextual information if it fails.
|
||||||
fn run_quiet(&self, cmd: &mut Command) {
|
fn run_quiet(&self, cmd: &mut Command) {
|
||||||
self.verbose(&format!("running: {:?}", cmd));
|
self.verbose(&format!("running: {:?}", cmd));
|
||||||
run_suppressed(cmd, BuildExpectation::None)
|
run_suppressed(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a command, printing out nice contextual information if its build
|
/// Runs a command, printing out nice contextual information if it fails.
|
||||||
/// status is not the expected one.
|
/// Exits if the command failed to execute at all, otherwise returns its
|
||||||
/// Exits if the command failed to execute at all, otherwise returns whether
|
/// `status.success()`.
|
||||||
/// the expectation was met
|
fn try_run(&self, cmd: &mut Command) -> bool {
|
||||||
fn try_run(&self, cmd: &mut Command, expect: BuildExpectation) -> bool {
|
|
||||||
self.verbose(&format!("running: {:?}", cmd));
|
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.
|
/// Runs a command, printing out nice contextual information if it fails.
|
||||||
@ -600,7 +596,7 @@ impl Build {
|
|||||||
/// `status.success()`.
|
/// `status.success()`.
|
||||||
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
|
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
|
||||||
self.verbose(&format!("running: {:?}", cmd));
|
self.verbose(&format!("running: {:?}", cmd));
|
||||||
try_run_suppressed(cmd, BuildExpectation::None)
|
try_run_suppressed(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_verbose(&self) -> bool {
|
pub fn is_verbose(&self) -> bool {
|
||||||
@ -725,6 +721,11 @@ impl Build {
|
|||||||
self.config.python.as_ref().unwrap()
|
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
|
/// Tests whether the `compiler` compiling for `target` should be forced to
|
||||||
/// use a stage1 compiler instead.
|
/// use a stage1 compiler instead.
|
||||||
///
|
///
|
||||||
@ -776,12 +777,63 @@ impl Build {
|
|||||||
fn release(&self, num: &str) -> String {
|
fn release(&self, num: &str) -> String {
|
||||||
match &self.config.channel[..] {
|
match &self.config.channel[..] {
|
||||||
"stable" => num.to_string(),
|
"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),
|
"nightly" => format!("{}-nightly", num),
|
||||||
_ => format!("{}-dev", 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.
|
/// Returns the value of `release` above for Rust itself.
|
||||||
fn rust_release(&self) -> String {
|
fn rust_release(&self) -> String {
|
||||||
self.release(channel::CFG_RELEASE_NUM)
|
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.
|
/// Get a list of crates from a root crate.
|
||||||
///
|
///
|
||||||
/// Returns Vec<(crate, path to crate, is_root_crate)>
|
/// Returns Vec<(crate, path to crate, is_root_crate)>
|
||||||
|
@ -53,9 +53,7 @@ check:
|
|||||||
check-aux:
|
check-aux:
|
||||||
$(Q)$(BOOTSTRAP) test \
|
$(Q)$(BOOTSTRAP) test \
|
||||||
src/tools/cargo \
|
src/tools/cargo \
|
||||||
src/tools/rls \
|
src/tools/cargotest \
|
||||||
src/tools/rustfmt \
|
|
||||||
src/tools/miri \
|
|
||||||
src/test/pretty \
|
src/test/pretty \
|
||||||
src/test/run-pass/pretty \
|
src/test/run-pass/pretty \
|
||||||
src/test/run-fail/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",
|
None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon",
|
||||||
};
|
};
|
||||||
|
|
||||||
let llvm_exp_targets = match build.config.llvm_experimental_targets {
|
let llvm_exp_targets = &build.config.llvm_experimental_targets;
|
||||||
Some(ref s) => s,
|
|
||||||
None => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
|
||||||
|
|
||||||
@ -319,7 +316,7 @@ impl Step for TestHelpers {
|
|||||||
.warnings(false)
|
.warnings(false)
|
||||||
.debug(false)
|
.debug(false)
|
||||||
.file(build.src.join("src/rt/rust_test_helpers.c"))
|
.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();
|
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.
|
// submodules and learn about various other aspects.
|
||||||
if build.rust_info.is_git() {
|
if build.rust_info.is_git() {
|
||||||
cmd_finder.must_have("git");
|
cmd_finder.must_have("git");
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::{Command, exit};
|
||||||
|
|
||||||
use Mode;
|
use Mode;
|
||||||
use Compiler;
|
use Compiler;
|
||||||
@ -22,7 +22,6 @@ use native;
|
|||||||
use channel::GitInfo;
|
use channel::GitInfo;
|
||||||
use cache::Interned;
|
use cache::Interned;
|
||||||
use toolstate::ToolState;
|
use toolstate::ToolState;
|
||||||
use build_helper::BuildExpectation;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct CleanTools {
|
pub struct CleanTools {
|
||||||
@ -82,7 +81,7 @@ struct ToolBuild {
|
|||||||
tool: &'static str,
|
tool: &'static str,
|
||||||
path: &'static str,
|
path: &'static str,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
expectation: BuildExpectation,
|
is_ext_tool: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for ToolBuild {
|
impl Step for ToolBuild {
|
||||||
@ -102,7 +101,7 @@ impl Step for ToolBuild {
|
|||||||
let target = self.target;
|
let target = self.target;
|
||||||
let tool = self.tool;
|
let tool = self.tool;
|
||||||
let path = self.path;
|
let path = self.path;
|
||||||
let expectation = self.expectation;
|
let is_ext_tool = self.is_ext_tool;
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
||||||
@ -115,15 +114,25 @@ impl Step for ToolBuild {
|
|||||||
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
|
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
|
||||||
|
|
||||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
||||||
build.run_expecting(&mut cargo, expectation);
|
let is_expected = build.try_run(&mut cargo);
|
||||||
if expectation == BuildExpectation::Succeeding || expectation == BuildExpectation::None {
|
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)
|
let cargo_out = build.cargo_out(compiler, Mode::Tool, target)
|
||||||
.join(exe(tool, &compiler.host));
|
.join(exe(tool, &compiler.host));
|
||||||
let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host));
|
let bin = build.tools_dir(compiler).join(exe(tool, &compiler.host));
|
||||||
copy(&cargo_out, &bin);
|
copy(&cargo_out, &bin);
|
||||||
Some(bin)
|
Some(bin)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,8 +241,8 @@ macro_rules! tool {
|
|||||||
tool: $tool_name,
|
tool: $tool_name,
|
||||||
mode: $mode,
|
mode: $mode,
|
||||||
path: $path,
|
path: $path,
|
||||||
expectation: BuildExpectation::None,
|
is_ext_tool: false,
|
||||||
}).expect("expected to build -- BuildExpectation::None")
|
}).expect("expected to build -- essential tool")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
@ -280,8 +289,8 @@ impl Step for RemoteTestServer {
|
|||||||
tool: "remote-test-server",
|
tool: "remote-test-server",
|
||||||
mode: Mode::Libstd,
|
mode: Mode::Libstd,
|
||||||
path: "src/tools/remote-test-server",
|
path: "src/tools/remote-test-server",
|
||||||
expectation: BuildExpectation::None,
|
is_ext_tool: false,
|
||||||
}).expect("expected to build -- BuildExpectation::None")
|
}).expect("expected to build -- essential tool")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,76 +407,70 @@ impl Step for Cargo {
|
|||||||
tool: "cargo",
|
tool: "cargo",
|
||||||
mode: Mode::Librustc,
|
mode: Mode::Librustc,
|
||||||
path: "src/tools/cargo",
|
path: "src/tools/cargo",
|
||||||
expectation: BuildExpectation::None,
|
is_ext_tool: false,
|
||||||
}).expect("BuildExpectation::None - expected to build")
|
}).expect("expected to build -- essential tool")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
macro_rules! tool_extended {
|
||||||
pub struct Clippy {
|
(($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 compiler: Compiler,
|
||||||
pub target: Interned<String>,
|
pub target: Interned<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for Clippy {
|
impl Step for $name {
|
||||||
type Output = Option<PathBuf>;
|
type Output = Option<PathBuf>;
|
||||||
const DEFAULT: bool = true;
|
const DEFAULT: bool = true;
|
||||||
const ONLY_HOSTS: bool = true;
|
const ONLY_HOSTS: bool = true;
|
||||||
|
|
||||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||||
let builder = run.builder;
|
let builder = run.builder;
|
||||||
run.path("src/tools/clippy").default_condition(builder.build.config.extended)
|
run.path($path).default_condition(builder.build.config.extended)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_run(run: RunConfig) {
|
fn make_run(run: RunConfig) {
|
||||||
run.builder.ensure(Clippy {
|
run.builder.ensure($name {
|
||||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||||
target: run.target,
|
target: run.target,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
fn run($sel, $builder: &Builder) -> Option<PathBuf> {
|
||||||
|
$extra_deps
|
||||||
|
$builder.ensure(ToolBuild {
|
||||||
|
compiler: $sel.compiler,
|
||||||
|
target: $sel.target,
|
||||||
|
tool: $tool_name,
|
||||||
|
mode: Mode::Librustc,
|
||||||
|
path: $path,
|
||||||
|
is_ext_tool: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tool_extended!((self, builder),
|
||||||
|
Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {};
|
||||||
|
Clippy, clippy, "src/tools/clippy", "clippy-driver", {
|
||||||
// Clippy depends on procedural macros (serde), which requires a full host
|
// Clippy depends on procedural macros (serde), which requires a full host
|
||||||
// compiler to be available, so we need to depend on that.
|
// compiler to be available, so we need to depend on that.
|
||||||
builder.ensure(compile::Rustc {
|
builder.ensure(compile::Rustc {
|
||||||
compiler: self.compiler,
|
compiler: self.compiler,
|
||||||
target: builder.build.build,
|
target: builder.build.build,
|
||||||
});
|
});
|
||||||
builder.ensure(ToolBuild {
|
};
|
||||||
compiler: self.compiler,
|
Miri, miri, "src/tools/miri", "miri", {};
|
||||||
target: self.target,
|
Rls, rls, "src/tools/rls", "rls", {
|
||||||
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> {
|
|
||||||
builder.ensure(native::Openssl {
|
builder.ensure(native::Openssl {
|
||||||
target: self.target,
|
target: self.target,
|
||||||
});
|
});
|
||||||
@ -477,87 +480,9 @@ impl Step for Rls {
|
|||||||
compiler: self.compiler,
|
compiler: self.compiler,
|
||||||
target: builder.build.build,
|
target: builder.build.build,
|
||||||
});
|
});
|
||||||
builder.ensure(ToolBuild {
|
};
|
||||||
compiler: self.compiler,
|
Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {};
|
||||||
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),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
/// Get a `Command` which is ready to run `tool` in `stage` built for
|
/// 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
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use build_helper::BuildExpectation;
|
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)]
|
|
||||||
/// Whether a tool can be compiled, tested or neither
|
/// Whether a tool can be compiled, tested or neither
|
||||||
pub enum ToolState {
|
pub enum ToolState {
|
||||||
/// The tool compiles successfully, but the test suite fails
|
/// The tool compiles successfully, but the test suite fails
|
||||||
Compiling = 1,
|
TestFail = 1,
|
||||||
/// The tool compiles successfully and its test suite passes
|
/// The tool compiles successfully and its test suite passes
|
||||||
Testing = 2,
|
TestPass = 2,
|
||||||
/// The tool can't even be compiled
|
/// The tool can't even be compiled
|
||||||
Broken = 0,
|
BuildFail = 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ToolState {
|
impl Default for ToolState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// err on the safe side
|
// 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::env;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File, OpenOptions};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write, Seek, SeekFrom};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::{SystemTime, Instant};
|
use std::time::{SystemTime, Instant};
|
||||||
@ -51,6 +51,20 @@ pub fn copy(src: &Path, dst: &Path) {
|
|||||||
t!(filetime::set_file_times(dst, atime, mtime));
|
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> {
|
pub fn read_stamp_file(stamp: &Path) -> Vec<PathBuf> {
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
|
@ -35,97 +35,55 @@ macro_rules! t {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
pub fn run(cmd: &mut Command) {
|
||||||
pub enum BuildExpectation {
|
|
||||||
Succeeding,
|
|
||||||
Failing,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(cmd: &mut Command, expect: BuildExpectation) {
|
|
||||||
println!("running: {:?}", cmd);
|
println!("running: {:?}", cmd);
|
||||||
run_silent(cmd, expect);
|
run_silent(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_silent(cmd: &mut Command, expect: BuildExpectation) {
|
pub fn run_silent(cmd: &mut Command) {
|
||||||
if !try_run_silent(cmd, expect) {
|
if !try_run_silent(cmd) {
|
||||||
std::process::exit(1);
|
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() {
|
let status = match cmd.status() {
|
||||||
Ok(status) => status,
|
Ok(status) => status,
|
||||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||||
cmd, e)),
|
cmd, e)),
|
||||||
};
|
};
|
||||||
process_status(
|
if !status.success() {
|
||||||
cmd,
|
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||||
status.success(),
|
|
||||||
expect,
|
|
||||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
|
||||||
expected success, got: {}\n\n",
|
expected success, got: {}\n\n",
|
||||||
cmd,
|
cmd,
|
||||||
status))
|
status);
|
||||||
|
}
|
||||||
|
status.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_status<F: FnOnce()>(
|
pub fn run_suppressed(cmd: &mut Command) {
|
||||||
cmd: &Command,
|
if !try_run_suppressed(cmd) {
|
||||||
success: bool,
|
|
||||||
expect: BuildExpectation,
|
|
||||||
f: F,
|
|
||||||
) -> bool {
|
|
||||||
use BuildExpectation::*;
|
|
||||||
match (expect, success) {
|
|
||||||
(None, false) => { f(); false },
|
|
||||||
// Non-tool build succeeds, everything is good
|
|
||||||
(None, true) => true,
|
|
||||||
// Tool expected to work and is working
|
|
||||||
(Succeeding, true) => true,
|
|
||||||
// Tool expected to fail and is failing
|
|
||||||
(Failing, false) => {
|
|
||||||
println!("This failure is expected (see `src/tools/toolstate.toml`)");
|
|
||||||
true
|
|
||||||
},
|
|
||||||
// Tool expected to work, but is failing
|
|
||||||
(Succeeding, false) => {
|
|
||||||
f();
|
|
||||||
println!("You can disable the tool in `src/tools/toolstate.toml`");
|
|
||||||
false
|
|
||||||
},
|
|
||||||
// Tool expected to fail, but is working
|
|
||||||
(Failing, true) => {
|
|
||||||
println!("Expected `{:?}` to fail, but it succeeded.\n\
|
|
||||||
Please adjust `src/tools/toolstate.toml` accordingly", cmd);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) {
|
|
||||||
if !try_run_suppressed(cmd, expect) {
|
|
||||||
std::process::exit(1);
|
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() {
|
let output = match cmd.output() {
|
||||||
Ok(status) => status,
|
Ok(status) => status,
|
||||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||||
cmd, e)),
|
cmd, e)),
|
||||||
};
|
};
|
||||||
process_status(
|
if !output.status.success() {
|
||||||
cmd,
|
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||||
output.status.success(),
|
|
||||||
expect,
|
|
||||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
|
||||||
expected success, got: {}\n\n\
|
expected success, got: {}\n\n\
|
||||||
stdout ----\n{}\n\
|
stdout ----\n{}\n\
|
||||||
stderr ----\n{}\n\n",
|
stderr ----\n{}\n\n",
|
||||||
cmd,
|
cmd,
|
||||||
output.status,
|
output.status,
|
||||||
String::from_utf8_lossy(&output.stdout),
|
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 {
|
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.
|
/// Uses last-modified time checks to verify this.
|
||||||
pub fn up_to_date(src: &Path, dst: &Path) -> bool {
|
pub fn up_to_date(src: &Path, dst: &Path) -> bool {
|
||||||
|
if !dst.exists() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let threshold = mtime(dst);
|
let threshold = mtime(dst);
|
||||||
let meta = match fs::metadata(src) {
|
let meta = match fs::metadata(src) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
|
@ -36,14 +36,14 @@ a Docker image.
|
|||||||
|
|
||||||
1. Select the "default" virtual machine inside VirtualBox, then click
|
1. Select the "default" virtual machine inside VirtualBox, then click
|
||||||
"Settings"
|
"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":
|
a plus sign), fill in the following information, then click "OK":
|
||||||
|
|
||||||
* Folder path: `E:\rust`
|
* Folder path: `E:\rust`
|
||||||
* Folder name: `e/rust`
|
* Folder name: `e/rust`
|
||||||
* Read-only: ☐ *unchecked*
|
* Read-only: ☐ *unchecked*
|
||||||
* Auto-mount: ☑ *checked*
|
* Auto-mount: ☑ *checked*
|
||||||
* Make Permanant: ☑ *checked*
|
* Make Permanent: ☑ *checked*
|
||||||
|
|
||||||
3. VirtualBox might not support creating symbolic links inside a shared folder
|
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`:
|
by default. You can enable it manually by running these from `cmd.exe`:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
FROM ubuntu:16.04
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
g++ \
|
clang \
|
||||||
make \
|
make \
|
||||||
file \
|
file \
|
||||||
curl \
|
curl \
|
||||||
@ -16,16 +16,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
libssl-dev \
|
libssl-dev \
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
COPY dist-i686-freebsd/build-toolchain.sh /tmp/
|
COPY scripts/freebsd-toolchain.sh /tmp/
|
||||||
RUN /tmp/build-toolchain.sh i686
|
RUN /tmp/freebsd-toolchain.sh i686
|
||||||
|
|
||||||
COPY scripts/sccache.sh /scripts/
|
COPY scripts/sccache.sh /scripts/
|
||||||
RUN sh /scripts/sccache.sh
|
RUN sh /scripts/sccache.sh
|
||||||
|
|
||||||
ENV \
|
ENV \
|
||||||
AR_i686_unknown_freebsd=i686-unknown-freebsd10-ar \
|
AR_i686_unknown_freebsd=i686-unknown-freebsd10-ar \
|
||||||
CC_i686_unknown_freebsd=i686-unknown-freebsd10-gcc \
|
CC_i686_unknown_freebsd=i686-unknown-freebsd10-clang \
|
||||||
CXX_i686_unknown_freebsd=i686-unknown-freebsd10-g++
|
CXX_i686_unknown_freebsd=i686-unknown-freebsd10-clang++
|
||||||
|
|
||||||
ENV HOSTS=i686-unknown-freebsd
|
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
|
WORKDIR /tmp
|
||||||
|
|
||||||
COPY cross/build-rumprun.sh /tmp/
|
COPY dist-various-1/build-rumprun.sh /tmp/
|
||||||
RUN ./build-rumprun.sh
|
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
|
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
|
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
|
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
|
RUN ./install-x86_64-redox.sh
|
||||||
|
|
||||||
ENV TARGETS=asmjs-unknown-emscripten
|
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,mipsel-unknown-linux-musl
|
||||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
|
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
|
||||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
|
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
|
||||||
|
ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi
|
||||||
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
|
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
|
||||||
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
|
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
|
||||||
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
|
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
|
||||||
ENV TARGETS=$TARGETS,x86_64-unknown-redox
|
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 \
|
ENV CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc \
|
||||||
CC_mips_unknown_linux_musl=mips-openwrt-linux-gcc \
|
CC_mips_unknown_linux_musl=mips-openwrt-linux-gcc \
|
||||||
CC_sparc64_unknown_linux_gnu=sparc64-linux-gnu-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
|
# Suppress some warnings in the openwrt toolchains we downloaded
|
||||||
ENV STAGING_DIR=/tmp
|
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'
|
RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2-testing main'
|
||||||
|
|
||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
COPY cross2/shared.sh cross2/build-fuchsia-toolchain.sh /tmp/
|
COPY dist-various-2/shared.sh dist-various-2/build-fuchsia-toolchain.sh /tmp/
|
||||||
COPY cross2/build-solaris-toolchain.sh /tmp/
|
COPY dist-various-2/build-solaris-toolchain.sh /tmp/
|
||||||
RUN /tmp/build-fuchsia-toolchain.sh
|
RUN /tmp/build-fuchsia-toolchain.sh
|
||||||
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386
|
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386
|
||||||
RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc
|
RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc
|
||||||
@ -47,6 +47,7 @@ ENV \
|
|||||||
ENV TARGETS=x86_64-unknown-fuchsia
|
ENV TARGETS=x86_64-unknown-fuchsia
|
||||||
ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia
|
ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia
|
||||||
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
|
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
|
||||||
|
ENV TARGETS=$TARGETS,wasm32-unknown-unknown
|
||||||
ENV TARGETS=$TARGETS,x86_64-sun-solaris
|
ENV TARGETS=$TARGETS,x86_64-sun-solaris
|
||||||
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
|
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
FROM ubuntu:16.04
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
g++ \
|
clang \
|
||||||
make \
|
make \
|
||||||
file \
|
file \
|
||||||
curl \
|
curl \
|
||||||
@ -16,16 +16,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
libssl-dev \
|
libssl-dev \
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
COPY dist-x86_64-freebsd/build-toolchain.sh /tmp/
|
COPY scripts/freebsd-toolchain.sh /tmp/
|
||||||
RUN /tmp/build-toolchain.sh x86_64
|
RUN /tmp/freebsd-toolchain.sh x86_64
|
||||||
|
|
||||||
COPY scripts/sccache.sh /scripts/
|
COPY scripts/sccache.sh /scripts/
|
||||||
RUN sh /scripts/sccache.sh
|
RUN sh /scripts/sccache.sh
|
||||||
|
|
||||||
ENV \
|
ENV \
|
||||||
AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \
|
AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \
|
||||||
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-gcc \
|
CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang \
|
||||||
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-g++
|
CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang++
|
||||||
|
|
||||||
ENV HOSTS=x86_64-unknown-freebsd
|
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
|
cd usr/src
|
||||||
|
|
||||||
# The options, in order, do the following
|
# The options, in order, do the following
|
||||||
# * this is an unpriviledged build
|
# * this is an unprivileged build
|
||||||
# * output to a predictable location
|
# * output to a predictable location
|
||||||
# * disable various uneeded stuff
|
# * disable various uneeded stuff
|
||||||
MKUNPRIVED=yes TOOLDIR=/x-tools/x86_64-unknown-netbsd \
|
MKUNPRIVED=yes TOOLDIR=/x-tools/x86_64-unknown-netbsd \
|
||||||
|
@ -99,6 +99,7 @@ exec docker \
|
|||||||
--env LOCAL_USER_ID=`id -u` \
|
--env LOCAL_USER_ID=`id -u` \
|
||||||
--env TRAVIS \
|
--env TRAVIS \
|
||||||
--env TRAVIS_BRANCH \
|
--env TRAVIS_BRANCH \
|
||||||
|
--env TOOLSTATE_REPO_ACCESS_TOKEN \
|
||||||
--volume "$HOME/.cargo:/cargo" \
|
--volume "$HOME/.cargo:/cargo" \
|
||||||
--volume "$HOME/rustsrc:$HOME/rustsrc" \
|
--volume "$HOME/rustsrc:$HOME/rustsrc" \
|
||||||
--init \
|
--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 \
|
libssl-dev \
|
||||||
sudo \
|
sudo \
|
||||||
xz-utils \
|
xz-utils \
|
||||||
pkg-config
|
pkg-config \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
llvm-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libexpat1-dev
|
||||||
|
|
||||||
COPY scripts/sccache.sh /scripts/
|
COPY scripts/sccache.sh /scripts/
|
||||||
RUN sh /scripts/sccache.sh
|
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
|
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/
|
COPY scripts/sccache.sh /scripts/
|
||||||
RUN sh /scripts/sccache.sh
|
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
|
ENV SCRIPT python2.7 ../x.py test
|
||||||
|
@ -36,6 +36,12 @@ fi
|
|||||||
rm -rf "$CACHE_DIR"
|
rm -rf "$CACHE_DIR"
|
||||||
mkdir "$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_fold start update_cache
|
||||||
travis_time_start
|
travis_time_start
|
||||||
|
|
||||||
|
@ -37,13 +37,14 @@ if [ "$DIST_SRC" = "" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# If we're deploying artifacts then we set the release channel, otherwise if
|
# 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
|
# we'll be running tests
|
||||||
#
|
#
|
||||||
# FIXME: need a scheme for changing this `nightly` value to `beta` and `stable`
|
# FIXME: need a scheme for changing this `nightly` value to `beta` and `stable`
|
||||||
# either automatically or manually.
|
# either automatically or manually.
|
||||||
|
export RUST_RELEASE_CHANNEL=stable
|
||||||
if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
|
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"
|
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp"
|
||||||
|
|
||||||
if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then
|
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
|
# The Rust Programming Language
|
||||||
|
|
||||||
[](https://travis-ci.org/rust-lang/book)
|
[](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,
|
The second edition is a rewrite that will be printed by NoStarch Press,
|
||||||
available around October 2017.
|
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
|
the first half of the book is much improved from the first edition. We recommend
|
||||||
starting with the second edition.
|
starting with the second edition.
|
||||||
|
|
||||||
[html]: http://rust-lang.github.io/book/
|
[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].
|
[The first edition is still available to read online][first].
|
||||||
|
|
||||||
[first]: https://doc.rust-lang.org/book/
|
[first]: https://doc.rust-lang.org/book/
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Building the book requires [mdBook], ideally the same version that
|
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:
|
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` 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*
|
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 `bool` or `char` and `U` is an integer type; *prim-int-cast*
|
||||||
* `e` has type `u8` and `U` is `char`; *u8-char-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);
|
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
|
```rust
|
||||||
pub enum Foo {}
|
#[repr(C)] pub struct Foo { private: [u8; 0] }
|
||||||
pub enum Bar {}
|
#[repr(C)] pub struct Bar { private: [u8; 0] }
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn foo(arg: *mut Foo);
|
pub fn foo(arg: *mut Foo);
|
||||||
@ -750,7 +750,9 @@ extern "C" {
|
|||||||
# fn main() {}
|
# fn main() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
By using an `enum` with no variants, we create an opaque type that we can’t
|
By including a private field and no constructor,
|
||||||
instantiate, as it has no variants. But because our `Foo` and `Bar` types are
|
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
|
different, we’ll get type safety between the two of them, so we cannot
|
||||||
accidentally pass a pointer to `Foo` to `bar()`.
|
accidentally pass a pointer to `Foo` to `bar()`.
|
||||||
|
@ -4,11 +4,13 @@ abcd
|
|||||||
abcdefghijklmnopqrstuvwxyz
|
abcdefghijklmnopqrstuvwxyz
|
||||||
adaptor
|
adaptor
|
||||||
adaptors
|
adaptors
|
||||||
|
AddAssign
|
||||||
Addr
|
Addr
|
||||||
aggregator
|
aggregator
|
||||||
AGraph
|
AGraph
|
||||||
aliasability
|
aliasability
|
||||||
alignof
|
alignof
|
||||||
|
alloc
|
||||||
allocator
|
allocator
|
||||||
Amir
|
Amir
|
||||||
anotherusername
|
anotherusername
|
||||||
@ -29,19 +31,23 @@ Baz’s
|
|||||||
benchmarking
|
benchmarking
|
||||||
bitand
|
bitand
|
||||||
BitAnd
|
BitAnd
|
||||||
|
BitAndAssign
|
||||||
bitor
|
bitor
|
||||||
BitOr
|
BitOr
|
||||||
|
BitOrAssign
|
||||||
bitwise
|
bitwise
|
||||||
Bitwise
|
Bitwise
|
||||||
bitxor
|
bitxor
|
||||||
BitXor
|
BitXor
|
||||||
|
BitXorAssign
|
||||||
Bjarne
|
Bjarne
|
||||||
Boehm
|
Boehm
|
||||||
bool
|
bool
|
||||||
boolean
|
Boolean
|
||||||
booleans
|
Booleans
|
||||||
Bors
|
Bors
|
||||||
BorrowMutError
|
BorrowMutError
|
||||||
|
BTreeSet
|
||||||
BuildHasher
|
BuildHasher
|
||||||
Cacher
|
Cacher
|
||||||
Cagain
|
Cagain
|
||||||
@ -88,6 +94,7 @@ dereferenced
|
|||||||
dereferences
|
dereferences
|
||||||
dereferencing
|
dereferencing
|
||||||
DerefMut
|
DerefMut
|
||||||
|
DeriveInput
|
||||||
destructor
|
destructor
|
||||||
destructure
|
destructure
|
||||||
destructured
|
destructured
|
||||||
@ -102,6 +109,7 @@ doccratesio
|
|||||||
DOCTYPE
|
DOCTYPE
|
||||||
doesn
|
doesn
|
||||||
disambiguating
|
disambiguating
|
||||||
|
DivAssign
|
||||||
DraftPost
|
DraftPost
|
||||||
DSTs
|
DSTs
|
||||||
ebooks
|
ebooks
|
||||||
@ -119,6 +127,7 @@ eprintln
|
|||||||
Erlang
|
Erlang
|
||||||
ErrorKind
|
ErrorKind
|
||||||
Executables
|
Executables
|
||||||
|
expr
|
||||||
extern
|
extern
|
||||||
favicon
|
favicon
|
||||||
FFFD
|
FFFD
|
||||||
@ -135,6 +144,7 @@ FnBox
|
|||||||
FnMut
|
FnMut
|
||||||
FnOnce
|
FnOnce
|
||||||
formatter
|
formatter
|
||||||
|
FrenchToast
|
||||||
FromIterator
|
FromIterator
|
||||||
frontend
|
frontend
|
||||||
getter
|
getter
|
||||||
@ -155,20 +165,26 @@ HashSet
|
|||||||
Haskell
|
Haskell
|
||||||
hasn
|
hasn
|
||||||
helloworld
|
helloworld
|
||||||
|
HelloWorld
|
||||||
|
HelloWorldName
|
||||||
Hmmm
|
Hmmm
|
||||||
Hoare
|
Hoare
|
||||||
Hola
|
Hola
|
||||||
homogenous
|
homogenous
|
||||||
html
|
html
|
||||||
|
hyperoptimize
|
||||||
Iceburgh
|
Iceburgh
|
||||||
|
ident
|
||||||
IEEE
|
IEEE
|
||||||
impl
|
impl
|
||||||
implementor
|
implementor
|
||||||
implementors
|
implementors
|
||||||
ImportantExcerpt
|
ImportantExcerpt
|
||||||
incrementing
|
incrementing
|
||||||
|
IndexMut
|
||||||
indices
|
indices
|
||||||
init
|
init
|
||||||
|
initializer
|
||||||
inline
|
inline
|
||||||
instantiation
|
instantiation
|
||||||
internet
|
internet
|
||||||
@ -190,6 +206,7 @@ JoinHandle
|
|||||||
kinded
|
kinded
|
||||||
lang
|
lang
|
||||||
latin
|
latin
|
||||||
|
liballoc
|
||||||
libc
|
libc
|
||||||
libcollections
|
libcollections
|
||||||
libcore
|
libcore
|
||||||
@ -213,6 +230,7 @@ Metadata
|
|||||||
metaprogramming
|
metaprogramming
|
||||||
mibbit
|
mibbit
|
||||||
Mibbit
|
Mibbit
|
||||||
|
millis
|
||||||
minigrep
|
minigrep
|
||||||
mixup
|
mixup
|
||||||
mkdir
|
mkdir
|
||||||
@ -225,6 +243,7 @@ monomorphized
|
|||||||
MoveMessage
|
MoveMessage
|
||||||
Mozilla
|
Mozilla
|
||||||
mpsc
|
mpsc
|
||||||
|
MulAssign
|
||||||
multibyte
|
multibyte
|
||||||
multithreaded
|
multithreaded
|
||||||
mutex
|
mutex
|
||||||
@ -248,6 +267,7 @@ nitty
|
|||||||
nocapture
|
nocapture
|
||||||
nomicon
|
nomicon
|
||||||
Nomicon
|
Nomicon
|
||||||
|
nonequality
|
||||||
NotFound
|
NotFound
|
||||||
null's
|
null's
|
||||||
OCaml
|
OCaml
|
||||||
@ -261,6 +281,7 @@ OsStr
|
|||||||
OsString
|
OsString
|
||||||
other's
|
other's
|
||||||
OutlinePrint
|
OutlinePrint
|
||||||
|
overloadable
|
||||||
overread
|
overread
|
||||||
param
|
param
|
||||||
parameterize
|
parameterize
|
||||||
@ -288,6 +309,9 @@ pushups
|
|||||||
QuitMessage
|
QuitMessage
|
||||||
RAII
|
RAII
|
||||||
randcrate
|
randcrate
|
||||||
|
RangeFrom
|
||||||
|
RangeTo
|
||||||
|
RangeFull
|
||||||
README
|
README
|
||||||
READMEs
|
READMEs
|
||||||
rect
|
rect
|
||||||
@ -302,6 +326,7 @@ RefCell
|
|||||||
RefMut
|
RefMut
|
||||||
refutability
|
refutability
|
||||||
reimplement
|
reimplement
|
||||||
|
RemAssign
|
||||||
repr
|
repr
|
||||||
representable
|
representable
|
||||||
request's
|
request's
|
||||||
@ -326,6 +351,9 @@ SecondaryColor
|
|||||||
SelectBox
|
SelectBox
|
||||||
semver
|
semver
|
||||||
SemVer
|
SemVer
|
||||||
|
serde
|
||||||
|
ShlAssign
|
||||||
|
ShrAssign
|
||||||
shouldn
|
shouldn
|
||||||
Simula
|
Simula
|
||||||
situps
|
situps
|
||||||
@ -346,12 +374,15 @@ Stdin
|
|||||||
stdlib
|
stdlib
|
||||||
stdout
|
stdout
|
||||||
steveklabnik's
|
steveklabnik's
|
||||||
|
stringify
|
||||||
Stroustrup
|
Stroustrup
|
||||||
|
Stroustrup's
|
||||||
struct
|
struct
|
||||||
Struct
|
Struct
|
||||||
structs
|
structs
|
||||||
struct's
|
struct's
|
||||||
Structs
|
Structs
|
||||||
|
SubAssign
|
||||||
subclasses
|
subclasses
|
||||||
subcommand
|
subcommand
|
||||||
subcommands
|
subcommands
|
||||||
@ -369,6 +400,7 @@ supertrait
|
|||||||
supertraits
|
supertraits
|
||||||
TcpListener
|
TcpListener
|
||||||
TcpStream
|
TcpStream
|
||||||
|
templating
|
||||||
test's
|
test's
|
||||||
TextField
|
TextField
|
||||||
That'd
|
That'd
|
||||||
@ -378,7 +410,9 @@ threadsafe
|
|||||||
timestamp
|
timestamp
|
||||||
Tiếng
|
Tiếng
|
||||||
timeline
|
timeline
|
||||||
|
tlborm
|
||||||
TODO
|
TODO
|
||||||
|
TokenStream
|
||||||
toml
|
toml
|
||||||
TOML
|
TOML
|
||||||
ToString
|
ToString
|
||||||
@ -389,7 +423,9 @@ trpl
|
|||||||
tuesday
|
tuesday
|
||||||
tuple
|
tuple
|
||||||
tuples
|
tuples
|
||||||
|
turbofish
|
||||||
typeof
|
typeof
|
||||||
|
TypeName
|
||||||
UFCS
|
UFCS
|
||||||
unary
|
unary
|
||||||
Unary
|
Unary
|
||||||
|
@ -1,63 +1,912 @@
|
|||||||
# Appendix
|
## Appendix A: Keywords
|
||||||
|
|
||||||
The following sections contain reference material you may find useful in your
|
|
||||||
Rust journey.
|
|
||||||
|
|
||||||
## Keywords
|
|
||||||
|
|
||||||
The following keywords are reserved by the Rust language and may not be used as
|
The following keywords are reserved by the Rust language and may not be used as
|
||||||
names of functions, variables, macros, modules, crates, constants, static
|
identifiers such as names of functions, variables, parameters, struct fields,
|
||||||
values, attributes, struct fields, or arguments.
|
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`
|
* `abstract`
|
||||||
* `alignof`
|
* `alignof`
|
||||||
* `as`
|
|
||||||
* `become`
|
* `become`
|
||||||
* `box`
|
* `box`
|
||||||
* `break`
|
|
||||||
* `const`
|
|
||||||
* `continue`
|
|
||||||
* `crate`
|
|
||||||
* `do`
|
* `do`
|
||||||
* `else`
|
|
||||||
* `enum`
|
|
||||||
* `extern`
|
|
||||||
* `false`
|
|
||||||
* `final`
|
* `final`
|
||||||
* `fn`
|
|
||||||
* `for`
|
|
||||||
* `if`
|
|
||||||
* `impl`
|
|
||||||
* `in`
|
|
||||||
* `let`
|
|
||||||
* `loop`
|
|
||||||
* `macro`
|
* `macro`
|
||||||
* `match`
|
|
||||||
* `mod`
|
|
||||||
* `move`
|
|
||||||
* `mut`
|
|
||||||
* `offsetof`
|
* `offsetof`
|
||||||
* `override`
|
* `override`
|
||||||
* `priv`
|
* `priv`
|
||||||
* `proc`
|
* `proc`
|
||||||
* `pub`
|
|
||||||
* `pure`
|
* `pure`
|
||||||
* `ref`
|
|
||||||
* `return`
|
|
||||||
* `Self`
|
|
||||||
* `self`
|
|
||||||
* `sizeof`
|
* `sizeof`
|
||||||
* `static`
|
|
||||||
* `struct`
|
|
||||||
* `super`
|
|
||||||
* `trait`
|
|
||||||
* `true`
|
|
||||||
* `type`
|
|
||||||
* `typeof`
|
* `typeof`
|
||||||
* `unsafe`
|
|
||||||
* `unsized`
|
* `unsized`
|
||||||
* `use`
|
|
||||||
* `virtual`
|
* `virtual`
|
||||||
* `where`
|
|
||||||
* `while`
|
|
||||||
* `yield`
|
* `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
|
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
|
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
|
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");
|
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),
|
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
|
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
|
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`,
|
`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
|
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||||
Listing 5-13, shown in Listing 5-15:
|
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
|
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
|
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
|
`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
|
Here, it can be any type. The type of `coin` in this example is the `Coin` enum
|
||||||
that we defined in Listing 6-3.
|
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.
|
modules from *src/lib.rs* and place them into their own files.
|
||||||
|
|
||||||
First, replace the `client` module code with only the declaration of the
|
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
|
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
|
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
|
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
|
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
|
$ 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*
|
*src/server.rs*
|
||||||
|
|
||||||
The error says we `cannot declare a new module at this location` and is
|
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
|
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.
|
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:
|
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`
|
## 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
|
`network::server` code into the *src/network/mod.rs* and
|
||||||
*src/network/server.rs* files, respectively. At that point, `cargo build` was
|
*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
|
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
|
### Privacy Examples
|
||||||
|
|
||||||
Let’s look at a few more privacy examples to get some practice. Create a new
|
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*:
|
*src/lib.rs*:
|
||||||
|
|
||||||
Filename: 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
|
incorrect
|
||||||
|
|
||||||
Before you try to compile this code, make a guess about which lines in the
|
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
|
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
|
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
|
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.
|
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.
|
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
|
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
|
`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
|
Filename: src/lib.rs
|
||||||
|
|
||||||
@ -1081,7 +1084,7 @@ $ cargo test
|
|||||||
running 1 test
|
running 1 test
|
||||||
test tests::it_works ... ok
|
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
|
## 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 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
|
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
|
a vector that holds five elements and then tries to access an element at index
|
||||||
100, as shown in Listing 8-6:
|
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
|
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
|
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
|
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:
|
work:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -194,15 +194,16 @@ to an item
|
|||||||
Compiling this code will result in this error:
|
Compiling this code will result in this error:
|
||||||
|
|
||||||
```
|
```
|
||||||
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
|
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
|
||||||
immutable
|
-->
|
||||||
|
|
|
|
||||||
4 | let first = &v[0];
|
4 | let first = &v[0];
|
||||||
| - immutable borrow occurs here
|
| - immutable borrow occurs here
|
||||||
5 |
|
5 |
|
||||||
6 | v.push(6);
|
6 | v.push(6);
|
||||||
| ^ mutable borrow occurs here
|
| ^ mutable borrow occurs here
|
||||||
7 | }
|
7 |
|
||||||
|
8 | }
|
||||||
| - immutable borrow ends here
|
| - immutable borrow ends here
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -102,17 +102,18 @@ Filename: src/main.rs
|
|||||||
fn main() {
|
fn main() {
|
||||||
let v = vec![1, 2, 3];
|
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
|
Listing 9-1: Attempting to access an element beyond the end of a vector, which
|
||||||
will cause a `panic!`
|
will cause a `panic!`
|
||||||
|
|
||||||
Here, we’re attempting to access the hundredth element of our vector, but it
|
Here, we’re attempting to access the hundredth element of our vector (which is
|
||||||
has only three elements. In this situation, Rust will panic. Using `[]` is
|
at index 99 because indexing starts at zero), but it has only three elements.
|
||||||
supposed to return an element, but if you pass an invalid index, there’s no
|
In this situation, Rust will panic. Using `[]` is supposed to return an
|
||||||
element that Rust could return here that would be correct.
|
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
|
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
|
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();
|
let mut s = String::new();
|
||||||
|
|
||||||
File::open("hello.txt")?.read_to_string(&mut s)?;
|
File::open("hello.txt")?.read_to_string(&mut s)?;
|
||||||
|
|
||||||
Ok(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.
|
//! calculations more convenient.
|
||||||
|
|
||||||
/// Adds one to the number given.
|
/// Adds one to the number given.
|
||||||
// ...snip...
|
// --snip--
|
||||||
```
|
```
|
||||||
|
|
||||||
Listing 14-3: Documentation for the `my_crate` crate as a whole
|
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
|
/// Combines two primary colors in equal amounts to create
|
||||||
/// a secondary color.
|
/// a secondary color.
|
||||||
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
|
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -372,11 +372,11 @@ pub use kinds::SecondaryColor;
|
|||||||
pub use utils::mix;
|
pub use utils::mix;
|
||||||
|
|
||||||
pub mod kinds {
|
pub mod kinds {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod utils {
|
pub mod utils {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -404,7 +404,7 @@ use art::PrimaryColor;
|
|||||||
use art::mix;
|
use art::mix;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -469,7 +469,7 @@ $ cargo publish
|
|||||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||||
warning: manifest has no description, license, license-file, documentation,
|
warning: manifest has no description, license, license-file, documentation,
|
||||||
homepage or repository.
|
homepage or repository.
|
||||||
...snip...
|
--snip--
|
||||||
error: api errors: missing or empty metadata fields: description, license.
|
error: api errors: missing or empty metadata fields: description, license.
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -766,7 +766,7 @@ and compile the `rand` crate:
|
|||||||
$ cargo build
|
$ cargo build
|
||||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||||
Downloading rand v0.3.14
|
Downloading rand v0.3.14
|
||||||
...snip...
|
--snip--
|
||||||
Compiling rand v0.3.14
|
Compiling rand v0.3.14
|
||||||
Compiling add-one v0.1.0 (file:///projects/adder/add-one)
|
Compiling add-one v0.1.0 (file:///projects/adder/add-one)
|
||||||
Compiling adder v0.1.0 (file:///projects/adder)
|
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
|
$ cargo install ripgrep
|
||||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||||
Downloading ripgrep v0.3.2
|
Downloading ripgrep v0.3.2
|
||||||
...snip...
|
--snip--
|
||||||
Compiling ripgrep v0.3.2
|
Compiling ripgrep v0.3.2
|
||||||
Finished release [optimized + debuginfo] target(s) in 97.91 secs
|
Finished release [optimized + debuginfo] target(s) in 97.91 secs
|
||||||
Installing ~/.cargo/bin/rg
|
Installing ~/.cargo/bin/rg
|
||||||
|
@ -1690,7 +1690,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_sends_an_over_75_percent_warning_message() {
|
fn it_sends_an_over_75_percent_warning_message() {
|
||||||
// ...snip...
|
// --snip--
|
||||||
# let mock_messenger = MockMessenger::new();
|
# let mock_messenger = MockMessenger::new();
|
||||||
# let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
|
# let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
|
||||||
# limit_tracker.set_value(75);
|
# limit_tracker.set_value(75);
|
||||||
|
@ -817,7 +817,7 @@ shown in Listing 16-11:
|
|||||||
Filename: src/main.rs
|
Filename: src/main.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
let tx1 = mpsc::Sender::clone(&tx);
|
let tx1 = mpsc::Sender::clone(&tx);
|
||||||
@ -848,7 +848,7 @@ thread::spawn(move || {
|
|||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// ...snip...
|
// --snip--
|
||||||
```
|
```
|
||||||
|
|
||||||
Listing 16-11: Sending multiple messages and pausing between each one
|
Listing 16-11: Sending multiple messages and pausing between each one
|
||||||
|
@ -795,7 +795,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl Post {
|
impl Post {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn add_text(&mut self, text: &str) {
|
pub fn add_text(&mut self, text: &str) {
|
||||||
self.content.push_str(text);
|
self.content.push_str(text);
|
||||||
}
|
}
|
||||||
@ -828,7 +828,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl Post {
|
impl Post {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn content(&self) -> &str {
|
pub fn content(&self) -> &str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
@ -860,7 +860,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl Post {
|
impl Post {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn request_review(&mut self) {
|
pub fn request_review(&mut self) {
|
||||||
if let Some(s) = self.state.take() {
|
if let Some(s) = self.state.take() {
|
||||||
self.state = Some(s.request_review())
|
self.state = Some(s.request_review())
|
||||||
@ -943,7 +943,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl Post {
|
impl Post {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn approve(&mut self) {
|
pub fn approve(&mut self) {
|
||||||
if let Some(s) = self.state.take() {
|
if let Some(s) = self.state.take() {
|
||||||
self.state = Some(s.approve())
|
self.state = Some(s.approve())
|
||||||
@ -959,7 +959,7 @@ trait State {
|
|||||||
struct Draft {}
|
struct Draft {}
|
||||||
|
|
||||||
impl State for Draft {
|
impl State for Draft {
|
||||||
// ...snip...
|
// --snip--
|
||||||
fn approve(self: Box<Self>) -> Box<State> {
|
fn approve(self: Box<Self>) -> Box<State> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -968,7 +968,7 @@ impl State for Draft {
|
|||||||
struct PendingReview {}
|
struct PendingReview {}
|
||||||
|
|
||||||
impl State for PendingReview {
|
impl State for PendingReview {
|
||||||
// ...snip...
|
// --snip--
|
||||||
fn approve(self: Box<Self>) -> Box<State> {
|
fn approve(self: Box<Self>) -> Box<State> {
|
||||||
Box::new(Published {})
|
Box::new(Published {})
|
||||||
}
|
}
|
||||||
@ -1007,11 +1007,11 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl Post {
|
impl Post {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn content(&self) -> &str {
|
pub fn content(&self) -> &str {
|
||||||
self.state.as_ref().unwrap().content(&self)
|
self.state.as_ref().unwrap().content(&self)
|
||||||
}
|
}
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1047,17 +1047,17 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
trait State {
|
trait State {
|
||||||
// ...snip...
|
// --snip--
|
||||||
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
struct Published {}
|
struct Published {}
|
||||||
|
|
||||||
impl State for Published {
|
impl State for Published {
|
||||||
// ...snip...
|
// --snip--
|
||||||
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
fn content<'a>(&self, post: &'a Post) -> &'a str {
|
||||||
&post.content
|
&post.content
|
||||||
}
|
}
|
||||||
@ -1237,7 +1237,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl DraftPost {
|
impl DraftPost {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
pub fn request_review(self) -> PendingReviewPost {
|
pub fn request_review(self) -> PendingReviewPost {
|
||||||
PendingReviewPost {
|
PendingReviewPost {
|
||||||
|
@ -110,9 +110,9 @@ Raw pointers:
|
|||||||
|
|
||||||
- Are allowed to ignore the borrowing rules and have both immutable and a
|
- Are allowed to ignore the borrowing rules and have both immutable and a
|
||||||
mutable pointer or multiple mutable pointers to the same location
|
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
|
- 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:
|
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
|
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
|
mutable reference into their corresponding raw pointer types. These particular
|
||||||
raw pointers will be valid since we created them directly from references that
|
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.
|
pointer.
|
||||||
|
|
||||||
Listing 19-2 shows how to create a raw pointer to an arbitrary location in
|
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 {
|
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 {
|
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 {
|
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)>) {
|
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"));
|
let f: Box<Fn() + Send + 'static> = Box::new(|| println!("hi"));
|
||||||
|
|
||||||
fn takes_long_type(f: Box<Fn() + Send + 'static>) {
|
fn takes_long_type(f: Box<Fn() + Send + 'static>) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
fn returns_long_type() -> Box<Fn() + Send + 'static> {
|
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"));
|
let f: Thunk = Box::new(|| println!("hi"));
|
||||||
|
|
||||||
fn takes_long_type(f: Thunk) {
|
fn takes_long_type(f: Thunk) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
fn returns_long_type() -> Thunk {
|
fn returns_long_type() -> Thunk {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1652,7 +1652,7 @@ function will never return. For example:
|
|||||||
|
|
||||||
```
|
```
|
||||||
fn bar() -> ! {
|
fn bar() -> ! {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1795,7 +1795,7 @@ That is, a generic function definition like this:
|
|||||||
|
|
||||||
```
|
```
|
||||||
fn generic<T>(t: T) {
|
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) {
|
fn generic<T: Sized>(t: T) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1813,7 +1813,7 @@ restriction:
|
|||||||
|
|
||||||
```
|
```
|
||||||
fn generic<T: ?Sized>(t: &T) {
|
fn generic<T: ?Sized>(t: &T) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -391,7 +391,7 @@ Filename: src/main.rs
|
|||||||
```
|
```
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
fn handle_connection(mut stream: TcpStream) {
|
||||||
let mut buffer = [0; 512];
|
let mut buffer = [0; 512];
|
||||||
@ -442,7 +442,7 @@ add code to treat requests differently:
|
|||||||
Filename: src/main.rs
|
Filename: src/main.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
fn handle_connection(mut stream: TcpStream) {
|
||||||
let mut buffer = [0; 512];
|
let mut buffer = [0; 512];
|
||||||
@ -495,7 +495,7 @@ browser indicating as such to the end user:
|
|||||||
Filename: src/main.rs
|
Filename: src/main.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
|
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
|
Filename: src/main.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
fn handle_connection(mut stream: TcpStream) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
let (status_line, filename) = if buffer.starts_with(get) {
|
let (status_line, filename) = if buffer.starts_with(get) {
|
||||||
("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
|
("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
|
||||||
@ -625,10 +625,10 @@ Filename: src/main.rs
|
|||||||
```
|
```
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
fn handle_connection(mut stream: TcpStream) {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
let get = b"GET / HTTP/1.1\r\n";
|
let get = b"GET / HTTP/1.1\r\n";
|
||||||
let sleep = b"GET /sleep 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")
|
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
|
||||||
};
|
};
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -922,7 +922,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
pub fn execute<F>(&self, f: F)
|
pub fn execute<F>(&self, f: F)
|
||||||
where
|
where
|
||||||
@ -1003,7 +1003,7 @@ impl ThreadPool {
|
|||||||
ThreadPool
|
ThreadPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1063,7 +1063,7 @@ pub struct ThreadPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: u32) -> ThreadPool {
|
pub fn new(size: u32) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
@ -1078,7 +1078,7 @@ impl ThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1164,7 +1164,7 @@ pub struct ThreadPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
pub fn new(size: usize) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
@ -1178,7 +1178,7 @@ impl ThreadPool {
|
|||||||
workers
|
workers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Worker {
|
struct Worker {
|
||||||
@ -1250,7 +1250,7 @@ hold anything for now:
|
|||||||
Filename: src/lib.rs
|
Filename: src/lib.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
pub struct ThreadPool {
|
pub struct ThreadPool {
|
||||||
@ -1261,7 +1261,7 @@ pub struct ThreadPool {
|
|||||||
struct Job;
|
struct Job;
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
pub fn new(size: usize) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
@ -1278,7 +1278,7 @@ impl ThreadPool {
|
|||||||
sender,
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1298,7 +1298,7 @@ Filename: src/lib.rs
|
|||||||
|
|
||||||
```
|
```
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
pub fn new(size: usize) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
@ -1315,10 +1315,10 @@ impl ThreadPool {
|
|||||||
sender,
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: mpsc::Receiver<Job>) -> 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::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
pub fn new(size: usize) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
@ -1405,12 +1405,12 @@ impl ThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> 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
|
Filename: src/lib.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
type Job = Box<FnOnce() + Send + 'static>;
|
type Job = Box<FnOnce() + Send + 'static>;
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
pub fn execute<F>(&self, f: F)
|
pub fn execute<F>(&self, f: F)
|
||||||
where
|
where
|
||||||
@ -1450,7 +1450,7 @@ impl ThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
```
|
```
|
||||||
|
|
||||||
Listing 20-19: Creating a `Job` type alias for a `Box` that holds each closure,
|
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
|
Filename: src/lib.rs
|
||||||
|
|
||||||
```
|
```
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> 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>;
|
type Job = Box<FnBox + Send + 'static>;
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||||
@ -1793,7 +1793,7 @@ Filename: src/lib.rs
|
|||||||
```
|
```
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
Worker {
|
Worker {
|
||||||
id,
|
id,
|
||||||
@ -1865,16 +1865,16 @@ pub struct ThreadPool {
|
|||||||
sender: mpsc::Sender<Message>,
|
sender: mpsc::Sender<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
// ...snip...
|
// --snip--
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
pub fn new(size: usize) -> ThreadPool {
|
||||||
assert!(size > 0);
|
assert!(size > 0);
|
||||||
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<F>(&self, f: F)
|
pub fn execute<F>(&self, f: F)
|
||||||
@ -1887,7 +1887,7 @@ impl ThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...snip...
|
// --snip--
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) ->
|
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) ->
|
||||||
|
@ -67,11 +67,11 @@
|
|||||||
|
|
||||||
## Thinking in Rust
|
## Thinking in Rust
|
||||||
|
|
||||||
- [Functional Language Features in Rust](ch13-00-functional-features.md)
|
- [Functional Language Features: Iterators and Closures](ch13-00-functional-features.md)
|
||||||
- [Closures](ch13-01-closures.md)
|
- [Closures: Anonymous Functions that Can Capture Their Environment](ch13-01-closures.md)
|
||||||
- [Iterators](ch13-02-iterators.md)
|
- [Processing a Series of Items with Iterators](ch13-02-iterators.md)
|
||||||
- [Improving our I/O Project](ch13-03-improving-our-io-project.md)
|
- [Improving Our I/O Project](ch13-03-improving-our-io-project.md)
|
||||||
- [Performance](ch13-04-performance.md)
|
- [Comparing Performance: Loops vs. Iterators](ch13-04-performance.md)
|
||||||
|
|
||||||
- [More about Cargo and Crates.io](ch14-00-more-about-cargo.md)
|
- [More about Cargo and Crates.io](ch14-00-more-about-cargo.md)
|
||||||
- [Customizing Builds with Release Profiles](ch14-01-release-profiles.md)
|
- [Customizing Builds with Release Profiles](ch14-01-release-profiles.md)
|
||||||
@ -123,8 +123,8 @@
|
|||||||
|
|
||||||
- [Appendix](appendix-00.md)
|
- [Appendix](appendix-00.md)
|
||||||
- [A - Keywords](appendix-01-keywords.md)
|
- [A - Keywords](appendix-01-keywords.md)
|
||||||
- [B - Operators](appendix-02-operators.md)
|
- [B - Operators and Symbols](appendix-02-operators.md)
|
||||||
- [C - Derivable Traits]()
|
- [C - Derivable Traits](appendix-03-derivable-traits.md)
|
||||||
- [D - Macros]()
|
- [D - Macros](appendix-04-macros.md)
|
||||||
- [E - Translations]()
|
- [E - Translations](appendix-05-translation.md)
|
||||||
- [F - Newest Features](appendix-07-newest-features.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
|
* `else` - fallback for `if` and `if let` control flow constructs
|
||||||
* `enum` - defining an enumeration
|
* `enum` - defining an enumeration
|
||||||
* `extern` - external crate, function, and variable linkage
|
* `extern` - external crate, function, and variable linkage
|
||||||
* `false` - boolean false literal
|
* `false` - Boolean false literal
|
||||||
* `fn` - function definition and function pointer type
|
* `fn` - function definition and function pointer type
|
||||||
* `for` - iterator loop, part of trait impl syntax, and higher-ranked lifetime
|
* `for` - iterator loop, part of trait impl syntax, and higher-ranked lifetime
|
||||||
syntax
|
syntax
|
||||||
@ -39,7 +39,7 @@ or lifetimes.
|
|||||||
* `struct` - structure definition
|
* `struct` - structure definition
|
||||||
* `super` - parent module of the current module
|
* `super` - parent module of the current module
|
||||||
* `trait` - trait definition
|
* `trait` - trait definition
|
||||||
* `true` - boolean true literal
|
* `true` - Boolean true literal
|
||||||
* `type` - type alias and associated type definition
|
* `type` - type alias and associated type definition
|
||||||
* `unsafe` - denotes unsafe code, functions, traits, and implementations
|
* `unsafe` - denotes unsafe code, functions, traits, and implementations
|
||||||
* `use` - import symbols into scope
|
* `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
|
The following lists the operators in Rust, an example of how the operator would
|
||||||
operators, before the expression they apply to.
|
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.
|
||||||
: Negation. Signed integer types and floating-point types support negation. It
|
* `!` (`!expr`): bitwise or logical complement. Overloadable (`Not`).
|
||||||
is an error to apply negation to unsigned types; for example, the compiler
|
* `!=` (`var != expr`): nonequality comparison. Overloadable (`PartialEq`).
|
||||||
rejects `-1u32`.
|
* `%` (`expr % expr`): arithmetic remainder. Overloadable (`Rem`).
|
||||||
* `*`
|
* `%=` (`var %= expr`): arithmetic remainder and assignment. Overloadable (`RemAssign`).
|
||||||
: Dereference. When applied to a pointer, it denotes the pointed-to location.
|
* `&` (`&expr`, `&mut expr`): borrow.
|
||||||
For pointers to mutable locations, the resulting value can be assigned to.
|
* `&` (`&type`, `&mut type`, `&'a type`, `&'a mut type`): borrowed pointer type.
|
||||||
On non-pointer types, it calls the `deref` method of the `std::ops::Deref`
|
* `&` (`expr & expr`): bitwise AND. Overloadable (`BitAnd`).
|
||||||
trait, or the `deref_mut` method of the `std::ops::DerefMut` trait (if
|
* `&=` (`var &= expr`): bitwise AND and assignment. Overloadable (`BitAndAssign`).
|
||||||
implemented by the type and required for an outer expression that will or
|
* `&&` (`expr && expr`): logical AND.
|
||||||
could mutate the dereference), and produces the result of dereferencing the
|
* `*` (`expr * expr`): arithmetic multiplication. Overloadable (`Mul`).
|
||||||
`&` or `&mut` borrowed pointer returned from the overload method.
|
* `*` (`*expr`): dereference.
|
||||||
* `!`
|
* `*` (`*const type`, `*mut type`): raw pointer.
|
||||||
: Logical negation. On the boolean type, this flips between `true` and
|
* `*=` (`var *= expr`): arithmetic multiplication and assignment. Overloadable (`MulAssign`).
|
||||||
`false`. On integer types, this inverts the individual bits in the
|
* `+` (`trait + trait`, `'a + trait`): compound type constraint.
|
||||||
two’s complement representation of the value.
|
* `+` (`expr + expr`): arithmetic addition. Overloadable (`Add`).
|
||||||
* `&` and `&mut`
|
* `+=` (`var += expr`): arithmetic addition and assignment. Overloadable (`AddAssign`).
|
||||||
: Borrowing. When applied to a value, these operators produce a
|
* `,`: argument and element separator.
|
||||||
reference (pointer) to that value. The value is also placed into
|
* `-` (`- expr`): arithmetic negation. Overloadable (`Neg`).
|
||||||
a borrowed state for the duration of the reference. For a shared
|
* `-` (`expr - expr`): arithmetic subtraction. Overloadable (`Sub`).
|
||||||
borrow (`&`), this implies that the value may not be mutated, but
|
* `-=` (`var -= expr`): arithmetic subtraction and assignment. Overloadable (`SubAssign`).
|
||||||
it may be read or shared again. For a mutable borrow (`&mut`), the
|
* `->` (`fn(…) -> type`, `|…| -> type`): function and closure return type.
|
||||||
value may not be accessed in any way until the borrow expires.
|
* `.` (`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,
|
#### Path-related Syntax
|
||||||
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.
|
|
||||||
|
|
||||||
* `+`
|
* `ident::ident`: namespace path.
|
||||||
: Addition and array/string concatenation.
|
* `::path`: path relative to the crate root (*i.e.* an explicitly absolute path).
|
||||||
Calls the `add` method on the `std::ops::Add` trait.
|
* `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.
|
||||||
: Subtraction.
|
* `type::ident`, `<type as trait>::ident`: associated constants, functions, and types.
|
||||||
Calls the `sub` method on the `std::ops::Sub` trait.
|
* `<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.
|
||||||
: Multiplication.
|
* `type::method(…)`: disambiguating a method call by naming the type for which it’s defined.
|
||||||
Calls the `mul` method on the `std::ops::Mul` trait.
|
* `<type as trait>::method(…)`: disambiguating a method call by naming the trait *and* type.
|
||||||
* `/`
|
|
||||||
: Quotient.
|
|
||||||
Calls the `div` method on the `std::ops::Div` trait.
|
|
||||||
* `%`
|
|
||||||
: Remainder.
|
|
||||||
Calls the `rem` method on the `std::ops::Rem` trait.
|
|
||||||
|
|
||||||
Note that Rust does not have a built-in operator for exponential (power)
|
#### Generics
|
||||||
calculation; see the `pow` method on the numeric types.
|
|
||||||
|
|
||||||
#### 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
|
#### Trait Bound Constraints
|
||||||
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.
|
|
||||||
|
|
||||||
* `&`
|
* `T: U`: generic parameter `T` constrained to types that implement `U`.
|
||||||
: Bitwise AND.
|
* `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`.
|
||||||
Calls the `bitand` method of the `std::ops::BitAnd` trait.
|
* `T : 'static`: The generic type `T` contains no borrowed references other than `'static` ones.
|
||||||
* `|`
|
* `'b: 'a`: generic lifetime `'b` must outlive lifetime `'a`.
|
||||||
: Bitwise inclusive OR.
|
* `T: ?Sized`: allow generic type parameter to be a dynamically-sized type.
|
||||||
Calls the `bitor` method of the `std::ops::BitOr` trait.
|
* `'a + trait`, `trait + trait`: compound type constraint.
|
||||||
* `^`
|
|
||||||
: 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.
|
|
||||||
|
|
||||||
#### Lazy boolean operators
|
#### Macros and Attributes
|
||||||
|
|
||||||
The operators `||` and `&&` may be applied to operands of boolean type. The
|
* `#[meta]`: outer attribute.
|
||||||
`||` operator denotes logical ‘or’, and the `&&` operator denotes logical
|
* `#![meta]`: inner attribute.
|
||||||
‘and’. They differ from `|` and `&` in that the right-hand operand is only
|
* `$ident`: macro substitution.
|
||||||
evaluated when the left-hand operand does not already determine the result of
|
* `$ident:kind`: macro capture.
|
||||||
the expression. That is, `||` only evaluates its right-hand operand when the
|
* `$(…)…`: macro repetition.
|
||||||
left-hand operand evaluates to `false`, and `&&` only when it evaluates to
|
|
||||||
`true`.
|
|
||||||
|
|
||||||
#### Comparison operators
|
#### Comments
|
||||||
|
|
||||||
Comparison operators are, like the arithmetic operators and bitwise operators,
|
* `//`: line comment.
|
||||||
syntactic sugar for calls to built-in traits. This means that comparison
|
* `//!`: inner line doc comment.
|
||||||
operators can be overridden for user-defined types. The default meaning of the
|
* `///`: outer line doc comment.
|
||||||
operators on standard types is given here.
|
* `/*…*/`: block comment.
|
||||||
|
* `/*!…*/`: inner block doc comment.
|
||||||
|
* `/**…*/`: outer block doc comment.
|
||||||
|
|
||||||
* `==`
|
#### Tuples
|
||||||
: 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.
|
|
||||||
|
|
||||||
#### 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
|
* `{…}`: block expression.
|
||||||
on the right-hand side.
|
* `Type {…}`: `struct` literal.
|
||||||
|
|
||||||
An example of an `as` expression:
|
#### Square Brackets
|
||||||
|
|
||||||
```rust
|
* `[…]`: array literal.
|
||||||
# fn sum(values: &[f64]) -> f64 { 0.0 }
|
* `[expr; len]`: array literal containing `len` copies of `expr`.
|
||||||
# fn len(values: &[f64]) -> i32 { 0 }
|
* `[type; len]`: array type containing `len` instances of `type`.
|
||||||
|
* `expr[expr]`: collection indexing. Overloadable (`Index`, `IndexMut`).
|
||||||
fn average(values: &[f64]) -> f64 {
|
* `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]`: collection indexing pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, `RangeFull` as the “index”.
|
||||||
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 |
|
|
||||||
|
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/rinthel/rust-lang-book-ko)
|
||||||
- [日本語](https://github.com/hazama-yuinyan/book)
|
- [日本語](https://github.com/hazama-yuinyan/book)
|
||||||
- [Français](https://github.com/quadrifoglio/rust-book-fr)
|
- [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!
|
Rust is installed now. Great!
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, if you disapprove of the `curl | sh` pattern, you can download, inspect
|
Of course, if you distrust using `curl URL | sh` to install software, you can download,
|
||||||
and run the script however you like.
|
inspect, and run the script however you like.
|
||||||
|
|
||||||
The installation script automatically adds Rust to your system PATH after your next login.
|
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:
|
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
|
```cmd
|
||||||
> dir /B %= the /B option says to only show the file names =%
|
> dir /B %= the /B option says to only show the file names =%
|
||||||
main.exe
|
main.exe
|
||||||
|
main.pdb
|
||||||
main.rs
|
main.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -316,6 +317,7 @@ program through Cargo! To do so, enter the following commands:
|
|||||||
```text
|
```text
|
||||||
$ cargo build
|
$ cargo build
|
||||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
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
|
This should have created an executable file in *target/debug/hello_cargo* (or
|
||||||
@ -350,6 +352,7 @@ and then run:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo run
|
$ cargo run
|
||||||
|
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||||
Running `target/debug/hello_cargo`
|
Running `target/debug/hello_cargo`
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
@ -363,6 +366,7 @@ this:
|
|||||||
```text
|
```text
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
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`
|
Running `target/debug/hello_cargo`
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
@ -36,7 +36,7 @@ You’ll see different type annotations as we discuss the various data types.
|
|||||||
### Scalar Types
|
### Scalar Types
|
||||||
|
|
||||||
A *scalar* type represents a single value. Rust has four primary 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
|
recognize these from other programming languages, but let’s jump into how they
|
||||||
work in Rust.
|
work in Rust.
|
||||||
|
|
||||||
@ -156,8 +156,8 @@ list of all operators that Rust provides.
|
|||||||
|
|
||||||
#### The Boolean Type
|
#### The Boolean Type
|
||||||
|
|
||||||
As in most other programming languages, a boolean type in Rust has two possible
|
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`.
|
values: `true` and `false`. The Boolean type in Rust is specified using `bool`.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<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”
|
expression. We’ll cover how `if` expressions work in Rust in the “Control Flow”
|
||||||
section.
|
section.
|
||||||
|
|
||||||
|
@ -102,9 +102,9 @@ error[E0308]: mismatched types
|
|||||||
```
|
```
|
||||||
|
|
||||||
The error indicates that Rust expected a `bool` but got an integer. Rust will
|
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
|
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`
|
only when a number is not equal to `0`, for example, we can change the `if`
|
||||||
expression to the following:
|
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`:
|
`Copy`. Here are some of the types that are `Copy`:
|
||||||
|
|
||||||
* All the integer types, like `u32`.
|
* 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`.
|
* The character type, `char`.
|
||||||
* All the floating point types, like `f64`.
|
* All the floating point types, like `f64`.
|
||||||
* Tuples, but only if they contain types that are also `Copy`. `(i32, i32)` is
|
* 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),
|
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
|
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
|
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`,
|
`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
|
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||||
Listing 5-13, shown in Listing 5-15:
|
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
|
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
|
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
|
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.
|
possible cases are handled.
|
||||||
|
|
||||||
Think of a `match` expression kind of like a coin sorting machine: coins slide
|
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
|
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
|
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
|
`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
|
Here, it can be any type. The type of `coin` in this example is the `Coin` enum
|
||||||
that we defined in Listing 6-3.
|
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.
|
modules from *src/lib.rs* and place them into their own files.
|
||||||
|
|
||||||
First, replace the `client` module code with only the declaration of the
|
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>
|
<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
|
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
|
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
|
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
|
```text
|
||||||
$ cargo build
|
$ 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>
|
submodule into *src/server.rs*</span>
|
||||||
|
|
||||||
The error says we `cannot declare a new module at this location` and is
|
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
|
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.
|
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:
|
points out something we haven’t yet talked about doing:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## Controlling Visibility with `pub`
|
## 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
|
`network::server` code into the *src/network/mod.rs* and
|
||||||
*src/network/server.rs* files, respectively. At that point, `cargo build` was
|
*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
|
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
|
### Privacy Examples
|
||||||
|
|
||||||
Let’s look at a few more privacy examples to get some practice. Create a new
|
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*:
|
*src/lib.rs*:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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>
|
some of which are incorrect</span>
|
||||||
|
|
||||||
Before you try to compile this code, make a guess about which lines in the
|
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
|
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
|
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>
|
<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>
|
enclosing module’s path</span>
|
||||||
|
|
||||||
As you can see, referring to the fully qualified name can get quite lengthy.
|
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
|
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
|
`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>
|
<span class="filename">Filename: src/lib.rs</span>
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ $ cargo test
|
|||||||
running 1 test
|
running 1 test
|
||||||
test tests::it_works ... ok
|
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
|
## 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 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
|
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
|
a vector that holds five elements and then tries to access an element at index
|
||||||
100, as shown in Listing 8-6:
|
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
|
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
|
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
|
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:
|
work:
|
||||||
|
|
||||||
```rust,ignore
|
```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
|
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
|
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
|
the `panic!` macro that stops execution when it encounters unrecoverable
|
||||||
errors. This chapter covers calling `panic!` first and then talks about
|
errors. This chapter covers calling `panic!` first and then talks about
|
||||||
returning `Result<T, E>` values. Additionally, we’ll explore considerations to
|
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)
|
Compiling panic v0.1.0 (file:///projects/panic)
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs
|
Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs
|
||||||
Running `target/debug/panic`
|
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.
|
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
|
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
|
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
|
where the panic occurred: *src/main.rs:2:4* indicates that it’s the second
|
||||||
of our *src/main.rs* file.
|
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
|
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
|
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() {
|
fn main() {
|
||||||
let v = vec![1, 2, 3];
|
let v = vec![1, 2, 3];
|
||||||
|
|
||||||
v[100];
|
v[99];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 9-1: Attempting to access an element beyond the
|
<span class="caption">Listing 9-1: Attempting to access an element beyond the
|
||||||
end of a vector, which will cause a `panic!`</span>
|
end of a vector, which will cause a `panic!`</span>
|
||||||
|
|
||||||
Here, we’re attempting to access the hundredth element of our vector, but it
|
Here, we’re attempting to access the hundredth element of our vector (which is
|
||||||
has only three elements. In this situation, Rust will panic. Using `[]` is
|
at index 99 because indexing starts at zero), but it has only three elements.
|
||||||
supposed to return an element, but if you pass an invalid index, there’s no
|
In this situation, Rust will panic. Using `[]` is supposed to return an
|
||||||
element that Rust could return here that would be correct.
|
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
|
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
|
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
|
Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs
|
||||||
Running `target/debug/panic`
|
Running `target/debug/panic`
|
||||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
|
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.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
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
|
This error points at a file we didn’t write, *vec.rs*. That’s the
|
||||||
the implementation of `Vec<T>` in the standard library. The code that gets run
|
implementation of `Vec<T>` in the standard library. The code that gets run when
|
||||||
when we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is
|
we use `[]` on our vector `v` is in *vec.rs*, and that is where the `panic!` is
|
||||||
where the `panic!` is actually happening.
|
actually happening.
|
||||||
|
|
||||||
The next note line tells us that we can set the `RUST_BACKTRACE` environment
|
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
|
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
|
$ RUST_BACKTRACE=1 cargo run
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||||
Running `target/debug/panic`
|
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:
|
stack backtrace:
|
||||||
1: 0x560ed90ec04c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
|
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
|
||||||
at /stable-dist-rustc/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
|
at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
|
||||||
2: 0x560ed90ee03e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
|
1: std::sys_common::backtrace::_print
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:351
|
at /checkout/src/libstd/sys_common/backtrace.rs:71
|
||||||
3: 0x560ed90edc44 - std::panicking::default_hook::h1670459d2f3f8843
|
2: std::panicking::default_hook::{{closure}}
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:367
|
at /checkout/src/libstd/sys_common/backtrace.rs:60
|
||||||
4: 0x560ed90ee41b - std::panicking::rust_panic_with_hook::hcf0ddb069e7abcd7
|
at /checkout/src/libstd/panicking.rs:381
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:555
|
3: std::panicking::default_hook
|
||||||
5: 0x560ed90ee2b4 - std::panicking::begin_panic::hd6eb68e27bdf6140
|
at /checkout/src/libstd/panicking.rs:397
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:517
|
4: std::panicking::rust_panic_with_hook
|
||||||
6: 0x560ed90ee1d9 - std::panicking::begin_panic_fmt::abcd5965948b877f8
|
at /checkout/src/libstd/panicking.rs:611
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:501
|
5: std::panicking::begin_panic
|
||||||
7: 0x560ed90ee167 - rust_begin_unwind
|
at /checkout/src/libstd/panicking.rs:572
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:477
|
6: std::panicking::begin_panic_fmt
|
||||||
8: 0x560ed911401d - core::panicking::panic_fmt::hc0f6d7b2c300cdd9
|
at /checkout/src/libstd/panicking.rs:522
|
||||||
at /stable-dist-rustc/build/src/libcore/panicking.rs:69
|
7: rust_begin_unwind
|
||||||
9: 0x560ed9113fc8 - core::panicking::panic_bounds_check::h02a4af86d01b3e96
|
at /checkout/src/libstd/panicking.rs:498
|
||||||
at /stable-dist-rustc/build/src/libcore/panicking.rs:56
|
8: core::panicking::panic_fmt
|
||||||
10: 0x560ed90e71c5 - <collections::vec::Vec<T> as core::ops::Index<usize>>::index::h98abcd4e2a74c41
|
at /checkout/src/libcore/panicking.rs:71
|
||||||
at /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
9: core::panicking::panic_bounds_check
|
||||||
11: 0x560ed90e727a - panic::main::h5d6b77c20526bc35
|
at /checkout/src/libcore/panicking.rs:58
|
||||||
at /home/you/projects/panic/src/main.rs:4
|
10: <alloc::vec::Vec<T> as core::ops::index::Index<usize>>::index
|
||||||
12: 0x560ed90f5d6a - __rust_maybe_catch_panic
|
at /checkout/src/liballoc/vec.rs:1555
|
||||||
at /stable-dist-rustc/build/src/libpanic_unwind/lib.rs:98
|
11: panic::main
|
||||||
13: 0x560ed90ee926 - std::rt::lang_start::hd7c880a37a646e81
|
at src/main.rs:4
|
||||||
at /stable-dist-rustc/build/src/libstd/panicking.rs:436
|
12: __rust_maybe_catch_panic
|
||||||
at /stable-dist-rustc/build/src/libstd/panic.rs:361
|
at /checkout/src/libpanic_unwind/lib.rs:99
|
||||||
at /stable-dist-rustc/build/src/libstd/rt.rs:57
|
13: std::rt::lang_start
|
||||||
14: 0x560ed90e7302 - main
|
at /checkout/src/libstd/panicking.rs:459
|
||||||
15: 0x7f0d53f16400 - __libc_start_main
|
at /checkout/src/libstd/panic.rs:361
|
||||||
16: 0x560ed90e6659 - _start
|
at /checkout/src/libstd/rt.rs:61
|
||||||
17: 0x0 - <unknown>
|
14: main
|
||||||
|
15: __libc_start_main
|
||||||
|
16: <unknown>
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 9-2: The backtrace generated by a call to
|
<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
|
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
|
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
|
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
|
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
|
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.
|
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
|
operation fails because the file doesn’t exist, we might want to create the
|
||||||
file instead of terminating the process.
|
file instead of terminating the process.
|
||||||
|
|
||||||
Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result`
|
Recall from “[Handling Potential Failure with the `Result`
|
||||||
Type][handle_failure]<!-- ignore -->” section that the `Result` enum is defined
|
Type][handle_failure]<!-- ignore -->” in Chapter 2 that the `Result` enum is
|
||||||
as having two variants, `Ok` and `Err`, as follows:
|
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
|
[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`
|
`std::result::Result`
|
||||||
|
|
|
|
||||||
= note: expected type `u32`
|
= 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>`.
|
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
|
```text
|
||||||
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
|
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
|
||||||
repr: Os { code: 2, message: "No such file or directory" } }',
|
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
|
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
|
```text
|
||||||
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||||
2, message: "No such file or directory" } }',
|
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
|
||||||
/stable-dist-rustc/build/src/libcore/result.rs:868
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Because this error message starts with the text we specified, `Failed to open
|
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
|
function as if we had used the `return` keyword so the error value gets
|
||||||
propagated to the calling code.
|
propagated to the calling code.
|
||||||
|
|
||||||
The one difference between the `match` expression from Listing 9-6 and what the
|
There is a difference between what the `match` expression from Listing 9-6 and
|
||||||
question mark operator does is that when using the question mark operator,
|
the question mark operator do: error values used with `?` go through the `from`
|
||||||
error values go through the `from` function defined in the `From` trait in the
|
function, defined in the `From` trait in the standard library, which is used to
|
||||||
standard library. Many error types implement the `from` function to convert an
|
convert errors from one type into another. When the question mark calls the
|
||||||
error of one type into an error of another type. When used by the question mark
|
`from` function, the error type received is converted into the error type
|
||||||
operator, the call to the `from` function converts the error type that the
|
defined in the return type of the current function. This is useful when a
|
||||||
question mark operator gets into the error type defined in the return type of
|
function returns one error type to represent all the ways a function might
|
||||||
the current function that we’re using `?` in. This is useful when parts of a
|
fail, even if parts might fail for many different reasons. As long as each
|
||||||
function might fail for many different reasons, but the function returns one
|
error type implements the `from` function to define how to convert itself to
|
||||||
error type that represents all the ways the function might fail. As long as
|
the returned error type, the question mark operator takes care of the
|
||||||
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.
|
conversion automatically.
|
||||||
|
|
||||||
In the context of Listing 9-7, the `?` at the end of the `File::open` call will
|
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:
|
When we compile this code, we get the following error message:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
error[E0277]: the `?` operator can only be used in a function that returns
|
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
|
||||||
`Result` (or another type that implements `std::ops::Try`)
|
|
||||||
--> src/main.rs:4:13
|
--> src/main.rs:4:13
|
||||||
|
|
|
|
||||||
4 | let f = File::open("hello.txt")?;
|
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
|
| in this macro invocation
|
||||||
|
|
|
|
||||||
= help: the trait `std::ops::Try` is not implemented for `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
|
||||||
|
@ -138,7 +138,7 @@ number being in range, like so:
|
|||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
loop {
|
loop {
|
||||||
// snip
|
// --snip--
|
||||||
|
|
||||||
let guess: i32 = match guess.trim().parse() {
|
let guess: i32 = match guess.trim().parse() {
|
||||||
Ok(num) => num,
|
Ok(num) => num,
|
||||||
@ -151,7 +151,7 @@ loop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match guess.cmp(&secret_number) {
|
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>
|
<span class="filename">Filename: src/main.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::cmp::PartialOrd;
|
|
||||||
|
|
||||||
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
||||||
let mut largest = list[0];
|
let mut largest = list[0];
|
||||||
|
|
||||||
@ -505,7 +503,7 @@ similar to this code:
|
|||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
impl<T: Display> ToString for T {
|
impl<T: Display> ToString for T {
|
||||||
// ...snip...
|
// --snip--
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
# Writing Automated Tests
|
# Writing Automated Tests
|
||||||
|
|
||||||
> Program testing can be a very effective way to show the presence of bugs, but
|
In his 1972 essay, “The Humble Programmer,” Edsger W. Dijkstra said that
|
||||||
> it is hopelessly inadequate for showing their absence.
|
“Program testing can be a very effective way to show the presence of bugs, but
|
||||||
> Edsger W. Dijkstra, “The Humble Programmer” (1972)
|
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
|
||||||
Correctness in our programs means that our code does what we intend for it to
|
extent to which our code does what we intend it to do. Rust is a programming
|
||||||
do. Rust is a programming language that cares a lot about correctness, but
|
language designed with a high degree of concern about the correctness of
|
||||||
correctness is a complex topic and isn’t easy to prove. Rust’s type system
|
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
|
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
|
kind of incorrectness. As such, Rust includes support for writing automated
|
||||||
tests within the language itself.
|
software tests within the language.
|
||||||
|
|
||||||
As an example, say we write a function called `add_two` that adds two to
|
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
|
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
|
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
|
compile that function, Rust does all the type checking and borrow checking that
|
||||||
that we’ve seen so far to make sure that, for instance, we aren’t passing a
|
you’ve learned so far to ensure that, for instance, we aren’t passing a
|
||||||
`String` value or an invalid reference to this function. What Rust *can’t*
|
`String` value or an invalid reference to this function. But Rust *can’t* check
|
||||||
check is that this function will do precisely what we intend: return the
|
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
|
parameter plus two rather than, say, the parameter plus 10 or the parameter
|
||||||
minus 50! That’s where tests come in.
|
minus 50! That’s where tests come in.
|
||||||
|
|
||||||
We can write tests that assert, for example, that when we pass `3` to the
|
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
|
`add_two` function, the returned value is `5`. We can run these tests whenever
|
||||||
changes to our code to make sure any existing correct behavior has not changed.
|
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
|
Testing is a complex skill: although we can’t cover every detail about how to
|
||||||
write good tests in one chapter of a book, so here we’ll just discuss the
|
write good tests in one chapter, we’ll discuss the mechanics of Rust’s testing
|
||||||
mechanics of Rust’s testing facilities. We’ll talk about the annotations and
|
facilities. We’ll talk about the annotations and macros available to you when
|
||||||
macros available to you when writing your tests, the default behavior and
|
writing your tests, the default behavior and options provided for running your
|
||||||
options provided for running your tests, and how to organize tests into unit
|
tests, and how to organize tests into unit tests and integration tests.
|
||||||
tests and integration tests.
|
|
||||||
|
@ -1,32 +1,37 @@
|
|||||||
## How to Write Tests
|
## How to Write Tests
|
||||||
|
|
||||||
Tests are Rust functions that verify that the non-test code is functioning in
|
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,
|
the expected manner. The bodies of test functions typically perform these three
|
||||||
run the code we want to test, then assert whether the results are what we
|
actions:
|
||||||
expect. Let’s look at the features Rust provides specifically for writing
|
|
||||||
tests: the `test` attribute, a few macros, and the `should_panic` attribute.
|
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
|
### The Anatomy of a Test Function
|
||||||
|
|
||||||
At its simplest, a test in Rust is a function that’s annotated with the `test`
|
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. Attributes are metadata about pieces of Rust code; one example is
|
||||||
attribute that we used with structs in Chapter 5 is one example. To make a
|
the `derive` attribute we used with structs in Chapter 5. To change a function
|
||||||
function into a test function, we add `#[test]` on the line before `fn`. When
|
into a test function, we add `#[test]` on the line before `fn`. When we run our
|
||||||
we run our tests with the `cargo test` command, Rust will build a test runner
|
tests with the `cargo test` command, Rust builds a test runner binary that runs
|
||||||
binary that runs the functions annotated with the `test` attribute and reports
|
the functions annotated with the `test` attribute and reports on whether each
|
||||||
on whether each test function passes or fails.
|
test function passes or fails.
|
||||||
|
|
||||||
We saw in Chapter 7 that when you make a new library project with Cargo, a test
|
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 is to
|
module with a test function in it is automatically generated for us. This
|
||||||
help us get started writing our tests so we don’t have to go look up the
|
module helps us start writing our tests so we don’t have to look up the exact
|
||||||
exact structure and syntax of test functions every time we start a new project.
|
structure and syntax of test functions every time we start a new project. We
|
||||||
We can add as many additional test functions and as many test modules as we
|
can add as many additional test functions and as many test modules as we want!
|
||||||
want, though!
|
|
||||||
|
|
||||||
We’re going to explore some aspects of how tests work by experimenting with the
|
We’ll explore some aspects of how tests work by experimenting with the template
|
||||||
template test generated for us, without actually testing any code. Then we’ll
|
test generated for us without actually testing any code. Then we’ll write some
|
||||||
write some real-world tests that call some code that we’ve written and assert
|
real-world tests that call some code that we’ve written and assert that its
|
||||||
that its behavior is correct.
|
behavior is correct.
|
||||||
|
|
||||||
Let’s create a new library project called `adder`:
|
Let’s create a new library project called `adder`:
|
||||||
|
|
||||||
@ -36,8 +41,8 @@ $ cargo new adder
|
|||||||
$ cd adder
|
$ cd adder
|
||||||
```
|
```
|
||||||
|
|
||||||
The contents of the `src/lib.rs` file in your adder library should be as
|
The contents of the *src/lib.rs* file in your adder library should look like
|
||||||
follows:
|
Listing 11-1:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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
|
<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
|
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
|
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
|
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
|
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.
|
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
|
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
|
The `cargo test` command runs all tests in our project, as shown in Listing
|
||||||
Listing 11-2:
|
11-2:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test
|
$ cargo test
|
||||||
@ -77,41 +82,44 @@ $ cargo test
|
|||||||
running 1 test
|
running 1 test
|
||||||
test tests::it_works ... ok
|
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
|
Doc-tests adder
|
||||||
|
|
||||||
running 0 tests
|
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
|
<span class="caption">Listing 11-2: The output from running the automatically
|
||||||
automatically generated test</span>
|
generated test</span>
|
||||||
|
|
||||||
Cargo compiled and ran our test. After the `Compiling`, `Finished`, and
|
Cargo compiled and ran the test. After the `Compiling`, `Finished`, and
|
||||||
`Running` lines, we see the line `running 1 test`. The next line shows the name
|
`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
|
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
|
that test, `ok`. The overall summary of running the tests appears next. The
|
||||||
result: ok.` means all the tests passed. `1 passed; 0 failed` adds up the
|
text `test result: ok.` means that all the tests passed, and the portion that
|
||||||
number of tests that passed or failed.
|
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
|
Because we don’t have any tests we’ve marked as ignored, the summary shows `0
|
||||||
ignored`. We’re going to talk about ignoring tests in the next section on
|
ignored`. We also haven’t filtered the tests being run, so the end of the
|
||||||
different ways to run tests. The `0 measured` statistic is for benchmark tests
|
summary shows `0 filtered out`. We’ll talk about ignoring and filtering out
|
||||||
that measure performance. Benchmark tests are, as of this writing, only
|
tests in the next section, “Controlling How Tests Are Run.”
|
||||||
available in nightly Rust. See Chapter 1 for more information about nightly
|
|
||||||
Rust.
|
|
||||||
|
|
||||||
The next part of the test output that starts with `Doc-tests adder` is for the
|
The `0 measured` statistic is for benchmark tests that measure performance.
|
||||||
results of any documentation tests. We don’t have any documentation tests yet,
|
Benchmark tests are, as of this writing, only available in nightly Rust. See
|
||||||
but Rust can compile any code examples that appear in our API documentation.
|
Chapter 1 for more information about nightly Rust.
|
||||||
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.
|
|
||||||
|
|
||||||
Let’s change the name of our test and see how that changes the test output.
|
The next part of the test output, which starts with `Doc-tests adder`, is for
|
||||||
Give the `it_works` function a different name, such as `exploration`, like so:
|
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>
|
<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
|
Then run `cargo test` again. The output now shows `exploration` instead of
|
||||||
of `it_works`:
|
`it_works`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
test tests::exploration ... ok
|
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
|
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,
|
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
|
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
|
as failed. We talked about the simplest way to cause a panic in Chapter 9,
|
||||||
the `panic!` macro! Type in the new test so that your `src/lib.rs` now looks
|
which is to call the `panic!` macro. Enter the new test, `another`, so your
|
||||||
like Listing 11-3:
|
*src/lib.rs* file looks like Listing 11-3:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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
|
<span class="caption">Listing 11-3: Adding a second test that will fail because
|
||||||
since we call the `panic!` macro</span>
|
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:
|
11-4, which shows that our `exploration` test passed and `another` failed:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@ -173,13 +181,13 @@ test tests::another ... FAILED
|
|||||||
failures:
|
failures:
|
||||||
|
|
||||||
---- tests::another stdout ----
|
---- 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.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
|
|
||||||
failures:
|
failures:
|
||||||
tests::another
|
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
|
error: test failed
|
||||||
```
|
```
|
||||||
@ -187,34 +195,35 @@ error: test failed
|
|||||||
<span class="caption">Listing 11-4: Test results when one test passes and one
|
<span class="caption">Listing 11-4: Test results when one test passes and one
|
||||||
test fails</span>
|
test fails</span>
|
||||||
|
|
||||||
Instead of `ok`, the line `test tests::another` says `FAILED`. We have two new
|
Instead of `ok`, the line `test tests::another` shows `FAILED`. Two new
|
||||||
sections between the individual results and the summary: the first section
|
sections appear between the individual results and the summary: the first
|
||||||
displays the detailed reason for the test failures. In this case, `another`
|
section displays the detailed reason for each test failure. In this case,
|
||||||
failed because it `panicked at 'Make this test fail'`, which happened on
|
`another` failed because it `panicked at 'Make this test fail'`, which happened
|
||||||
*src/lib.rs* line 9. The next section lists just the names of all the failing
|
on line 10 in the *src/lib.rs* file. The next section lists just the names of
|
||||||
tests, which is useful when there are lots of tests and lots of detailed
|
all the failing tests, which is useful when there are lots of tests and lots of
|
||||||
failing test output. We can use the name of a failing test to run just that
|
detailed failing test output. We can use the name of a failing test to run just
|
||||||
test in order to more easily debug it; we’ll talk more about ways to run tests
|
that test to more easily debug it; we’ll talk more about ways to run tests in
|
||||||
in the next section.
|
the “Controlling How Tests Are Run” section.
|
||||||
|
|
||||||
Finally, we have the summary line: overall, our test result is `FAILED`. We had
|
The summary line displays at the end: overall, our test result is `FAILED`.
|
||||||
1 test pass and 1 test fail.
|
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.
|
let’s look at some macros other than `panic!` that are useful in tests.
|
||||||
|
|
||||||
### Checking Results with the `assert!` Macro
|
### Checking Results with the `assert!` Macro
|
||||||
|
|
||||||
The `assert!` macro, provided by the standard library, is useful when you want
|
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
|
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!` macro an argument that evaluates to a Boolean. If the value is
|
||||||
`assert!` does nothing and the test passes. If the value is `false`, `assert!`
|
`true`, `assert!` does nothing and the test passes. If the value is `false`,
|
||||||
calls the `panic!` macro, which causes the test to fail. This is one macro that
|
the `assert!` macro calls the `panic!` macro, which causes the test to fail.
|
||||||
helps us check that our code is functioning in the way we intend.
|
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`
|
In Chapter 5, Listing 5-15, we used a `Rectangle` struct and a `can_hold`
|
||||||
struct and a `can_hold` method, repeated here in Listing 11-5. Let’s put this
|
method, which are repeated here in Listing 11-5. Let’s put this code in the
|
||||||
code in *src/lib.rs* and write some tests for it using the `assert!` macro.
|
*src/lib.rs* file and write some tests for it using the `assert!` macro.
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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`
|
<span class="caption">Listing 11-5: Using the `Rectangle` struct and its
|
||||||
method from Chapter 5</span>
|
`can_hold` method from Chapter 5</span>
|
||||||
|
|
||||||
The `can_hold` method returns a boolean, which means it’s a perfect use case
|
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
|
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
|
`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
|
a width of 7, and asserting that it can hold another `Rectangle` instance that
|
||||||
has a length of 5 and a width of 1:
|
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
|
<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::*;`.
|
Note that we’ve added a new line inside the `tests` module: the `use super::*;`
|
||||||
The `tests` module is a regular module that follows the usual visibility rules
|
line. The `tests` module is a regular module that follows the usual visibility
|
||||||
we covered in Chapter 7. Because we’re in an inner module, we need to bring the
|
rules we covered in Chapter 7 in the “Privacy Rules” section. Because the
|
||||||
code under test in the outer module into the scope of the inner module. We’ve
|
`tests` module is an inner module, we need to bring the code under test in the
|
||||||
chosen to use a glob here so that anything we define in the outer module is
|
outer module into the scope of the inner module. We use a glob here so anything
|
||||||
available to this `tests` module.
|
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
|
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
|
`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
|
passed it the result of calling `larger.can_hold(&smaller)`. This expression
|
||||||
supposed to return `true`, so our test should pass. Let’s find out!
|
is supposed to return `true`, so our test should pass. Let’s find out!
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
test tests::larger_can_hold_smaller ... ok
|
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
|
It does pass! Let’s add another test, this time asserting that a smaller
|
||||||
@ -292,10 +301,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn larger_can_hold_smaller() {
|
fn larger_can_hold_smaller() {
|
||||||
let larger = Rectangle { length: 8, width: 7 };
|
// --snip--
|
||||||
let smaller = Rectangle { length: 5, width: 1 };
|
|
||||||
|
|
||||||
assert!(larger.can_hold(&smaller));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -309,28 +315,29 @@ mod tests {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Because the correct result of the `can_hold` function in this case is `false`,
|
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
|
we need to negate that result before we pass it to the `assert!` macro. As a
|
||||||
way, our test will pass if `can_hold` returns `false`:
|
result, our test will pass if `can_hold` returns `false`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 2 tests
|
running 2 tests
|
||||||
test tests::smaller_cannot_hold_larger ... ok
|
test tests::smaller_cannot_hold_larger ... ok
|
||||||
test tests::larger_can_hold_smaller ... 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`
|
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
|
method by replacing the greater-than sign with a less-than sign when it
|
||||||
supposed to have a greater-than sign:
|
compares the lengths:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
pub struct Rectangle {
|
# pub struct Rectangle {
|
||||||
length: u32,
|
# length: u32,
|
||||||
width: u32,
|
# width: u32,
|
||||||
}
|
# }
|
||||||
|
// --snip--
|
||||||
|
|
||||||
impl Rectangle {
|
impl Rectangle {
|
||||||
pub fn can_hold(&self, other: &Rectangle) -> bool {
|
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
|
```text
|
||||||
running 2 tests
|
running 2 tests
|
||||||
@ -350,35 +357,35 @@ failures:
|
|||||||
|
|
||||||
---- tests::larger_can_hold_smaller stdout ----
|
---- tests::larger_can_hold_smaller stdout ----
|
||||||
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
|
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
|
||||||
larger.can_hold(&smaller)', src/lib.rs:22
|
larger.can_hold(&smaller)', src/lib.rs:22:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
|
|
||||||
failures:
|
failures:
|
||||||
tests::larger_can_hold_smaller
|
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,
|
Our tests caught the bug! Because `larger.length` is 8 and `smaller.length` is
|
||||||
the comparison of the lengths in `can_hold` now returns `false` since 8 is not
|
5, the comparison of the lengths in `can_hold` now returns `false`: 8 is not
|
||||||
less than 5.
|
less than 5.
|
||||||
|
|
||||||
### Testing Equality with the `assert_eq!` and `assert_ne!` Macros
|
### 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
|
A common way to test functionality is to compare the result of the code under
|
||||||
and the value we expect the code to return and check that they’re equal. We
|
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
|
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
|
`==` 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!`
|
provides a pair of macros—`assert_eq!` and `assert_ne!`—to perform this test
|
||||||
and `assert_ne!`. These macros compare two arguments for equality or
|
more conveniently. These macros compare two arguments for equality or
|
||||||
inequality, respectively. They’ll also print out the two values if the
|
inequality, respectively. They’ll also print the two values if the assertion
|
||||||
assertion fails, so that it’s easier to see *why* the test failed, while the
|
fails, which makes it easier to see *why* the test failed; conversely, the
|
||||||
`assert!` macro only tells us that it got a `false` value for the `==`
|
`assert!` macro only indicates that it got a `false` value for the `==`
|
||||||
expression, not the values that lead to the `false` value.
|
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
|
In Listing 11-7, we write a function named `add_two` that adds `2` to its
|
||||||
parameter and returns the result. Then let’s test this function using the
|
parameter and returns the result. Then we test this function using the
|
||||||
`assert_eq!` macro:
|
`assert_eq!` macro.
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<span class="filename">Filename: src/lib.rs</span>
|
||||||
|
|
||||||
@ -407,16 +414,16 @@ Let’s check that it passes!
|
|||||||
running 1 test
|
running 1 test
|
||||||
test tests::it_adds_two ... ok
|
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
|
The first argument we gave to the `assert_eq!` macro, `4`, is equal to the
|
||||||
of calling `add_two(2)`. We see a line for this test that says `test
|
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!
|
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
|
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
|
uses `assert_eq!` fails. Change the implementation of the `add_two` function to
|
||||||
instead add 3:
|
instead add `3`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn add_two(a: i32) -> i32 {
|
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
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
@ -433,62 +440,64 @@ test tests::it_adds_two ... FAILED
|
|||||||
failures:
|
failures:
|
||||||
|
|
||||||
---- tests::it_adds_two stdout ----
|
---- tests::it_adds_two stdout ----
|
||||||
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left ==
|
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
|
||||||
right)` (left: `4`, right: `5`)', src/lib.rs:11
|
left: `4`,
|
||||||
|
right: `5`', src/lib.rs:11:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
|
|
||||||
failures:
|
failures:
|
||||||
tests::it_adds_two
|
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 ``
|
Our test caught the bug! The `it_adds_two` test failed, displaying the message
|
||||||
assertion failed: `(left == right)` (left: `4`, right: `5`) ``. This message is
|
`` assertion failed: `(left == right)` `` and showing that `left` was `4` and
|
||||||
useful and helps us get started debugging: it says the `left` argument to
|
`right` was `5`. This message is useful and helps us start debugging: it means
|
||||||
`assert_eq!` was 4, but the `right` argument, where we had `add_two(2)`, was 5.
|
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
|
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,
|
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
|
they’re called `left` and `right`, and the order in which we specify the value
|
||||||
the value we expect and the value that the code under test produces doesn’t
|
we expect and the value that the code under test produces doesn’t matter. We
|
||||||
matter. We could write the assertion in this test as
|
could write the assertion in this test as `assert_eq!(add_two(2), 4)`, which
|
||||||
`assert_eq!(add_two(2), 4)`, which would result in a failure message that says
|
would result in a failure message that displays `` assertion failed: `(left ==
|
||||||
`` assertion failed: `(left == right)` (left: `5`, right: `4`) ``.
|
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
|
The `assert_ne!` macro will pass if the two values we give it are not equal and
|
||||||
and fail if they are equal. This macro is most useful for cases when we’re not
|
fail if they’re equal. This macro is most useful for cases when we’re not sure
|
||||||
sure exactly what a value *will* be, but we know what the value definitely
|
what a value *will* be, but we know what the value definitely *won’t* be if our
|
||||||
*won’t* be, if our code is functioning as we intend. For example, if we have a
|
code is functioning as we intend. For example, if we’re testing a function that
|
||||||
function that is guaranteed to change its input in some way, but the way in
|
is guaranteed to change its input in some way, but the way in which the input
|
||||||
which the input is changed depends on the day of the week that we run our
|
is changed depends on the day of the week that we run our tests, the best thing
|
||||||
tests, the best thing to assert might be that the output of the function is not
|
to assert might be that the output of the function is not equal to the input.
|
||||||
equal to the input.
|
|
||||||
|
|
||||||
Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators
|
Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators
|
||||||
`==` and `!=`, respectively. When the assertions fail, these macros print their
|
`==` and `!=`, respectively. When the assertions fail, these macros print their
|
||||||
arguments using debug formatting, which means the values being compared must
|
arguments using debug formatting, which means the values being compared must
|
||||||
implement the `PartialEq` and `Debug` traits. All of the primitive types and
|
implement the `PartialEq` and `Debug` traits. All the primitive types and most
|
||||||
most of the standard library types implement these traits. For structs and
|
of the standard library types implement these traits. For structs and enums
|
||||||
enums that you define, you’ll need to implement `PartialEq` in order to be able
|
that you define, you’ll need to implement `PartialEq` to assert that values of
|
||||||
to assert that values of those types are equal or not equal. You’ll need to
|
those types are equal or not equal. You’ll need to implement `Debug` to print
|
||||||
implement `Debug` in order to be able to print out the values in the case that
|
out the values when the assertion fails. Because both traits are derivable
|
||||||
the assertion fails. Because both of these traits are derivable traits, as we
|
traits, as mentioned in Listing 5-12 in Chapter 5, this is usually as
|
||||||
mentioned in Chapter 5, this is usually as straightforward as adding the
|
straightforward as adding the `#[derive(PartialEq, Debug)]` annotation to your
|
||||||
`#[derive(PartialEq, Debug)]` annotation to your struct or enum definition. See
|
struct or enum definition. See Appendix C for more details about these and
|
||||||
Appendix C for more details about these and other derivable traits.
|
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
|
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
|
optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any
|
||||||
specified after the one required argument to `assert!` or the two required
|
arguments specified after the one required argument to `assert!` or the two
|
||||||
arguments to `assert_eq!` and `assert_ne!` are passed along to the `format!`
|
required arguments to `assert_eq!` and `assert_ne!` are passed along to the
|
||||||
macro that we talked about in Chapter 8, so you can pass a format string that
|
`format!` macro (discussed in Chapter 8 in the “Concatenation with the `+`
|
||||||
contains `{}` placeholders and values to go in the placeholders. Custom
|
Operator or the `format!` Macro” section), so you can pass a format string that
|
||||||
messages are useful in order to document what an assertion means, so that when
|
contains `{}` placeholders and values to go in those placeholders. Custom
|
||||||
the test fails, we have a better idea of what the problem is with the code.
|
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
|
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:
|
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
|
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
|
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
|
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 `greeting` function, we’ll just assert that the output contains the text of
|
||||||
the text of the input parameter.
|
the input parameter.
|
||||||
|
|
||||||
Let’s introduce a bug into this code to see what this test failure looks like,
|
Let’s introduce a bug into this code by changing `greeting` to not include
|
||||||
by changing `greeting` to not include `name`:
|
`name` to see what this test failure looks like:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn greeting(name: &str) -> String {
|
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
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
@ -538,18 +547,18 @@ failures:
|
|||||||
|
|
||||||
---- tests::greeting_contains_name stdout ----
|
---- tests::greeting_contains_name stdout ----
|
||||||
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
||||||
result.contains("Carol")', src/lib.rs:12
|
result.contains("Carol")', src/lib.rs:12:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
|
|
||||||
failures:
|
failures:
|
||||||
tests::greeting_contains_name
|
tests::greeting_contains_name
|
||||||
```
|
```
|
||||||
|
|
||||||
This just tells us that the assertion failed and which line the assertion is
|
This result just indicates that the assertion failed and which line the
|
||||||
on. A more useful failure message in this case would print the value we did get
|
assertion is on. A more useful failure message in this case would print the
|
||||||
from the `greeting` function. Let’s change the test function to have a custom
|
value we got from the `greeting` function. Let’s change the test function,
|
||||||
failure message made from a format string with a placeholder filled in with the
|
giving it a custom failure message made from a format string with a placeholder
|
||||||
actual value we got from the `greeting` function:
|
filled in with the actual value we got from the `greeting` function:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
#[test]
|
#[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
|
```text
|
||||||
---- tests::greeting_contains_name stdout ----
|
---- tests::greeting_contains_name stdout ----
|
||||||
thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain
|
thread 'tests::greeting_contains_name' panicked at 'Greeting did not
|
||||||
name, value was `Hello`', src/lib.rs:12
|
contain name, value was `Hello!`', src/lib.rs:12:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
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,
|
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
|
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
|
expect. For example, consider the `Guess` type that we created in Chapter 9,
|
||||||
Listing 9-8. Other code that uses `Guess` is depending on the guarantee that
|
Listing 9-9. Other code that uses `Guess` depends on the guarantee that `Guess`
|
||||||
`Guess` instances will only contain values between 1 and 100. We can write a
|
instances will only contain values between 1 and 100. We can write a test that
|
||||||
test that ensures that attempting to create a `Guess` instance with a value
|
ensures that attempting to create a `Guess` instance with a value outside that
|
||||||
outside that range panics.
|
range panics.
|
||||||
|
|
||||||
We can do this by adding another attribute, `should_panic`, to our test
|
We do this by adding another attribute, `should_panic`, to our test function.
|
||||||
function. This attribute makes a test pass if the code inside the function
|
This attribute makes a test pass if the code inside the function panics; the
|
||||||
panics, and the test will fail if the code inside the function doesn’t panic.
|
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
|
Listing 11-8 shows a test that checks that the error conditions of `Guess::new`
|
||||||
`Guess::new` happen when we expect:
|
happen when we expect:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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
|
<span class="caption">Listing 11-8: Testing that a condition will cause a
|
||||||
`panic!`</span>
|
`panic!`</span>
|
||||||
|
|
||||||
The `#[should_panic]` attribute goes after the `#[test]` attribute and before
|
We place the `#[should_panic]` attribute after the `#[test]` attribute and
|
||||||
the test function it applies to. Let’s see what it looks like when this test
|
before the test function it applies to. Let’s look at the result when this test
|
||||||
passes:
|
passes:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
test tests::greater_than_100 ... ok
|
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:
|
that the `new` function will panic if the value is greater than 100:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -644,6 +653,8 @@ that the `new` function will panic if the value is greater than 100:
|
|||||||
# value: u32,
|
# value: u32,
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
|
// --snip--
|
||||||
|
|
||||||
impl Guess {
|
impl Guess {
|
||||||
pub fn new(value: u32) -> Guess {
|
pub fn new(value: u32) -> Guess {
|
||||||
if value < 1 {
|
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
|
```text
|
||||||
running 1 test
|
running 1 test
|
||||||
@ -668,15 +679,14 @@ failures:
|
|||||||
failures:
|
failures:
|
||||||
tests::greater_than_100
|
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
|
We don’t get a very helpful message in this case, but when we look at the test
|
||||||
function, we can see that it’s annotated with `#[should_panic]`. The failure we
|
function, we see that it’s annotated with `#[should_panic]`. The failure we got
|
||||||
got means that the code in the function, `Guess::new(200)`, did not cause a
|
means that the code in the test function did not cause a panic.
|
||||||
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
|
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
|
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`
|
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>
|
<span class="filename">Filename: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub struct Guess {
|
# pub struct Guess {
|
||||||
value: u32,
|
# value: u32,
|
||||||
}
|
# }
|
||||||
|
#
|
||||||
|
// --snip--
|
||||||
|
|
||||||
impl Guess {
|
impl Guess {
|
||||||
pub fn new(value: u32) -> 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
|
<span class="caption">Listing 11-9: Testing that a condition will cause a
|
||||||
`panic!` with a particular panic message</span>
|
`panic!` with a particular panic message</span>
|
||||||
|
|
||||||
This test will pass, because the value we put in the `expected` parameter of
|
This test will pass because the value we put in the `should_panic` attribute’s
|
||||||
the `should_panic` attribute is a substring of the message that the
|
`expected` parameter is a substring of the message that the `Guess::new`
|
||||||
`Guess::new` function panics with. We could have specified the whole panic
|
function panics with. We could have specified the entire panic message that we
|
||||||
message that we expect, which in this case would be `Guess value must be less
|
expect, which in this case would be `Guess value must be less than or equal to
|
||||||
than or equal to 100, got 200.` It depends on how much of the panic message is
|
100, got 200.` What you choose to specify in the expected parameter for
|
||||||
unique or dynamic and how precise you want your test to be. In this case, a
|
`should_panic` depends on how much of the panic message is unique or dynamic
|
||||||
substring of the panic message is enough to ensure that the code in the
|
and how precise you want your test to be. In this case, a substring of the
|
||||||
function that gets run is the `else if value > 100` case.
|
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
|
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
|
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:
|
failures:
|
||||||
|
|
||||||
---- tests::greater_than_100 stdout ----
|
---- tests::greater_than_100 stdout ----
|
||||||
thread 'tests::greater_than_100' panicked at 'Guess value must be greater
|
thread 'tests::greater_than_100' panicked at 'Guess value must be
|
||||||
than or equal to 1, got 200.', src/lib.rs:10
|
greater than or equal to 1, got 200.', src/lib.rs:11:12
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
note: Panic did not include expected string 'Guess value must be less than or
|
note: Panic did not include expected string 'Guess value must be less than or
|
||||||
equal to 100'
|
equal to 100'
|
||||||
@ -762,15 +775,15 @@ equal to 100'
|
|||||||
failures:
|
failures:
|
||||||
tests::greater_than_100
|
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,
|
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
|
but the panic message did not include the expected string `'Guess value must be
|
||||||
less than or equal to 100'`. We can see the panic message that we did get,
|
less than or equal to 100'`. The panic message that we did get in this case was
|
||||||
which in this case was `Guess value must be greater than or equal to 1, got
|
`Guess value must be greater than or equal to 1, got 200.` Now we can start
|
||||||
200.` We could then start figuring out where our bug was!
|
figuring out where our bug is!
|
||||||
|
|
||||||
Now that we’ve gone over ways to write tests, let’s look at what is happening
|
Now that you know several 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
|
when we run our tests and explore the different options we can use with `cargo
|
||||||
`cargo test`.
|
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,
|
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
|
`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
|
binary. You can specify command line options to change the default behavior of
|
||||||
test`. For example, the default behavior of the binary produced by `cargo test`
|
`cargo test`. For example, the default behavior of the binary produced by
|
||||||
is to run all the tests in parallel and capture output generated during test
|
`cargo test` is to run all the tests in parallel and capture output generated
|
||||||
runs, preventing it from being displayed to make it easier to read the output
|
during test runs, preventing the output from being displayed and making it
|
||||||
related to the test results. You can change this default behavior by specifying
|
easier to read the output related to the test results.
|
||||||
command line options.
|
|
||||||
|
|
||||||
Some command line options can be passed to `cargo test`, and some need to be
|
Some command line options go to `cargo test` and some go to the resulting test
|
||||||
passed instead to the resulting test binary. To separate these two types of
|
binary. To separate these two types of arguments, you list the arguments that
|
||||||
arguments, you list the arguments that go to `cargo test`, then the separator
|
go to `cargo test` followed by the separator `--` and then the arguments that
|
||||||
`--`, and then the arguments that go to the test binary. Running `cargo test
|
go to the test binary. Running `cargo test --help` displays the options you can
|
||||||
--help` will tell you about the options that go with `cargo test`, and running
|
use with `cargo test`, and running `cargo test -- --help` displays the options
|
||||||
`cargo test -- --help` will tell you about the options that go after the
|
you can use after the separator `--`.
|
||||||
separator `--`.
|
|
||||||
|
|
||||||
### Running Tests in Parallel or Consecutively
|
### Running Tests in Parallel or Consecutively
|
||||||
|
|
||||||
When multiple tests are run, by default they run in parallel using threads.
|
When you run multiple tests, by default they run in parallel using threads.
|
||||||
This means the tests will finish running faster, so that we can get faster
|
This means the tests will finish running faster so you can get feedback quicker
|
||||||
feedback on whether or not our code is working. Since the tests are running at
|
on whether or not your code is working. Because the tests are running at the
|
||||||
the same time, you should take care that your tests do not depend on each other
|
same time, make sure your tests don’t depend on each other or on any shared
|
||||||
or on any shared state, including a shared environment such as the current
|
state, including a shared environment, such as the current working directory or
|
||||||
working directory or environment variables.
|
environment variables.
|
||||||
|
|
||||||
For example, say each of your tests runs some code that creates a file on disk
|
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,
|
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
|
which is different in each test. Because the tests run at the same time, one
|
||||||
time, one test might overwrite the file between when another test writes and
|
test might overwrite the file between when another test writes and reads the
|
||||||
reads the file. The second test will then fail, not because the code is
|
file. The second test will then fail, not because the code is incorrect, but
|
||||||
incorrect, but because the tests have interfered with each other while running
|
because the tests have interfered with each other while running in parallel.
|
||||||
in parallel. One solution would be to make sure each test writes to a different
|
One solution is to make sure each test writes to a different file; another
|
||||||
file; another solution is to run the tests one at a time.
|
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
|
If you don’t want to run the tests in parallel or if you want more fine-grained
|
||||||
fine-grained control over the number of threads used, you can send the
|
control over the number of threads used, you can send the `--test-threads` flag
|
||||||
`--test-threads` flag and the number of threads you want to use to the test
|
and the number of threads you want to use to the test binary. Take a look at
|
||||||
binary. For example:
|
the following example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test -- --test-threads=1
|
$ cargo test -- --test-threads=1
|
||||||
```
|
```
|
||||||
|
|
||||||
We set the number of test threads to 1, telling the program not to use any
|
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
|
parallelism. Running the tests using one thread will take longer than running
|
||||||
won’t be potentially interfering with each other if they share state.
|
them in parallel, but the tests won’t interfere with each other if they share
|
||||||
|
state.
|
||||||
|
|
||||||
### Showing Function Output
|
### Showing Function Output
|
||||||
|
|
||||||
By default, if a test passes, Rust’s test library captures anything printed to
|
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
|
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
|
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
|
line that indicates the test passed. If a test fails, we’ll see whatever was
|
||||||
to standard output with the rest of the failure message.
|
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
|
As an example, Listing 11-10 has a silly function that prints the value of its
|
||||||
its parameter and then returns 10. We then have a test that passes and a test
|
parameter and returns 10, as well as a test that passes and a test that fails.
|
||||||
that fails:
|
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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
|
<span class="caption">Listing 11-10: Tests for a function that calls
|
||||||
`println!`</span>
|
`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
|
```text
|
||||||
running 2 tests
|
running 2 tests
|
||||||
@ -101,38 +99,40 @@ failures:
|
|||||||
|
|
||||||
---- tests::this_test_will_fail stdout ----
|
---- tests::this_test_will_fail stdout ----
|
||||||
I got the value 8
|
I got the value 8
|
||||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left ==
|
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
||||||
right)` (left: `5`, right: `10`)', src/lib.rs:19
|
left: `5`,
|
||||||
|
right: `10`', src/lib.rs:19:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
|
|
||||||
failures:
|
failures:
|
||||||
tests::this_test_will_fail
|
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
|
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
|
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
|
If we want to see printed values for passing tests as well, we can disable the
|
||||||
output capture behavior can be disabled by using the `--nocapture` flag:
|
output capture behavior by using the `--nocapture` flag:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test -- --nocapture
|
$ cargo test -- --nocapture
|
||||||
```
|
```
|
||||||
|
|
||||||
Running the tests from Listing 11-10 again with the `--nocapture` flag now
|
When we run the tests in Listing 11-10 again with the `--nocapture` flag, we
|
||||||
shows:
|
see the following output:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 2 tests
|
running 2 tests
|
||||||
I got the value 4
|
I got the value 4
|
||||||
I got the value 8
|
I got the value 8
|
||||||
test tests::this_test_will_pass ... ok
|
test tests::this_test_will_pass ... ok
|
||||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left ==
|
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
||||||
right)` (left: `5`, right: `10`)', src/lib.rs:19
|
left: `5`,
|
||||||
|
right: `10`', src/lib.rs:19:8
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
test tests::this_test_will_fail ... FAILED
|
test tests::this_test_will_fail ... FAILED
|
||||||
|
|
||||||
@ -141,13 +141,13 @@ failures:
|
|||||||
failures:
|
failures:
|
||||||
tests::this_test_will_fail
|
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
|
Note that the output for the tests and the test results are interleaved; the
|
||||||
because the tests are running in parallel as we talked about in the previous
|
reason is that the tests are running in parallel, as we talked about in the
|
||||||
section. Try using both the `--test-threads=1` option and the `--nocapture`
|
previous section. Try using the `--test-threads=1` option and the `--nocapture`
|
||||||
flag and see what the output looks like then!
|
flag, and see what the output looks like then!
|
||||||
|
|
||||||
### Running a Subset of Tests by Name
|
### 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.
|
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
|
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>
|
<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
|
If we run the tests without passing any arguments, as we saw earlier, all the
|
||||||
the tests will run in parallel:
|
tests will run in parallel:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
running 3 tests
|
running 3 tests
|
||||||
@ -198,7 +199,7 @@ test tests::add_two_and_two ... ok
|
|||||||
test tests::add_three_and_two ... ok
|
test tests::add_three_and_two ... ok
|
||||||
test tests::one_hundred ... 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
|
#### Running Single Tests
|
||||||
@ -213,17 +214,21 @@ $ cargo test one_hundred
|
|||||||
running 1 test
|
running 1 test
|
||||||
test tests::one_hundred ... ok
|
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
|
Only the test with the name `one_hundred` ran; the other two tests didn’t match
|
||||||
given to `cargo test` will be used.
|
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
|
#### Filtering to Run Multiple Tests
|
||||||
|
|
||||||
However, we can specify part of a test name, and any test whose name matches
|
We can specify part of a test name, and any test whose name matches that value
|
||||||
that value will get run. For example, since two of our tests’ names contain
|
will be run. For example, because two of our tests’ names contain `add`, we can
|
||||||
`add`, we can run those two by running `cargo test add`:
|
run those two by running `cargo test add`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test add
|
$ cargo test add
|
||||||
@ -234,19 +239,21 @@ running 2 tests
|
|||||||
test tests::add_two_and_two ... ok
|
test tests::add_two_and_two ... ok
|
||||||
test tests::add_three_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
|
This command ran all tests with `add` in the name name and filtered out the
|
||||||
tests appear becomes part of the test’s name, so we can run all the tests in a
|
test named `one_hundred`. Also note that the module in which tests appear
|
||||||
module by filtering on the module’s name.
|
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
|
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
|
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
|
listing as arguments all tests you do want to run, you can instead annotate the
|
||||||
time consuming tests with the `ignore` attribute to exclude them:
|
time-consuming tests using the `ignore` attribute to exclude them, as shown
|
||||||
|
here:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<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]`.
|
After `#[test]` we add the `#[ignore]` line to the test we want to exclude. Now
|
||||||
Now if we run our tests, we’ll see `it_works` runs, but `expensive_test` does
|
when we run our tests, `it_works` runs, but `expensive_test` doesn’t:
|
||||||
not:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test
|
$ cargo test
|
||||||
@ -277,17 +283,11 @@ running 2 tests
|
|||||||
test expensive_test ... ignored
|
test expensive_test ... ignored
|
||||||
test it_works ... ok
|
test it_works ... ok
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured
|
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
|
||||||
|
|
||||||
Doc-tests adder
|
|
||||||
|
|
||||||
running 0 tests
|
|
||||||
|
|
||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`expensive_test` is listed as `ignored`. If we want to run only the ignored
|
The `expensive_test` function is listed as `ignored`. If we want to run only
|
||||||
tests, we can ask for them to be run with `cargo test -- --ignored`:
|
the ignored tests, we can use `cargo test -- --ignored`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ cargo test -- --ignored
|
$ cargo test -- --ignored
|
||||||
@ -297,10 +297,10 @@ $ cargo test -- --ignored
|
|||||||
running 1 test
|
running 1 test
|
||||||
test expensive_test ... ok
|
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
|
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
|
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
|
of the `ignored` tests and you have time to wait for the results, you can run
|
||||||
choose to run `cargo test -- --ignored` instead.
|
`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