mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-06 02:13:09 +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).
|
||||||
@ -446,14 +468,14 @@ failed to run: ~/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo build --ma
|
|||||||
If you haven't used the `[patch]`
|
If you haven't used the `[patch]`
|
||||||
section of `Cargo.toml` before, there is [some relevant documentation about it
|
section of `Cargo.toml` before, there is [some relevant documentation about it
|
||||||
in the cargo docs](http://doc.crates.io/manifest.html#the-patch-section). In
|
in the cargo docs](http://doc.crates.io/manifest.html#the-patch-section). In
|
||||||
addition to that, you should read the
|
addition to that, you should read the
|
||||||
[Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies)
|
[Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies)
|
||||||
section of the documentation as well.
|
section of the documentation as well.
|
||||||
|
|
||||||
Specifically, the following [section in Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#testing-a-bugfix) reveals what the problem is:
|
Specifically, the following [section in Overriding dependencies](http://doc.crates.io/specifying-dependencies.html#testing-a-bugfix) reveals what the problem is:
|
||||||
|
|
||||||
> Next up we need to ensure that our lock file is updated to use this new version of uuid so our project uses the locally checked out copy instead of one from crates.io. The way [patch] works is that it'll load the dependency at ../path/to/uuid and then whenever crates.io is queried for versions of uuid it'll also return the local version.
|
> Next up we need to ensure that our lock file is updated to use this new version of uuid so our project uses the locally checked out copy instead of one from crates.io. The way [patch] works is that it'll load the dependency at ../path/to/uuid and then whenever crates.io is queried for versions of uuid it'll also return the local version.
|
||||||
>
|
>
|
||||||
> This means that the version number of the local checkout is significant and will affect whether the patch is used. Our manifest declared uuid = "1.0" which means we'll only resolve to >= 1.0.0, < 2.0.0, and Cargo's greedy resolution algorithm also means that we'll resolve to the maximum version within that range. Typically this doesn't matter as the version of the git repository will already be greater or match the maximum version published on crates.io, but it's important to keep this in mind!
|
> This means that the version number of the local checkout is significant and will affect whether the patch is used. Our manifest declared uuid = "1.0" which means we'll only resolve to >= 1.0.0, < 2.0.0, and Cargo's greedy resolution algorithm also means that we'll resolve to the maximum version within that range. Typically this doesn't matter as the version of the git repository will already be greater or match the maximum version published on crates.io, but it's important to keep this in mind!
|
||||||
|
|
||||||
This says that when we updated the submodule, the version number in our
|
This says that when we updated the submodule, the version number in our
|
||||||
|
@ -129,6 +129,9 @@ CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.
|
|||||||
python x.py build
|
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),
|
||||||
pub compiler: Compiler,
|
$($name:ident,
|
||||||
pub target: Interned<String>,
|
$toolstate:ident,
|
||||||
|
$path:expr,
|
||||||
|
$tool_name:expr,
|
||||||
|
$extra_deps:block;)+) => {
|
||||||
|
$(
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct $name {
|
||||||
|
pub compiler: Compiler,
|
||||||
|
pub target: Interned<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Step for $name {
|
||||||
|
type Output = Option<PathBuf>;
|
||||||
|
const DEFAULT: bool = true;
|
||||||
|
const ONLY_HOSTS: bool = true;
|
||||||
|
|
||||||
|
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||||
|
let builder = run.builder;
|
||||||
|
run.path($path).default_condition(builder.build.config.extended)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_run(run: RunConfig) {
|
||||||
|
run.builder.ensure($name {
|
||||||
|
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
||||||
|
target: run.target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run($sel, $builder: &Builder) -> Option<PathBuf> {
|
||||||
|
$extra_deps
|
||||||
|
$builder.ensure(ToolBuild {
|
||||||
|
compiler: $sel.compiler,
|
||||||
|
target: $sel.target,
|
||||||
|
tool: $tool_name,
|
||||||
|
mode: Mode::Librustc,
|
||||||
|
path: $path,
|
||||||
|
is_ext_tool: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for Clippy {
|
tool_extended!((self, builder),
|
||||||
type Output = Option<PathBuf>;
|
Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {};
|
||||||
const DEFAULT: bool = true;
|
Clippy, clippy, "src/tools/clippy", "clippy-driver", {
|
||||||
const ONLY_HOSTS: bool = true;
|
|
||||||
|
|
||||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
|
||||||
let builder = run.builder;
|
|
||||||
run.path("src/tools/clippy").default_condition(builder.build.config.extended)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_run(run: RunConfig) {
|
|
||||||
run.builder.ensure(Clippy {
|
|
||||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
|
|
||||||
target: run.target,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(self, builder: &Builder) -> Option<PathBuf> {
|
|
||||||
// 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(),
|
expected success, got: {}\n\n",
|
||||||
expect,
|
cmd,
|
||||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
status);
|
||||||
expected success, got: {}\n\n",
|
|
||||||
cmd,
|
|
||||||
status))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_status<F: FnOnce()>(
|
|
||||||
cmd: &Command,
|
|
||||||
success: bool,
|
|
||||||
expect: BuildExpectation,
|
|
||||||
f: F,
|
|
||||||
) -> bool {
|
|
||||||
use BuildExpectation::*;
|
|
||||||
match (expect, success) {
|
|
||||||
(None, false) => { f(); false },
|
|
||||||
// Non-tool build succeeds, everything is good
|
|
||||||
(None, true) => true,
|
|
||||||
// Tool expected to work and is working
|
|
||||||
(Succeeding, true) => true,
|
|
||||||
// Tool expected to fail and is failing
|
|
||||||
(Failing, false) => {
|
|
||||||
println!("This failure is expected (see `src/tools/toolstate.toml`)");
|
|
||||||
true
|
|
||||||
},
|
|
||||||
// Tool expected to work, but is failing
|
|
||||||
(Succeeding, false) => {
|
|
||||||
f();
|
|
||||||
println!("You can disable the tool in `src/tools/toolstate.toml`");
|
|
||||||
false
|
|
||||||
},
|
|
||||||
// Tool expected to fail, but is working
|
|
||||||
(Failing, true) => {
|
|
||||||
println!("Expected `{:?}` to fail, but it succeeded.\n\
|
|
||||||
Please adjust `src/tools/toolstate.toml` accordingly", cmd);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
status.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) {
|
pub fn run_suppressed(cmd: &mut Command) {
|
||||||
if !try_run_suppressed(cmd, expect) {
|
if !try_run_suppressed(cmd) {
|
||||||
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
|
||||||
@ -349,36 +356,36 @@ test tests::larger_can_hold_smaller ... FAILED
|
|||||||
failures:
|
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
|
||||||
@ -537,19 +546,19 @@ test tests::greeting_contains_name ... FAILED
|
|||||||
failures:
|
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
|
||||||
@ -100,39 +98,41 @@ test tests::this_test_will_fail ... FAILED
|
|||||||
failures:
|
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