mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-11 16:25:47 +00:00
New upstream version 1.22.1+dfsg1
This commit is contained in:
parent
3b2f29766f
commit
ea8adc8c7c
110
CONTRIBUTING.md
110
CONTRIBUTING.md
@ -1,4 +1,5 @@
|
||||
# Contributing to Rust
|
||||
[contributing-to-rust]: #contributing-to-rust
|
||||
|
||||
Thank you for your interest in contributing to Rust! There are many ways to
|
||||
contribute, and we appreciate all of them. This document is a bit long, so here's
|
||||
@ -18,11 +19,12 @@ hop on [#rust-internals][pound-rust-internals].
|
||||
|
||||
As a reminder, all contributors are expected to follow our [Code of Conduct][coc].
|
||||
|
||||
[pound-rust-internals]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals
|
||||
[pound-rust-internals]: https://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals
|
||||
[internals]: https://internals.rust-lang.org
|
||||
[coc]: https://www.rust-lang.org/conduct.html
|
||||
|
||||
## Feature Requests
|
||||
[feature-requests]: #feature-requests
|
||||
|
||||
To request a change to the way that the Rust language works, please open an
|
||||
issue in the [RFCs repository](https://github.com/rust-lang/rfcs/issues/new)
|
||||
@ -30,6 +32,7 @@ rather than this one. New features and other significant language changes
|
||||
must go through the RFC process.
|
||||
|
||||
## Bug Reports
|
||||
[bug-reports]: #bug-reports
|
||||
|
||||
While bugs are unfortunate, they're a reality in software. We can't fix what we
|
||||
don't know about, so please report liberally. If you're not sure if something
|
||||
@ -80,6 +83,7 @@ $ RUST_BACKTRACE=1 rustc ...
|
||||
```
|
||||
|
||||
## The Build System
|
||||
[the-build-system]: #the-build-system
|
||||
|
||||
Rust's build system allows you to bootstrap the compiler, run tests &
|
||||
benchmarks, generate documentation, install a fresh build of Rust, and more.
|
||||
@ -94,6 +98,7 @@ system internals, try asking in [`#rust-internals`][pound-rust-internals].
|
||||
[bootstrap]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/
|
||||
|
||||
### Configuration
|
||||
[configuration]: #configuration
|
||||
|
||||
Before you can start building the compiler you need to configure the build for
|
||||
your system. In most cases, that will just mean using the defaults provided
|
||||
@ -125,6 +130,11 @@ file. If you still have a `config.mk` file in your directory - from
|
||||
`./configure` - you may need to delete it for `config.toml` to work.
|
||||
|
||||
### Building
|
||||
[building]: #building
|
||||
|
||||
Dependencies
|
||||
- [build dependencies](README.md#building-from-source)
|
||||
- `gdb` 6.2.0 minimum, 7.1 or later recommended for test builds
|
||||
|
||||
The build system uses the `x.py` script to control the build process. This script
|
||||
is used to build, test, and document various parts of the compiler. You can
|
||||
@ -194,6 +204,7 @@ Note: Previously `./configure` and `make` were used to build this project.
|
||||
They are still available, but `x.py` is the recommended build system.
|
||||
|
||||
### Useful commands
|
||||
[useful-commands]: #useful-commands
|
||||
|
||||
Some common invocations of `x.py` are:
|
||||
|
||||
@ -232,9 +243,38 @@ Some common invocations of `x.py` are:
|
||||
guidelines as of yet, but basic rules like 4 spaces for indentation and no
|
||||
more than 99 characters in a single line should be kept in mind when writing
|
||||
code.
|
||||
- `rustup toolchain link <name> build/<host-triple>/<stage>` - Use the custom compiler build via [rustup](https://github.com/rust-lang-nursery/rustup.rs#working-with-custom-toolchains-and-local-builds).
|
||||
|
||||
### Using your local build
|
||||
[using-local-build]: #using-local-build
|
||||
|
||||
If you use Rustup to manage your rust install, it has a feature called ["custom
|
||||
toolchains"][toolchain-link] that you can use to access your newly-built compiler
|
||||
without having to install it to your system or user PATH. If you've run `python
|
||||
x.py build`, then you can add your custom rustc to a new toolchain like this:
|
||||
|
||||
[toolchain-link]: https://github.com/rust-lang-nursery/rustup.rs#working-with-custom-toolchains-and-local-builds
|
||||
|
||||
```
|
||||
rustup toolchain link <name> build/<host-triple>/stage2
|
||||
```
|
||||
|
||||
Where `<host-triple>` is the build triple for the host (the triple of your
|
||||
computer, by default), and `<name>` is the name for your custom toolchain. (If you
|
||||
added `--stage 1` to your build command, the compiler will be in the `stage1`
|
||||
folder instead.) You'll only need to do this once - it will automatically point
|
||||
to the latest build you've done.
|
||||
|
||||
Once this is set up, you can use your custom toolchain just like any other. For
|
||||
example, if you've named your toolchain `local`, running `cargo +local build` will
|
||||
compile a project with your custom rustc, setting `rustup override set local` will
|
||||
override the toolchain for your current directory, and `cargo +local doc` will use
|
||||
your custom rustc and rustdoc to generate docs. (If you do this with a `--stage 1`
|
||||
build, you'll need to build rustdoc specially, since it's not normally built in
|
||||
stage 1. `python x.py build --stage 1 src/libstd src/tools/rustdoc` will build
|
||||
rustdoc and libstd, which will allow rustdoc to be run with that toolchain.)
|
||||
|
||||
## Pull Requests
|
||||
[pull-requests]: #pull-requests
|
||||
|
||||
Pull requests are the primary mechanism we use to change Rust. GitHub itself
|
||||
has some [great documentation][pull-requests] on using the Pull Request feature.
|
||||
@ -298,7 +338,33 @@ Speaking of tests, Rust has a comprehensive test suite. More information about
|
||||
it can be found
|
||||
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md).
|
||||
|
||||
### External Dependencies
|
||||
[external-dependencies]: #external-dependencies
|
||||
|
||||
Currently building Rust will also build the following external projects:
|
||||
|
||||
* [clippy](https://github.com/rust-lang-nursery/rust-clippy)
|
||||
* [miri](https://github.com/solson/miri)
|
||||
|
||||
If your changes break one of these projects, you need to fix them by opening
|
||||
a pull request against the broken project asking to put the fix on a branch.
|
||||
Then you can disable the tool building via `src/tools/toolstate.toml`.
|
||||
Once the branch containing your fix is likely to be merged, you can point
|
||||
the affected submodule at this branch.
|
||||
|
||||
Don't forget to also add your changes with
|
||||
|
||||
```
|
||||
git add path/to/submodule
|
||||
```
|
||||
|
||||
outside the submodule.
|
||||
|
||||
It can also be more convenient during development to set `submodules = false`
|
||||
in the `config.toml` to prevent `x.py` from resetting to the original branch.
|
||||
|
||||
## Writing Documentation
|
||||
[writing-documentation]: #writing-documentation
|
||||
|
||||
Documentation improvements are very welcome. The source of `doc.rust-lang.org`
|
||||
is located in `src/doc` in the tree, and standard API documentation is generated
|
||||
@ -329,6 +395,7 @@ reference to `doc/reference.html`. The CSS might be messed up, but you can
|
||||
verify that the HTML is right.
|
||||
|
||||
## Issue Triage
|
||||
[issue-triage]: #issue-triage
|
||||
|
||||
Sometimes, an issue will stay open, even though the bug has been fixed. And
|
||||
sometimes, the original bug may go stale because something has changed in the
|
||||
@ -347,32 +414,56 @@ labels to triage issues:
|
||||
|
||||
* Magenta, **B**-prefixed labels identify bugs which are **blockers**.
|
||||
|
||||
* Dark blue, **beta-** labels track changes which need to be backported into
|
||||
the beta branches.
|
||||
|
||||
* Light purple, **C**-prefixed labels represent the **category** of an issue.
|
||||
|
||||
* Green, **E**-prefixed labels explain the level of **experience** necessary
|
||||
to fix the issue.
|
||||
|
||||
* The dark blue **final-comment-period** label marks bugs that are using the
|
||||
RFC signoff functionality of [rfcbot][rfcbot] and are currenty in the final
|
||||
comment period.
|
||||
|
||||
* Red, **I**-prefixed labels indicate the **importance** of the issue. The
|
||||
[I-nominated][inom] label indicates that an issue has been nominated for
|
||||
prioritizing at the next triage meeting.
|
||||
|
||||
* The purple **metabug** label marks lists of bugs collected by other
|
||||
categories.
|
||||
|
||||
* Purple gray, **O**-prefixed labels are the **operating system** or platform
|
||||
that this issue is specific to.
|
||||
|
||||
* Orange, **P**-prefixed labels indicate a bug's **priority**. These labels
|
||||
are only assigned during triage meetings, and replace the [I-nominated][inom]
|
||||
label.
|
||||
|
||||
* The gray **proposed-final-comment-period** label marks bugs that are using
|
||||
the RFC signoff functionality of [rfcbot][rfcbot] and are currently awaiting
|
||||
signoff of all team members in order to enter the final comment period.
|
||||
|
||||
* Pink, **regression**-prefixed labels track regressions from stable to the
|
||||
release channels.
|
||||
|
||||
* The light orange **relnotes** label marks issues that should be documented in
|
||||
the release notes of the next release.
|
||||
|
||||
* Gray, **S**-prefixed labels are used for tracking the **status** of pull
|
||||
requests.
|
||||
|
||||
* Blue, **T**-prefixed bugs denote which **team** the issue belongs to.
|
||||
|
||||
* Dark blue, **beta-** labels track changes which need to be backported into
|
||||
the beta branches.
|
||||
|
||||
* The purple **metabug** label marks lists of bugs collected by other
|
||||
categories.
|
||||
|
||||
If you're looking for somewhere to start, check out the [E-easy][eeasy] tag.
|
||||
|
||||
[inom]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AI-nominated
|
||||
[eeasy]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy
|
||||
[lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc
|
||||
[rfcbot]: https://github.com/dikaiosune/rust-dashboard/blob/master/RFCBOT.md
|
||||
|
||||
## Out-of-tree Contributions
|
||||
[out-of-tree-contributions]: #out-of-tree-contributions
|
||||
|
||||
There are a number of other ways to contribute to Rust that don't deal with
|
||||
this repository.
|
||||
@ -392,11 +483,13 @@ valuable!
|
||||
[community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library
|
||||
|
||||
## Helpful Links and Information
|
||||
[helpful-info]: #helpful-info
|
||||
|
||||
For people new to Rust, and just starting to contribute, or even for
|
||||
more seasoned developers, some useful places to look for information
|
||||
are:
|
||||
|
||||
* [Rust Forge][rustforge] contains additional documentation, including write-ups of how to achieve common tasks
|
||||
* The [Rust Internals forum][rif], a place to ask questions and
|
||||
discuss Rust's internals
|
||||
* The [generated documentation for rust's compiler][gdfrustc]
|
||||
@ -412,6 +505,7 @@ are:
|
||||
[gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here
|
||||
[rif]: http://internals.rust-lang.org
|
||||
[rr]: https://doc.rust-lang.org/book/README.html
|
||||
[rustforge]: https://forge.rust-lang.org/
|
||||
[tlgba]: http://tomlee.co/2014/04/a-more-detailed-tour-of-the-rust-compiler/
|
||||
[ro]: http://www.rustaceans.org/
|
||||
[rctd]: ./src/test/COMPILER_TESTS.md
|
||||
|
12
README.md
12
README.md
@ -6,6 +6,7 @@ standard library, and documentation.
|
||||
[Rust]: https://www.rust-lang.org
|
||||
|
||||
## Quick Start
|
||||
[quick-start]: #quick-start
|
||||
|
||||
Read ["Installation"] from [The Book].
|
||||
|
||||
@ -13,6 +14,7 @@ Read ["Installation"] from [The Book].
|
||||
[The Book]: https://doc.rust-lang.org/book/index.html
|
||||
|
||||
## Building from Source
|
||||
[building-from-source]: #building-from-source
|
||||
|
||||
1. Make sure you have installed the dependencies:
|
||||
|
||||
@ -52,6 +54,7 @@ Read ["Installation"] from [The Book].
|
||||
[Cargo]: https://github.com/rust-lang/cargo
|
||||
|
||||
### Building on Windows
|
||||
[building-on-windows]: #building-on-windows
|
||||
|
||||
There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by
|
||||
Visual Studio, and the GNU ABI used by the GCC toolchain. Which version of Rust
|
||||
@ -61,6 +64,7 @@ for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU
|
||||
build.
|
||||
|
||||
#### MinGW
|
||||
[windows-mingw]: #windows-mingw
|
||||
|
||||
[MSYS2][msys2] can be used to easily build Rust on Windows:
|
||||
|
||||
@ -101,6 +105,7 @@ build.
|
||||
```
|
||||
|
||||
#### MSVC
|
||||
[windows-msvc]: #windows-msvc
|
||||
|
||||
MSVC builds of Rust additionally require an installation of Visual Studio 2013
|
||||
(or later) so `rustc` can use its linker. Make sure to check the “C++ tools”
|
||||
@ -124,6 +129,7 @@ python x.py build
|
||||
```
|
||||
|
||||
#### Specifying an ABI
|
||||
[specifying-an-abi]: #specifying-an-abi
|
||||
|
||||
Each specific ABI can also be used from either environment (for example, using
|
||||
the GNU ABI in powershell) by using an explicit build triple. The available
|
||||
@ -141,6 +147,7 @@ in Building From Source), and modifying the `build` option under the `[build]`
|
||||
section.
|
||||
|
||||
### Configure and Make
|
||||
[configure-and-make]: #configure-and-make
|
||||
|
||||
While it's not the recommended build system, this project also provides a
|
||||
configure script and makefile (the latter of which just invokes `x.py`).
|
||||
@ -155,6 +162,7 @@ When using the configure script, the generated `config.mk` file may override the
|
||||
`config.mk` file.
|
||||
|
||||
## Building Documentation
|
||||
[building-documentation]: #building-documentation
|
||||
|
||||
If you’d like to build the documentation, it’s almost the same:
|
||||
|
||||
@ -167,6 +175,7 @@ the ABI used. I.e., if the ABI was `x86_64-pc-windows-msvc`, the directory will
|
||||
`build\x86_64-pc-windows-msvc\doc`.
|
||||
|
||||
## Notes
|
||||
[notes]: #notes
|
||||
|
||||
Since the Rust compiler is written in Rust, it must be built by a
|
||||
precompiled "snapshot" version of itself (made in an earlier state of
|
||||
@ -192,6 +201,7 @@ There is more advice about hacking on Rust in [CONTRIBUTING.md].
|
||||
[CONTRIBUTING.md]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Getting Help
|
||||
[getting-help]: #getting-help
|
||||
|
||||
The Rust community congregates in a few places:
|
||||
|
||||
@ -204,6 +214,7 @@ The Rust community congregates in a few places:
|
||||
[users.rust-lang.org]: https://users.rust-lang.org/
|
||||
|
||||
## Contributing
|
||||
[contributing]: #contributing
|
||||
|
||||
To contribute to Rust, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
@ -217,6 +228,7 @@ Rust. And a good place to ask for help would be [#rust-beginners].
|
||||
[#rust-beginners]: irc://irc.mozilla.org/rust-beginners
|
||||
|
||||
## License
|
||||
[license]: #license
|
||||
|
||||
Rust is primarily distributed under the terms of both the MIT license
|
||||
and the Apache License (Version 2.0), with portions covered by various
|
||||
|
106
RELEASES.md
106
RELEASES.md
@ -1,9 +1,107 @@
|
||||
Version 1.21.0 (2017-10-12)
|
||||
==========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Relaxed path syntax. You can now add type parameters to values][43540]
|
||||
Example:
|
||||
```rust
|
||||
my_macro!(Vec<i32>::new); // Always worked
|
||||
my_macro!(Vec::<i32>::new); // Now works
|
||||
```
|
||||
- [You can now use static references for literals.][43838]
|
||||
Example:
|
||||
```rust
|
||||
fn main() {
|
||||
let x: &'static u32 = &0;
|
||||
}
|
||||
```
|
||||
|
||||
Compiler
|
||||
--------
|
||||
- [Upgraded jemalloc to 4.5.0][43911]
|
||||
- [Enabled unwinding panics on Redox][43917]
|
||||
- [Now runs LLVM in parallel during translation phase.][43506]
|
||||
This should reduce peak memory usage.
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- [Generate builtin impls for `Clone` for all arrays and tuples that
|
||||
are `T: Clone`][43690]
|
||||
- [`Stdin`, `Stdout`, and `Stderr` now implement `AsRawFd`.][43459]
|
||||
- [`Rc` and `Arc` now implement `From<&[T]> where T: Clone`, `From<str>`,
|
||||
`From<String>`, `From<Box<T>> where T: ?Sized`, and `From<Vec<T>>`.][42565]
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
[`std::mem::discriminant`]
|
||||
|
||||
Cargo
|
||||
-----
|
||||
- [You can now call `cargo install` with multiple package names][cargo/4216]
|
||||
- [Cargo commands inside a virtual workspace will now implicitly
|
||||
pass `--all`][cargo/4335]
|
||||
- [Added a `[patch]` section to `Cargo.toml` to handle
|
||||
prepublication dependencies][cargo/4123] [RFC 1969]
|
||||
- [`include` & `exclude` fields in `Cargo.toml` now accept gitignore
|
||||
like patterns][cargo/4270]
|
||||
- [Added the `--all-targets` option][cargo/4400]
|
||||
- [Using required dependencies as a feature is now deprecated and emits
|
||||
a warning][cargo/4364]
|
||||
|
||||
|
||||
Misc
|
||||
----
|
||||
- [Cargo docs are moving][43916]
|
||||
to [doc.rust-lang.org/cargo](https://doc.rust-lang.org/cargo)
|
||||
- [The rustdoc book is now available][43863]
|
||||
at [doc.rust-lang.org/rustdoc](https://doc.rust-lang.org/rustdoc)
|
||||
- [Added a preview of RLS has been made available through rustup][44204]
|
||||
Install with `rustup component add rls-preview`
|
||||
- [`std::os` documentation for Unix, Linux, and Windows now appears on doc.rust-lang.org][43348]
|
||||
Previously only showed `std::os::unix`.
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
- [Changes in method matching against higher-ranked types][43880] This may cause
|
||||
breakage in subtyping corner cases. [A more in-depth explanation is available.][info/43880]
|
||||
- [rustc's JSON error output's byte position start at top of file.][42973]
|
||||
Was previously relative to the rustc's internal `CodeMap` struct which
|
||||
required the unstable library `libsyntax` to correctly use.
|
||||
- [`unused_results` lint no longer ignores booleans][43728]
|
||||
|
||||
[42565]: https://github.com/rust-lang/rust/pull/42565
|
||||
[42973]: https://github.com/rust-lang/rust/pull/42973
|
||||
[43348]: https://github.com/rust-lang/rust/pull/43348
|
||||
[43459]: https://github.com/rust-lang/rust/pull/43459
|
||||
[43506]: https://github.com/rust-lang/rust/pull/43506
|
||||
[43540]: https://github.com/rust-lang/rust/pull/43540
|
||||
[43690]: https://github.com/rust-lang/rust/pull/43690
|
||||
[43728]: https://github.com/rust-lang/rust/pull/43728
|
||||
[43838]: https://github.com/rust-lang/rust/pull/43838
|
||||
[43863]: https://github.com/rust-lang/rust/pull/43863
|
||||
[43880]: https://github.com/rust-lang/rust/pull/43880
|
||||
[43911]: https://github.com/rust-lang/rust/pull/43911
|
||||
[43916]: https://github.com/rust-lang/rust/pull/43916
|
||||
[43917]: https://github.com/rust-lang/rust/pull/43917
|
||||
[44204]: https://github.com/rust-lang/rust/pull/44204
|
||||
[cargo/4123]: https://github.com/rust-lang/cargo/pull/4123
|
||||
[cargo/4216]: https://github.com/rust-lang/cargo/pull/4216
|
||||
[cargo/4270]: https://github.com/rust-lang/cargo/pull/4270
|
||||
[cargo/4335]: https://github.com/rust-lang/cargo/pull/4335
|
||||
[cargo/4364]: https://github.com/rust-lang/cargo/pull/4364
|
||||
[cargo/4400]: https://github.com/rust-lang/cargo/pull/4400
|
||||
[RFC 1969]: https://github.com/rust-lang/rfcs/pull/1969
|
||||
[info/43880]: https://github.com/rust-lang/rust/issues/44224#issuecomment-330058902
|
||||
[`std::mem::discriminant`]: https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
||||
|
||||
Version 1.20.0 (2017-08-31)
|
||||
===========================
|
||||
|
||||
Language
|
||||
--------
|
||||
- [Associated constants in traits is now stabilised.][42809]
|
||||
- [Associated constants are now stabilised.][42809]
|
||||
- [A lot of macro bugs are now fixed.][42913]
|
||||
|
||||
Compiler
|
||||
@ -77,7 +175,7 @@ Stabilized APIs
|
||||
- [`slice::sort_unstable_by_key`]
|
||||
- [`slice::sort_unstable_by`]
|
||||
- [`slice::sort_unstable`]
|
||||
- [`ste::from_boxed_utf8_unchecked`]
|
||||
- [`str::from_boxed_utf8_unchecked`]
|
||||
- [`str::as_bytes_mut`]
|
||||
- [`str::as_bytes_mut`]
|
||||
- [`str::from_utf8_mut`]
|
||||
@ -110,7 +208,7 @@ Compatibility Notes
|
||||
- [Functions with `'static` in their return types will now not be as usable as
|
||||
if they were using lifetime parameters instead.][42417]
|
||||
- [The reimplementation of `{f32, f64}::is_sign_{negative, positive}` now
|
||||
takes the sign of NaN into account where previously didn't.][42430]
|
||||
takes the sign of NaN into account where previously didn't.][42430]
|
||||
|
||||
[42033]: https://github.com/rust-lang/rust/pull/42033
|
||||
[42155]: https://github.com/rust-lang/rust/pull/42155
|
||||
@ -170,7 +268,7 @@ Compatibility Notes
|
||||
[`slice::sort_unstable_by_key`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by_key
|
||||
[`slice::sort_unstable_by`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by
|
||||
[`slice::sort_unstable`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable
|
||||
[`ste::from_boxed_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_boxed_utf8_unchecked.html
|
||||
[`str::from_boxed_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_boxed_utf8_unchecked.html
|
||||
[`str::as_bytes_mut`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes_mut
|
||||
[`str::from_utf8_mut`]: https://doc.rust-lang.org/std/str/fn.from_utf8_mut.html
|
||||
[`str::from_utf8_unchecked_mut`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked_mut.html
|
||||
|
379
config.toml.example
Normal file
379
config.toml.example
Normal file
@ -0,0 +1,379 @@
|
||||
# Sample TOML configuration file for building Rust.
|
||||
#
|
||||
# To configure rustbuild, copy this file to the directory from which you will be
|
||||
# running the build, and name it config.toml.
|
||||
#
|
||||
# All options are commented out by default in this file, and they're commented
|
||||
# out with their default values. The build system by default looks for
|
||||
# `config.toml` in the current directory of a build for build configuration, but
|
||||
# a custom configuration file can also be specified with `--config` to the build
|
||||
# system.
|
||||
|
||||
# =============================================================================
|
||||
# Tweaking how LLVM is compiled
|
||||
# =============================================================================
|
||||
[llvm]
|
||||
|
||||
# Indicates whether rustc will support compilation with LLVM
|
||||
# note: rustc does not compile without LLVM at the moment
|
||||
#enabled = true
|
||||
|
||||
# Indicates whether the LLVM build is a Release or Debug build
|
||||
#optimize = true
|
||||
|
||||
# Indicates whether an LLVM Release build should include debug info
|
||||
#release-debuginfo = false
|
||||
|
||||
# Indicates whether the LLVM assertions are enabled or not
|
||||
#assertions = false
|
||||
|
||||
# Indicates whether ccache is used when building LLVM
|
||||
#ccache = false
|
||||
# or alternatively ...
|
||||
#ccache = "/path/to/ccache"
|
||||
|
||||
# If an external LLVM root is specified, we automatically check the version by
|
||||
# default to make sure it's within the range that we're expecting, but setting
|
||||
# this flag will indicate that this version check should not be done.
|
||||
#version-check = false
|
||||
|
||||
# Link libstdc++ statically into the librustc_llvm instead of relying on a
|
||||
# dynamic version to be available.
|
||||
#static-libstdcpp = false
|
||||
|
||||
# Tell the LLVM build system to use Ninja instead of the platform default for
|
||||
# the generated build system. This can sometimes be faster than make, for
|
||||
# example.
|
||||
#ninja = false
|
||||
|
||||
# LLVM targets to build support for.
|
||||
# Note: this is NOT related to Rust compilation targets. However, as Rust is
|
||||
# dependent on LLVM for code generation, turning targets off here WILL lead to
|
||||
# the resulting rustc being unable to compile for the disabled architectures.
|
||||
# Also worth pointing out is that, in case support for new targets are added to
|
||||
# LLVM, enabling them here doesn't mean Rust is automatically gaining said
|
||||
# support. You'll need to write a target specification at least, and most
|
||||
# likely, teach rustc about the C ABI of the target. Get in touch with the
|
||||
# Rust team and file an issue if you need assistance in porting!
|
||||
#targets = "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX;Hexagon"
|
||||
|
||||
# LLVM experimental targets to build support for. These targets are specified in
|
||||
# the same format as above, but since these targets are experimental, they are
|
||||
# not built by default and the experimental Rust compilation targets that depend
|
||||
# on them will not work unless the user opts in to building them. Possible
|
||||
# experimental LLVM targets include WebAssembly for the
|
||||
# wasm32-experimental-emscripten Rust target.
|
||||
#experimental-targets = ""
|
||||
|
||||
# Cap the number of parallel linker invocations when compiling LLVM.
|
||||
# This can be useful when building LLVM with debug info, which significantly
|
||||
# increases the size of binaries and consequently the memory required by
|
||||
# each linker process.
|
||||
# If absent or 0, linker invocations are treated like any other job and
|
||||
# controlled by rustbuild's -j parameter.
|
||||
#link-jobs = 0
|
||||
|
||||
# When invoking `llvm-config` this configures whether the `--shared` argument is
|
||||
# passed to prefer linking to shared libraries.
|
||||
#link-shared = false
|
||||
|
||||
# =============================================================================
|
||||
# General build configuration options
|
||||
# =============================================================================
|
||||
[build]
|
||||
|
||||
# Build triple for the original snapshot compiler. This must be a compiler that
|
||||
# nightlies are already produced for. The current platform must be able to run
|
||||
# binaries of this build triple and the nightly will be used to bootstrap the
|
||||
# first compiler.
|
||||
#build = "x86_64-unknown-linux-gnu" # defaults to your host platform
|
||||
|
||||
# In addition to the build triple, other triples to produce full compiler
|
||||
# toolchains for. Each of these triples will be bootstrapped from the build
|
||||
# triple and then will continue to bootstrap themselves. This platform must
|
||||
# currently be able to run all of the triples provided here.
|
||||
#host = ["x86_64-unknown-linux-gnu"] # defaults to just the build triple
|
||||
|
||||
# In addition to all host triples, other triples to produce the standard library
|
||||
# for. Each host triple will be used to produce a copy of the standard library
|
||||
# for each target triple.
|
||||
#target = ["x86_64-unknown-linux-gnu"] # defaults to just the build triple
|
||||
|
||||
# Instead of downloading the src/stage0.txt version of Cargo specified, use
|
||||
# this Cargo binary instead to build all Rust code
|
||||
#cargo = "/path/to/bin/cargo"
|
||||
|
||||
# Instead of downloading the src/stage0.txt version of the compiler
|
||||
# specified, use this rustc binary instead as the stage0 snapshot compiler.
|
||||
#rustc = "/path/to/bin/rustc"
|
||||
|
||||
# Flag to specify whether any documentation is built. If false, rustdoc and
|
||||
# friends will still be compiled but they will not be used to generate any
|
||||
# documentation.
|
||||
#docs = true
|
||||
|
||||
# Indicate whether the compiler should be documented in addition to the standard
|
||||
# library and facade crates.
|
||||
#compiler-docs = false
|
||||
|
||||
# Indicate whether submodules are managed and updated automatically.
|
||||
#submodules = true
|
||||
|
||||
# The path to (or name of) the GDB executable to use. This is only used for
|
||||
# executing the debuginfo test suite.
|
||||
#gdb = "gdb"
|
||||
|
||||
# The node.js executable to use. Note that this is only used for the emscripten
|
||||
# target when running tests, otherwise this can be omitted.
|
||||
#nodejs = "node"
|
||||
|
||||
# Python interpreter to use for various tasks throughout the build, notably
|
||||
# rustdoc tests, the lldb python interpreter, and some dist bits and pieces.
|
||||
# Note that Python 2 is currently required.
|
||||
#python = "python2.7"
|
||||
|
||||
# Force Cargo to check that Cargo.lock describes the precise dependency
|
||||
# set that all the Cargo.toml files create, instead of updating it.
|
||||
#locked-deps = false
|
||||
|
||||
# Indicate whether the vendored sources are used for Rust dependencies or not
|
||||
#vendor = false
|
||||
|
||||
# Typically the build system will build the rust compiler twice. The second
|
||||
# compiler, however, will simply use its own libraries to link against. If you
|
||||
# would rather to perform a full bootstrap, compiling the compiler three times,
|
||||
# then you can set this option to true. You shouldn't ever need to set this
|
||||
# option to true.
|
||||
#full-bootstrap = false
|
||||
|
||||
# Enable a build of the extended rust tool set which is not only the compiler
|
||||
# but also tools such as Cargo. This will also produce "combined installers"
|
||||
# which are used to install Rust and Cargo together. This is disabled by
|
||||
# default.
|
||||
#extended = false
|
||||
|
||||
# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
|
||||
#verbose = 0
|
||||
|
||||
# Build the sanitizer runtimes
|
||||
#sanitizers = false
|
||||
|
||||
# Build the profiler runtime
|
||||
#profiler = false
|
||||
|
||||
# Indicates whether the OpenSSL linked into Cargo will be statically linked or
|
||||
# not. If static linkage is specified then the build system will download a
|
||||
# known-good version of OpenSSL, compile it, and link it to Cargo.
|
||||
#openssl-static = false
|
||||
|
||||
# Run the build with low priority, by setting the process group's "nice" value
|
||||
# to +10 on Unix platforms, and by using a "low priority" job object on Windows.
|
||||
#low-priority = false
|
||||
|
||||
# Arguments passed to the `./configure` script, used during distcheck. You
|
||||
# probably won't fill this in but rather it's filled in by the `./configure`
|
||||
# script.
|
||||
#configure-args = []
|
||||
|
||||
# Indicates that a local rebuild is occurring instead of a full bootstrap,
|
||||
# essentially skipping stage0 as the local compiler is recompiling itself again.
|
||||
#local-rebuild = false
|
||||
|
||||
# =============================================================================
|
||||
# General install configuration options
|
||||
# =============================================================================
|
||||
[install]
|
||||
|
||||
# Instead of installing to /usr/local, install to this path instead.
|
||||
#prefix = "/usr/local"
|
||||
|
||||
# Where to install system configuration files
|
||||
# If this is a relative path, it will get installed in `prefix` above
|
||||
#sysconfdir = "/etc"
|
||||
|
||||
# Where to install documentation in `prefix` above
|
||||
#docdir = "share/doc/rust"
|
||||
|
||||
# Where to install binaries in `prefix` above
|
||||
#bindir = "bin"
|
||||
|
||||
# Where to install libraries in `prefix` above
|
||||
#libdir = "lib"
|
||||
|
||||
# Where to install man pages in `prefix` above
|
||||
#mandir = "share/man"
|
||||
|
||||
# =============================================================================
|
||||
# Options for compiling Rust code itself
|
||||
# =============================================================================
|
||||
[rust]
|
||||
|
||||
# Indicates that the build should be optimized for debugging Rust. Note that
|
||||
# this is typically not what you want as it takes an incredibly large amount of
|
||||
# time to have a debug-mode rustc compile any code (notably libstd). If this
|
||||
# value is set to `true` it will affect a number of configuration options below
|
||||
# as well, if unconfigured.
|
||||
#debug = false
|
||||
|
||||
# Whether or not to optimize the compiler and standard library
|
||||
# Note: the slowness of the non optimized compiler compiling itself usually
|
||||
# outweighs the time gains in not doing optimizations, therefore a
|
||||
# full bootstrap takes much more time with optimize set to false.
|
||||
#optimize = true
|
||||
|
||||
# Number of codegen units to use for each compiler invocation. A value of 0
|
||||
# means "the number of cores on this machine", and 1+ is passed through to the
|
||||
# compiler.
|
||||
#codegen-units = 1
|
||||
|
||||
# Whether or not debug assertions are enabled for the compiler and standard
|
||||
# library. Also enables compilation of debug! and trace! logging macros.
|
||||
#debug-assertions = false
|
||||
|
||||
# Whether or not debuginfo is emitted
|
||||
#debuginfo = false
|
||||
|
||||
# Whether or not line number debug information is emitted
|
||||
#debuginfo-lines = false
|
||||
|
||||
# Whether or not to only build debuginfo for the standard library if enabled.
|
||||
# If enabled, this will not compile the compiler with debuginfo, just the
|
||||
# standard library.
|
||||
#debuginfo-only-std = false
|
||||
|
||||
# Whether or not jemalloc is built and enabled
|
||||
#use-jemalloc = true
|
||||
|
||||
# Whether or not jemalloc is built with its debug option set
|
||||
#debug-jemalloc = false
|
||||
|
||||
# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
|
||||
#backtrace = true
|
||||
|
||||
# The default linker that will be used by the generated compiler. Note that this
|
||||
# is not the linker used to link said compiler.
|
||||
#default-linker = "cc"
|
||||
|
||||
# The default ar utility that will be used by the generated compiler if LLVM
|
||||
# cannot be used. Note that this is not used to assemble said compiler.
|
||||
#default-ar = "ar"
|
||||
|
||||
# The "channel" for the Rust build to produce. The stable/beta channels only
|
||||
# allow using stable features, whereas the nightly and dev channels allow using
|
||||
# nightly features
|
||||
#channel = "dev"
|
||||
|
||||
# By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix
|
||||
# platforms to ensure that the compiler is usable by default from the build
|
||||
# directory (as it links to a number of dynamic libraries). This may not be
|
||||
# desired in distributions, for example.
|
||||
#rpath = true
|
||||
|
||||
# Suppresses extraneous output from tests to ensure the output of the test
|
||||
# harness is relatively clean.
|
||||
#quiet-tests = false
|
||||
|
||||
# Flag indicating whether tests are compiled with optimizations (the -O flag) or
|
||||
# with debuginfo (the -g flag)
|
||||
#optimize-tests = true
|
||||
#debuginfo-tests = true
|
||||
|
||||
# Flag indicating whether codegen tests will be run or not. If you get an error
|
||||
# saying that the FileCheck executable is missing, you may want to disable this.
|
||||
#codegen-tests = true
|
||||
|
||||
# Flag indicating whether git info will be retrieved from .git automatically.
|
||||
# Having the git information can cause a lot of rebuilds during development.
|
||||
# Note: If this attribute is not explicity set (e.g. if left commented out) it
|
||||
# will default to true if channel = "dev", but will default to false otherwise.
|
||||
#ignore-git = true
|
||||
|
||||
# When creating source tarballs whether or not to create a source tarball.
|
||||
#dist-src = false
|
||||
|
||||
# Whether to also run the Miri tests suite when running tests.
|
||||
# As a side-effect also generates MIR for all libraries.
|
||||
#test-miri = false
|
||||
|
||||
# =============================================================================
|
||||
# Options for specific targets
|
||||
#
|
||||
# Each of the following options is scoped to the specific target triple in
|
||||
# question and is used for determining how to compile each target.
|
||||
# =============================================================================
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
|
||||
# C compiler to be used to compiler C code and link Rust code. Note that the
|
||||
# default value is platform specific, and if not specified it may also depend on
|
||||
# what platform is crossing to what platform.
|
||||
#cc = "cc"
|
||||
|
||||
# C++ compiler to be used to compiler C++ code (e.g. LLVM and our LLVM shims).
|
||||
# This is only used for host targets.
|
||||
#cxx = "c++"
|
||||
|
||||
# Path to the `llvm-config` binary of the installation of a custom LLVM to link
|
||||
# against. Note that if this is specifed we don't compile LLVM at all for this
|
||||
# target.
|
||||
#llvm-config = "../path/to/llvm/root/bin/llvm-config"
|
||||
|
||||
# Path to the custom jemalloc static library to link into the standard library
|
||||
# by default. This is only used if jemalloc is still enabled above
|
||||
#jemalloc = "/path/to/jemalloc/libjemalloc_pic.a"
|
||||
|
||||
# If this target is for Android, this option will be required to specify where
|
||||
# the NDK for the target lives. This is used to find the C compiler to link and
|
||||
# build native code.
|
||||
#android-ndk = "/path/to/ndk"
|
||||
|
||||
# Force static or dynamic linkage of the standard library for this target. If
|
||||
# this target is a host for rustc, this will also affect the linkage of the
|
||||
# compiler itself. This is useful for building rustc on targets that normally
|
||||
# only use static libraries. If unset, the target's default linkage is used.
|
||||
#crt-static = false
|
||||
|
||||
# The root location of the MUSL installation directory. The library directory
|
||||
# will also need to contain libunwind.a for an unwinding implementation. Note
|
||||
# that this option only makes sense for MUSL targets that produce statically
|
||||
# linked binaries
|
||||
#musl-root = "..."
|
||||
|
||||
# Used in testing for configuring where the QEMU images are located, you
|
||||
# probably don't want to use this.
|
||||
#qemu-rootfs = "..."
|
||||
|
||||
# =============================================================================
|
||||
# Distribution options
|
||||
#
|
||||
# These options are related to distribution, mostly for the Rust project itself.
|
||||
# You probably won't need to concern yourself with any of these options
|
||||
# =============================================================================
|
||||
[dist]
|
||||
|
||||
# This is the folder of artifacts that the build system will sign. All files in
|
||||
# this directory will be signed with the default gpg key using the system `gpg`
|
||||
# binary. The `asc` and `sha256` files will all be output into the standard dist
|
||||
# output folder (currently `build/dist`)
|
||||
#
|
||||
# This folder should be populated ahead of time before the build system is
|
||||
# invoked.
|
||||
#sign-folder = "path/to/folder/to/sign"
|
||||
|
||||
# This is a file which contains the password of the default gpg key. This will
|
||||
# be passed to `gpg` down the road when signing all files in `sign-folder`
|
||||
# above. This should be stored in plaintext.
|
||||
#gpg-password-file = "path/to/gpg/password"
|
||||
|
||||
# The remote address that all artifacts will eventually be uploaded to. The
|
||||
# build system generates manifests which will point to these urls, and for the
|
||||
# manifests to be correct they'll have to have the right URLs encoded.
|
||||
#
|
||||
# Note that this address should not contain a trailing slash as file names will
|
||||
# be appended to it.
|
||||
#upload-addr = "https://example.com/folder"
|
||||
|
||||
# Whether to build a plain source tarball to upload
|
||||
# We disable that on Windows not to override the one already uploaded on S3
|
||||
# as the one built on Windows will contain backslashes in paths causing problems
|
||||
# on linux
|
||||
#src-tarball = true
|
782
configure
vendored
782
configure
vendored
@ -1,779 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/bash is.
|
||||
if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then
|
||||
POSIX_SHELL="true"
|
||||
export POSIX_SHELL
|
||||
exec /usr/bin/env bash $0 "$@"
|
||||
fi
|
||||
unset POSIX_SHELL # clear it so if we invoke other scripts, they run as bash as well
|
||||
script="$(dirname $0)"/src/bootstrap/configure.py
|
||||
|
||||
msg() {
|
||||
echo "configure: $*"
|
||||
}
|
||||
|
||||
step_msg() {
|
||||
msg
|
||||
msg "$1"
|
||||
msg
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo "configure: WARNING: $1"
|
||||
}
|
||||
|
||||
err() {
|
||||
echo "configure: error: $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
run() {
|
||||
msg "$@"
|
||||
"$@"
|
||||
}
|
||||
|
||||
need_ok() {
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
err "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
if command -v $1 >/dev/null 2>&1
|
||||
then msg "found program '$1'"
|
||||
else err "program '$1' is missing, please install it"
|
||||
fi
|
||||
}
|
||||
|
||||
make_dir() {
|
||||
if [ ! -d $1 ]
|
||||
then
|
||||
run mkdir -p $1
|
||||
fi
|
||||
}
|
||||
|
||||
copy_if_changed() {
|
||||
if cmp -s $1 $2
|
||||
then
|
||||
msg "leaving $2 unchanged"
|
||||
else
|
||||
run cp -f $1 $2
|
||||
chmod u-w $2 # make copied artifact read-only
|
||||
fi
|
||||
}
|
||||
|
||||
move_if_changed() {
|
||||
if cmp -s $1 $2
|
||||
then
|
||||
msg "leaving $2 unchanged"
|
||||
else
|
||||
run mv -f $1 $2
|
||||
chmod u-w $2 # make moved artifact read-only
|
||||
fi
|
||||
}
|
||||
|
||||
putvar() {
|
||||
local T
|
||||
eval T=\$$1
|
||||
eval TLEN=\${#$1}
|
||||
if [ $TLEN -gt 35 ]
|
||||
then
|
||||
printf "configure: %-20s := %.35s ...\n" $1 "$T"
|
||||
else
|
||||
printf "configure: %-20s := %s %s\n" $1 "$T" "$2"
|
||||
fi
|
||||
printf "%-20s := %s\n" $1 "$T" >>config.tmp
|
||||
}
|
||||
|
||||
putpathvar() {
|
||||
local T
|
||||
eval T=\$$1
|
||||
eval TLEN=\${#$1}
|
||||
if [ $TLEN -gt 35 ]
|
||||
then
|
||||
printf "configure: %-20s := %.35s ...\n" $1 "$T"
|
||||
else
|
||||
printf "configure: %-20s := %s %s\n" $1 "$T" "$2"
|
||||
fi
|
||||
if [ -z "$T" ]
|
||||
then
|
||||
printf "%-20s := \n" $1 >>config.tmp
|
||||
else
|
||||
printf "%-20s := \"%s\"\n" $1 "$T" >>config.tmp
|
||||
fi
|
||||
}
|
||||
|
||||
probe() {
|
||||
local V=$1
|
||||
try() {
|
||||
cmd=$1
|
||||
shift
|
||||
local P
|
||||
local T
|
||||
for P
|
||||
do
|
||||
T=$(command -v $P 2>&1)
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
VER0=$($P --version 2>/dev/null \
|
||||
| grep -o '[vV]\?[0-9][0-9.][a-z0-9.-]*' | head -1 )
|
||||
if [ $? -eq 0 -a "x${VER0}" != "x" ]
|
||||
then
|
||||
VER="($VER0)"
|
||||
else
|
||||
VER=""
|
||||
fi
|
||||
break
|
||||
else
|
||||
VER=""
|
||||
T=""
|
||||
fi
|
||||
done
|
||||
eval $V=\$T
|
||||
putpathvar $V "$VER"
|
||||
}
|
||||
|
||||
probe_need() {
|
||||
probe $*
|
||||
local V=$1
|
||||
shift
|
||||
eval VV=\$$V
|
||||
if [ -z "$VV" ]
|
||||
then
|
||||
err "$V needed, but unable to find any of: $*"
|
||||
T=$($cmd --version 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
exec $cmd "$script" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
validate_opt () {
|
||||
for arg in $CFG_CONFIGURE_ARGS
|
||||
do
|
||||
isArgValid=0
|
||||
for option in $BOOL_OPTIONS
|
||||
do
|
||||
if test --disable-$option = $arg
|
||||
then
|
||||
isArgValid=1
|
||||
fi
|
||||
if test --enable-$option = $arg
|
||||
then
|
||||
isArgValid=1
|
||||
fi
|
||||
done
|
||||
for option in $VAL_OPTIONS
|
||||
do
|
||||
if echo "$arg" | grep -q -- "--$option="
|
||||
then
|
||||
isArgValid=1
|
||||
fi
|
||||
done
|
||||
if [ "$arg" = "--help" ]
|
||||
then
|
||||
echo
|
||||
echo "No more help available for Configure options,"
|
||||
echo "check the Wiki or join our IRC channel"
|
||||
break
|
||||
else
|
||||
if test $isArgValid -eq 0
|
||||
then
|
||||
err "Option '$arg' is not recognized"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# `valopt OPTION_NAME DEFAULT DOC` extracts a string-valued option
|
||||
# from command line, using provided default value for the option if
|
||||
# not present, and saves it to the generated config.mk.
|
||||
#
|
||||
# `valopt_nosave` is much the same, except that it does not save the
|
||||
# result to config.mk (instead the script should use `putvar` itself
|
||||
# later on to save it). `valopt_core` is the core upon which the
|
||||
# other two are built.
|
||||
|
||||
valopt_core() {
|
||||
VAL_OPTIONS="$VAL_OPTIONS $2"
|
||||
|
||||
local SAVE=$1
|
||||
local OP=$2
|
||||
local DEFAULT=$3
|
||||
shift
|
||||
shift
|
||||
shift
|
||||
local DOC="$*"
|
||||
if [ $HELP -eq 0 ]
|
||||
then
|
||||
local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
|
||||
local V="CFG_${UOP}"
|
||||
local V_PROVIDED="${V}_PROVIDED"
|
||||
eval $V="$DEFAULT"
|
||||
for arg in $CFG_CONFIGURE_ARGS
|
||||
do
|
||||
if echo "$arg" | grep -q -- "--$OP="
|
||||
then
|
||||
val=$(echo "$arg" | cut -f2 -d=)
|
||||
eval $V=$val
|
||||
eval $V_PROVIDED=1
|
||||
fi
|
||||
done
|
||||
if [ "$SAVE" = "save" ]
|
||||
then
|
||||
putvar $V
|
||||
fi
|
||||
else
|
||||
if [ -z "$DEFAULT" ]
|
||||
then
|
||||
DEFAULT="<none>"
|
||||
fi
|
||||
OP="${OP}=[${DEFAULT}]"
|
||||
printf " --%-30s %s\n" "$OP" "$DOC"
|
||||
fi
|
||||
}
|
||||
|
||||
valopt_nosave() {
|
||||
valopt_core nosave "$@"
|
||||
}
|
||||
|
||||
valopt() {
|
||||
valopt_core save "$@"
|
||||
}
|
||||
|
||||
# `opt OPTION_NAME DEFAULT DOC` extracts a boolean-valued option from
|
||||
# command line, using the provided default value (0/1) for the option
|
||||
# if not present, and saves it to the generated config.mk.
|
||||
#
|
||||
# `opt_nosave` is much the same, except that it does not save the
|
||||
# result to config.mk (instead the script should use `putvar` itself
|
||||
# later on to save it). `opt_core` is the core upon which the other
|
||||
# two are built.
|
||||
|
||||
opt_core() {
|
||||
BOOL_OPTIONS="$BOOL_OPTIONS $2"
|
||||
|
||||
local SAVE=$1
|
||||
local OP=$2
|
||||
local DEFAULT=$3
|
||||
shift
|
||||
shift
|
||||
shift
|
||||
local DOC="$*"
|
||||
local FLAG=""
|
||||
|
||||
if [ $DEFAULT -eq 0 ]
|
||||
then
|
||||
FLAG="enable"
|
||||
DEFAULT_FLAG="disable"
|
||||
else
|
||||
FLAG="disable"
|
||||
DEFAULT_FLAG="enable"
|
||||
DOC="don't $DOC"
|
||||
fi
|
||||
|
||||
if [ $HELP -eq 0 ]
|
||||
then
|
||||
for arg in $CFG_CONFIGURE_ARGS
|
||||
do
|
||||
if [ "$arg" = "--${FLAG}-${OP}" ]
|
||||
then
|
||||
OP=$(echo $OP | tr 'a-z-' 'A-Z_')
|
||||
FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
|
||||
local V="CFG_${FLAG}_${OP}"
|
||||
local V_PROVIDED="CFG_${FLAG}_${OP}_PROVIDED"
|
||||
eval $V=1
|
||||
eval $V_PROVIDED=1
|
||||
if [ "$SAVE" = "save" ]
|
||||
then
|
||||
putvar $V
|
||||
fi
|
||||
elif [ "$arg" = "--${DEFAULT_FLAG}-${OP}" ]
|
||||
then
|
||||
OP=$(echo $OP | tr 'a-z-' 'A-Z_')
|
||||
DEFAULT_FLAG=$(echo $DEFAULT_FLAG | tr 'a-z' 'A-Z')
|
||||
local V_PROVIDED="CFG_${DEFAULT_FLAG}_${OP}_PROVIDED"
|
||||
eval $V_PROVIDED=1
|
||||
fi
|
||||
done
|
||||
else
|
||||
if [ -n "$META" ]
|
||||
then
|
||||
OP="$OP=<$META>"
|
||||
fi
|
||||
printf " --%-30s %s\n" "$FLAG-$OP" "$DOC"
|
||||
fi
|
||||
}
|
||||
|
||||
opt_nosave() {
|
||||
opt_core nosave "$@"
|
||||
}
|
||||
|
||||
opt() {
|
||||
opt_core save "$@"
|
||||
}
|
||||
|
||||
envopt() {
|
||||
local NAME=$1
|
||||
local V="CFG_${NAME}"
|
||||
eval VV=\$$V
|
||||
|
||||
# If configure didn't set a value already, then check environment.
|
||||
#
|
||||
# (It is recommended that the configure script always check the
|
||||
# environment before setting any values to envopt variables; see
|
||||
# e.g. how CFG_CC is handled, where it first checks `-z "$CC"`,
|
||||
# and issues msg if it ends up employing that provided value.)
|
||||
if [ -z "$VV" ]
|
||||
then
|
||||
eval $V=\$$NAME
|
||||
eval VV=\$$V
|
||||
fi
|
||||
|
||||
# If script or environment provided a value, save it.
|
||||
if [ -n "$VV" ]
|
||||
then
|
||||
putvar $V
|
||||
fi
|
||||
}
|
||||
|
||||
enable_if_not_disabled() {
|
||||
local OP=$1
|
||||
local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
|
||||
local ENAB_V="CFG_ENABLE_$UOP"
|
||||
local EXPLICITLY_DISABLED="CFG_DISABLE_${UOP}_PROVIDED"
|
||||
eval VV=\$$EXPLICITLY_DISABLED
|
||||
if [ -z "$VV" ]; then
|
||||
eval $ENAB_V=1
|
||||
fi
|
||||
}
|
||||
|
||||
to_gnu_triple() {
|
||||
case $1 in
|
||||
i686-pc-windows-gnu) echo i686-w64-mingw32 ;;
|
||||
x86_64-pc-windows-gnu) echo x86_64-w64-mingw32 ;;
|
||||
*) echo $1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Prints the absolute path of a directory to stdout
|
||||
abs_path() {
|
||||
local _path="$1"
|
||||
# Unset CDPATH because it causes havok: it makes the destination unpredictable
|
||||
# and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
|
||||
# for good measure.
|
||||
(unset CDPATH && cd "$_path" > /dev/null && pwd)
|
||||
}
|
||||
|
||||
HELP=0
|
||||
for arg; do
|
||||
case "$arg" in
|
||||
--help) HELP=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
msg "looking for configure programs"
|
||||
need_cmd cmp
|
||||
need_cmd mkdir
|
||||
need_cmd printf
|
||||
need_cmd cut
|
||||
need_cmd head
|
||||
need_cmd grep
|
||||
need_cmd xargs
|
||||
need_cmd cp
|
||||
need_cmd find
|
||||
need_cmd uname
|
||||
need_cmd date
|
||||
need_cmd tr
|
||||
need_cmd sed
|
||||
need_cmd file
|
||||
need_cmd make
|
||||
|
||||
CFG_SRC_DIR="$(abs_path $(dirname $0))/"
|
||||
CFG_SRC_DIR_RELATIVE="$(dirname $0)/"
|
||||
CFG_BUILD_DIR="$(pwd)/"
|
||||
CFG_SELF="$0"
|
||||
CFG_CONFIGURE_ARGS="$@"
|
||||
|
||||
|
||||
case "${CFG_SRC_DIR}" in
|
||||
*\ * )
|
||||
err "The path to the rust source directory contains spaces, which is not supported"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
OPTIONS=""
|
||||
if [ "$HELP" -eq 1 ]
|
||||
then
|
||||
echo
|
||||
echo "Usage: $CFG_SELF [options]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo
|
||||
else
|
||||
msg "recreating config.tmp"
|
||||
echo '' >config.tmp
|
||||
|
||||
step_msg "processing $CFG_SELF args"
|
||||
fi
|
||||
|
||||
BOOL_OPTIONS=""
|
||||
VAL_OPTIONS=""
|
||||
|
||||
opt debug 0 "debug mode; disables optimization unless \`--enable-optimize\` given"
|
||||
opt valgrind 0 "run tests with valgrind (memcheck by default)"
|
||||
opt helgrind 0 "run tests with helgrind instead of memcheck"
|
||||
opt valgrind-rpass 1 "run rpass-valgrind tests with valgrind"
|
||||
opt docs 1 "build standard library documentation"
|
||||
opt compiler-docs 0 "build compiler documentation"
|
||||
opt optimize-tests 1 "build tests with optimizations"
|
||||
opt debuginfo-tests 0 "build tests with debugger metadata"
|
||||
opt quiet-tests 0 "enable quieter output when running tests"
|
||||
opt libcpp 1 "build llvm with libc++ instead of libstdc++ when using clang"
|
||||
opt llvm-assertions 0 "build LLVM with assertions"
|
||||
opt debug-assertions 0 "build with debugging assertions"
|
||||
opt fast-make 0 "use .gitmodules as timestamp for submodule deps"
|
||||
opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds"
|
||||
opt sccache 0 "invoke gcc/clang via sccache to reuse object files between builds"
|
||||
opt local-rust 0 "use an installed rustc rather than downloading a snapshot"
|
||||
opt local-rebuild 0 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version"
|
||||
opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
|
||||
opt llvm-link-shared 0 "prefer shared linking to LLVM (llvm-config --link-shared)"
|
||||
opt rpath 1 "build rpaths into rustc itself"
|
||||
opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0"
|
||||
# This is used by the automation to produce single-target nightlies
|
||||
opt dist-host-only 0 "only install bins for the host architecture"
|
||||
opt inject-std-version 1 "inject the current compiler version of libstd into programs"
|
||||
opt llvm-version-check 1 "check if the LLVM version is supported, build anyway"
|
||||
opt codegen-tests 1 "run the src/test/codegen tests"
|
||||
opt option-checking 1 "complain about unrecognized options in this configure script"
|
||||
opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)"
|
||||
opt locked-deps 0 "force Cargo.lock to be up to date"
|
||||
opt vendor 0 "enable usage of vendored Rust crates"
|
||||
opt sanitizers 0 "build the sanitizer runtimes (asan, lsan, msan, tsan)"
|
||||
opt dist-src 1 "when building tarballs enables building a source tarball"
|
||||
opt cargo-openssl-static 0 "static openssl in cargo"
|
||||
opt profiler 0 "build the profiler runtime"
|
||||
|
||||
# Optimization and debugging options. These may be overridden by the release channel, etc.
|
||||
opt_nosave optimize 1 "build optimized rust code"
|
||||
opt_nosave optimize-cxx 1 "build optimized C++ code"
|
||||
opt_nosave optimize-llvm 1 "build optimized LLVM"
|
||||
opt_nosave llvm-assertions 0 "build LLVM with assertions"
|
||||
opt_nosave debug-assertions 0 "build with debugging assertions"
|
||||
opt_nosave llvm-release-debuginfo 0 "build LLVM with debugger metadata"
|
||||
opt_nosave debuginfo 0 "build with debugger metadata"
|
||||
opt_nosave debuginfo-lines 0 "build with line number debugger metadata"
|
||||
opt_nosave debuginfo-only-std 0 "build only libstd with debugging information"
|
||||
opt_nosave debug-jemalloc 0 "build jemalloc with --enable-debug --enable-fill"
|
||||
|
||||
valopt localstatedir "/var/lib" "local state directory"
|
||||
valopt sysconfdir "/etc" "install system configuration files"
|
||||
|
||||
valopt datadir "${CFG_PREFIX}/share" "install data"
|
||||
valopt infodir "${CFG_PREFIX}/share/info" "install additional info"
|
||||
valopt llvm-root "" "set LLVM root"
|
||||
valopt python "" "set path to python"
|
||||
valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
|
||||
valopt build "" "GNUs ./configure syntax LLVM build triple"
|
||||
valopt android-cross-path "" "Android NDK standalone path (deprecated)"
|
||||
valopt i686-linux-android-ndk "" "i686-linux-android NDK standalone path"
|
||||
valopt arm-linux-androideabi-ndk "" "arm-linux-androideabi NDK standalone path"
|
||||
valopt armv7-linux-androideabi-ndk "" "armv7-linux-androideabi NDK standalone path"
|
||||
valopt aarch64-linux-android-ndk "" "aarch64-linux-android NDK standalone path"
|
||||
valopt x86_64-linux-android-ndk "" "x86_64-linux-android NDK standalone path"
|
||||
valopt nacl-cross-path "" "NaCl SDK path (Pepper Canary is recommended). Must be absolute!"
|
||||
valopt musl-root "/usr/local" "MUSL root installation directory (deprecated)"
|
||||
valopt musl-root-x86_64 "" "x86_64-unknown-linux-musl install directory"
|
||||
valopt musl-root-i686 "" "i686-unknown-linux-musl install directory"
|
||||
valopt musl-root-arm "" "arm-unknown-linux-musleabi install directory"
|
||||
valopt musl-root-armhf "" "arm-unknown-linux-musleabihf install directory"
|
||||
valopt musl-root-armv7 "" "armv7-unknown-linux-musleabihf install directory"
|
||||
valopt extra-filename "" "Additional data that is hashed and passed to the -C extra-filename flag"
|
||||
valopt qemu-armhf-rootfs "" "rootfs in qemu testing, you probably don't want to use this"
|
||||
valopt qemu-aarch64-rootfs "" "rootfs in qemu testing, you probably don't want to use this"
|
||||
valopt experimental-targets "" "experimental LLVM targets to build"
|
||||
|
||||
if [ -e ${CFG_SRC_DIR}.git ]
|
||||
then
|
||||
valopt release-channel "dev" "the name of the release channel to build"
|
||||
else
|
||||
# If we have no git directory then we are probably a tarball distribution
|
||||
# and should default to stable channel - Issue 28322
|
||||
probe CFG_GIT git
|
||||
msg "git: no git directory. Changing default release channel to stable"
|
||||
valopt release-channel "stable" "the name of the release channel to build"
|
||||
fi
|
||||
|
||||
# Used on systems where "cc" and "ar" are unavailable
|
||||
valopt default-linker "cc" "the default linker"
|
||||
valopt default-ar "ar" "the default ar"
|
||||
|
||||
# Many of these are saved below during the "writing configuration" step
|
||||
# (others are conditionally saved).
|
||||
opt_nosave manage-submodules 1 "let the build manage the git submodules"
|
||||
opt_nosave clang 0 "prefer clang to gcc for building the runtime"
|
||||
opt_nosave jemalloc 1 "build liballoc with jemalloc"
|
||||
opt full-bootstrap 0 "build three compilers instead of two"
|
||||
opt extended 0 "build an extended rust tool set"
|
||||
|
||||
valopt_nosave prefix "/usr/local" "set installation prefix"
|
||||
valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary"
|
||||
valopt_nosave host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples"
|
||||
valopt_nosave target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples"
|
||||
valopt_nosave mandir "${CFG_PREFIX}/share/man" "install man pages in PATH"
|
||||
valopt_nosave docdir "${CFG_PREFIX}/share/doc/rust" "install documentation in PATH"
|
||||
valopt_nosave bindir "${CFG_PREFIX}/bin" "install binaries"
|
||||
|
||||
# On Windows this determines root of the subtree for target libraries.
|
||||
# Host runtime libs always go to 'bin'.
|
||||
valopt libdir "${CFG_PREFIX}/lib" "install libraries"
|
||||
|
||||
case "$CFG_LIBDIR" in
|
||||
"$CFG_PREFIX"/*) CAT_INC=2;;
|
||||
"$CFG_PREFIX"*) CAT_INC=1;;
|
||||
*)
|
||||
err "libdir must begin with the prefix. Use --prefix to set it accordingly.";;
|
||||
esac
|
||||
|
||||
CFG_LIBDIR_RELATIVE=`echo ${CFG_LIBDIR} | cut -c$((${#CFG_PREFIX}+${CAT_INC}))-`
|
||||
|
||||
if [ $HELP -eq 1 ]
|
||||
then
|
||||
echo
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Validate Options
|
||||
if [ -z "$CFG_DISABLE_OPTION_CHECKING" ]
|
||||
then
|
||||
step_msg "validating $CFG_SELF args"
|
||||
validate_opt
|
||||
fi
|
||||
|
||||
# Validate the release channel, and configure options
|
||||
case "$CFG_RELEASE_CHANNEL" in
|
||||
nightly )
|
||||
msg "overriding settings for $CFG_RELEASE_CHANNEL"
|
||||
enable_if_not_disabled llvm-assertions
|
||||
# FIXME(stage0) re-enable this on the next stage0 now that #35566 is
|
||||
# fixed
|
||||
case "$CFG_BUILD" in
|
||||
*-pc-windows-gnu)
|
||||
;;
|
||||
*)
|
||||
enable_if_not_disabled debuginfo-lines
|
||||
enable_if_not_disabled debuginfo-only-std
|
||||
;;
|
||||
esac
|
||||
|
||||
;;
|
||||
beta | stable)
|
||||
msg "overriding settings for $CFG_RELEASE_CHANNEL"
|
||||
case "$CFG_BUILD" in
|
||||
*-pc-windows-gnu)
|
||||
;;
|
||||
*)
|
||||
enable_if_not_disabled debuginfo-lines
|
||||
enable_if_not_disabled debuginfo-only-std
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
dev)
|
||||
;;
|
||||
*)
|
||||
err "release channel must be 'dev', 'nightly', 'beta' or 'stable'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Adjust perf and debug options for debug mode
|
||||
if [ -n "$CFG_ENABLE_DEBUG" ]; then
|
||||
msg "debug mode enabled, setting performance options"
|
||||
if [ -z "$CFG_ENABLE_OPTIMIZE_PROVIDED" ]; then
|
||||
msg "optimization not explicitly enabled, disabling optimization"
|
||||
CFG_DISABLE_OPTIMIZE=1
|
||||
CFG_DISABLE_OPTIMIZE_CXX=1
|
||||
fi
|
||||
|
||||
# Set following variables to 1 unless setting already provided
|
||||
enable_if_not_disabled debug-assertions
|
||||
enable_if_not_disabled debug-jemalloc
|
||||
enable_if_not_disabled debuginfo
|
||||
enable_if_not_disabled llvm-assertions
|
||||
fi
|
||||
|
||||
# OK, now write the debugging options
|
||||
if [ -n "$CFG_DISABLE_OPTIMIZE" ]; then putvar CFG_DISABLE_OPTIMIZE; fi
|
||||
if [ -n "$CFG_DISABLE_OPTIMIZE_CXX" ]; then putvar CFG_DISABLE_OPTIMIZE_CXX; fi
|
||||
if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then putvar CFG_DISABLE_OPTIMIZE_LLVM; fi
|
||||
if [ -n "$CFG_ENABLE_LLVM_ASSERTIONS" ]; then putvar CFG_ENABLE_LLVM_ASSERTIONS; fi
|
||||
if [ -n "$CFG_ENABLE_DEBUG_ASSERTIONS" ]; then putvar CFG_ENABLE_DEBUG_ASSERTIONS; fi
|
||||
if [ -n "$CFG_ENABLE_LLVM_RELEASE_DEBUGINFO" ]; then putvar CFG_ENABLE_LLVM_RELEASE_DEBUGINFO; fi
|
||||
if [ -n "$CFG_ENABLE_DEBUGINFO" ]; then putvar CFG_ENABLE_DEBUGINFO; fi
|
||||
if [ -n "$CFG_ENABLE_DEBUGINFO_LINES" ]; then putvar CFG_ENABLE_DEBUGINFO_LINES; fi
|
||||
if [ -n "$CFG_ENABLE_DEBUGINFO_ONLY_STD" ]; then putvar CFG_ENABLE_DEBUGINFO_ONLY_STD; fi
|
||||
if [ -n "$CFG_ENABLE_DEBUG_JEMALLOC" ]; then putvar CFG_ENABLE_DEBUG_JEMALLOC; fi
|
||||
|
||||
step_msg "looking for build programs"
|
||||
|
||||
probe_need CFG_CURL curl
|
||||
if [ -z "$CFG_PYTHON_PROVIDED" ]; then
|
||||
probe_need CFG_PYTHON python2.7 python2 python
|
||||
fi
|
||||
|
||||
python_version=$($CFG_PYTHON -V 2>&1)
|
||||
if [ $(echo $python_version | grep -c '^Python 2\.7') -ne 1 ]; then
|
||||
err "Found $python_version, but Python 2.7 is required"
|
||||
fi
|
||||
|
||||
# the valgrind rpass tests will fail if you don't have a valgrind, but they're
|
||||
# only disabled if you opt out.
|
||||
if [ -z "$CFG_VALGRIND" ]
|
||||
then
|
||||
# If the user has explicitly asked for valgrind tests, then fail
|
||||
if [ -n "$CFG_ENABLE_VALGRIND" ] && [ -n "$CFG_ENABLE_VALGRIND_PROVIDED" ]
|
||||
then
|
||||
err "No valgrind present, but valgrind tests explicitly requested"
|
||||
else
|
||||
CFG_DISABLE_VALGRIND_RPASS=1
|
||||
putvar CFG_DISABLE_VALGRIND_RPASS
|
||||
fi
|
||||
fi
|
||||
|
||||
# Do some sanity checks if running on buildbot
|
||||
# (these env vars are set by rust-buildbot)
|
||||
if [ -n "$RUST_DIST_SERVER" -a -n "$ALLOW_NONZERO_RLIMIT_CORE" ]; then
|
||||
# Frequently the llvm submodule directory is broken by the build
|
||||
# being killed
|
||||
llvm_lock="${CFG_SRC_DIR}/.git/modules/src/llvm/index.lock"
|
||||
if [ -e "$llvm_lock" ]; then
|
||||
step_msg "removing $llvm_lock"
|
||||
rm -f "$llvm_lock"
|
||||
fi
|
||||
fi
|
||||
|
||||
BIN_SUF=
|
||||
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ]
|
||||
then
|
||||
BIN_SUF=.exe
|
||||
fi
|
||||
|
||||
# --enable-local-rebuild implies --enable-local-rust too
|
||||
if [ -n "$CFG_ENABLE_LOCAL_REBUILD" ]
|
||||
then
|
||||
if [ -z "$CFG_ENABLE_LOCAL_RUST" ]
|
||||
then
|
||||
CFG_ENABLE_LOCAL_RUST=1
|
||||
putvar CFG_ENABLE_LOCAL_RUST
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$CFG_ENABLE_LOCAL_RUST" ]
|
||||
then
|
||||
system_rustc=$(which rustc)
|
||||
if [ -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
|
||||
then
|
||||
: # everything already configured
|
||||
elif [ -n "$system_rustc" ]
|
||||
then
|
||||
# we assume that rustc is in a /bin directory
|
||||
CFG_LOCAL_RUST_ROOT=${system_rustc%/bin/rustc}
|
||||
else
|
||||
err "no local rust to use"
|
||||
fi
|
||||
|
||||
CMD="${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF}"
|
||||
LRV=`LD_LIBRARY_PATH=${CFG_LOCAL_RUST_ROOT}/lib $CMD --version`
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
step_msg "failure while running $CMD --version"
|
||||
exit 1
|
||||
fi
|
||||
step_msg "using rustc at: ${CFG_LOCAL_RUST_ROOT} with version: $LRV"
|
||||
putvar CFG_LOCAL_RUST_ROOT
|
||||
fi
|
||||
|
||||
# Same with jemalloc. save the setting here.
|
||||
if [ -n "$CFG_DISABLE_JEMALLOC" ]
|
||||
then
|
||||
putvar CFG_DISABLE_JEMALLOC
|
||||
fi
|
||||
|
||||
# All safeguards based on $CFG_ENABLE_CLANG should occur before this
|
||||
# point in the script; after this point, script logic should inspect
|
||||
# $CFG_USING_CLANG rather than $CFG_ENABLE_CLANG.
|
||||
|
||||
# Set CFG_{CC,CXX,CPP,CFLAGS,CXXFLAGS,LDFLAGS}
|
||||
envopt CC
|
||||
envopt CXX
|
||||
envopt CPP
|
||||
envopt CFLAGS
|
||||
envopt CXXFLAGS
|
||||
envopt LDFLAGS
|
||||
|
||||
# a little post-processing of various config values
|
||||
CFG_PREFIX=${CFG_PREFIX%/}
|
||||
CFG_MANDIR=${CFG_MANDIR%/}
|
||||
CFG_DOCDIR=${CFG_DOCDIR%/}
|
||||
CFG_BINDIR=${CFG_BINDIR%/}
|
||||
CFG_HOST="$(echo $CFG_HOST | tr ',' ' ')"
|
||||
CFG_TARGET="$(echo $CFG_TARGET | tr ',' ' ')"
|
||||
|
||||
# copy build-triples to host-triples so that builds are a subset of hosts
|
||||
V_TEMP=""
|
||||
for i in $CFG_BUILD $CFG_HOST;
|
||||
do
|
||||
echo "$V_TEMP" | grep -qF $i || V_TEMP="$V_TEMP${V_TEMP:+ }$i"
|
||||
done
|
||||
CFG_HOST=$V_TEMP
|
||||
|
||||
# copy host-triples to target-triples so that hosts are a subset of targets
|
||||
V_TEMP=""
|
||||
for i in $CFG_HOST $CFG_TARGET;
|
||||
do
|
||||
echo "$V_TEMP" | grep -qF $i || V_TEMP="$V_TEMP${V_TEMP:+ }$i"
|
||||
done
|
||||
CFG_TARGET=$V_TEMP
|
||||
|
||||
step_msg "writing configuration"
|
||||
|
||||
putvar CFG_SRC_DIR
|
||||
putvar CFG_SRC_DIR_RELATIVE
|
||||
putvar CFG_BUILD_DIR
|
||||
putvar CFG_OSTYPE
|
||||
putvar CFG_CPUTYPE
|
||||
putvar CFG_CONFIGURE_ARGS
|
||||
putvar CFG_PREFIX
|
||||
putvar CFG_HOST
|
||||
putvar CFG_TARGET
|
||||
putvar CFG_LIBDIR_RELATIVE
|
||||
putvar CFG_DISABLE_MANAGE_SUBMODULES
|
||||
putvar CFG_AARCH64_LINUX_ANDROID_NDK
|
||||
putvar CFG_ARM_LINUX_ANDROIDEABI_NDK
|
||||
putvar CFG_ARMV7_LINUX_ANDROIDEABI_NDK
|
||||
putvar CFG_I686_LINUX_ANDROID_NDK
|
||||
putvar CFG_X86_64_LINUX_ANDROID_NDK
|
||||
putvar CFG_NACL_CROSS_PATH
|
||||
putvar CFG_MANDIR
|
||||
putvar CFG_DOCDIR
|
||||
putvar CFG_BINDIR
|
||||
putvar CFG_USING_LIBCPP
|
||||
|
||||
msg
|
||||
copy_if_changed ${CFG_SRC_DIR}src/bootstrap/mk/Makefile.in ./Makefile
|
||||
move_if_changed config.tmp config.mk
|
||||
rm -f config.tmp
|
||||
touch config.stamp
|
||||
|
||||
if [ -z "$CFG_ENABLE_DEBUG" ]; then
|
||||
step_msg "configured in release mode. for development consider --enable-debug"
|
||||
else
|
||||
step_msg "complete"
|
||||
fi
|
||||
|
||||
if [ "$CFG_SRC_DIR" = `pwd` ]; then
|
||||
X_PY=x.py
|
||||
else
|
||||
X_PY=${CFG_SRC_DIR_RELATIVE}x.py
|
||||
fi
|
||||
|
||||
msg "run \`python ${X_PY} --help\`"
|
||||
msg
|
||||
try python2.7 "$@"
|
||||
try python27 "$@"
|
||||
try python2 "$@"
|
||||
exec python $script "$@"
|
||||
|
1
git-commit-hash
Normal file
1
git-commit-hash
Normal file
@ -0,0 +1 @@
|
||||
05e2e1c41414e8fc73d0f267ea8dab1a3eeeaa99
|
910
src/Cargo.lock
generated
910
src/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ members = [
|
||||
"tools/cargo",
|
||||
"tools/rustdoc",
|
||||
"tools/rls",
|
||||
"tools/rustfmt",
|
||||
# FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude
|
||||
"tools/rls/test_data/borrow_error",
|
||||
"tools/rls/test_data/completion",
|
||||
@ -37,6 +38,8 @@ members = [
|
||||
"tools/rls/test_data/infer_custom_bin",
|
||||
"tools/rls/test_data/infer_lib",
|
||||
"tools/rls/test_data/omit_init_build",
|
||||
"tools/rls/test_data/unicødë",
|
||||
"tools/rls/test_data/workspace_symbol",
|
||||
]
|
||||
|
||||
# Curiously, compiletest will segfault if compiled with opt-level=3 on 64-bit
|
||||
@ -55,3 +58,9 @@ debug-assertions = false
|
||||
[profile.test]
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
|
||||
[patch."https://github.com/rust-lang/cargo"]
|
||||
cargo = { path = "tools/cargo" }
|
||||
|
||||
[patch.crates-io]
|
||||
rustfmt-nightly = { path = "tools/rustfmt" }
|
||||
|
@ -34,7 +34,7 @@ cmake = "0.1.23"
|
||||
filetime = "0.1"
|
||||
num_cpus = "1.0"
|
||||
getopts = "0.2"
|
||||
gcc = "0.3.50"
|
||||
cc = "1.0"
|
||||
libc = "0.2"
|
||||
serde = "1.0.8"
|
||||
serde_derive = "1.0.8"
|
||||
|
@ -76,10 +76,9 @@ The script accepts commands, flags, and arguments to determine what to do:
|
||||
There are currently two methods for configuring the rustbuild build system.
|
||||
|
||||
First, rustbuild offers a TOML-based configuration system with a `config.toml`
|
||||
file in the same location as `config.mk`. An example of this configuration can
|
||||
be found at `config.toml.example`, and the configuration file can also be passed
|
||||
as `--config path/to/config.toml` if the build system is being invoked manually
|
||||
(via the python script).
|
||||
file. An example of this configuration can be found at `config.toml.example`,
|
||||
and the configuration file can also be passed as `--config path/to/config.toml`
|
||||
if the build system is being invoked manually (via the python script).
|
||||
|
||||
Next, the `./configure` options serialized in `config.mk` will be
|
||||
parsed and read. That is, if any `./configure` options are passed, they'll be
|
||||
|
@ -246,6 +246,12 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// When running miri tests, we need to generate MIR for all libraries
|
||||
if env::var("TEST_MIRI").ok().map_or(false, |val| val == "true") {
|
||||
cmd.arg("-Zalways-encode-mir");
|
||||
cmd.arg("-Zmir-emit-validate=1");
|
||||
}
|
||||
|
||||
// Force all crates compiled by this compiler to (a) be unstable and (b)
|
||||
// allow the `rustc_private` feature to link to other unstable crates
|
||||
// also in the sysroot.
|
||||
|
@ -8,7 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
extern crate gcc;
|
||||
extern crate cc;
|
||||
|
||||
use std::env;
|
||||
use std::process::{self, Command};
|
||||
@ -18,12 +18,13 @@ fn main() {
|
||||
// Locate the actual compiler that we're invoking
|
||||
env::remove_var("CC");
|
||||
env::remove_var("CXX");
|
||||
let mut cfg = gcc::Config::new();
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false)
|
||||
.out_dir("/")
|
||||
.target(&target)
|
||||
.host(&target)
|
||||
.opt_level(0)
|
||||
.warnings(false)
|
||||
.debug(false);
|
||||
let compiler = cfg.get_compiler();
|
||||
|
||||
|
@ -167,6 +167,141 @@ def format_build_time(duration):
|
||||
return str(datetime.timedelta(seconds=int(duration)))
|
||||
|
||||
|
||||
def default_build_triple():
|
||||
"""Build triple as in LLVM"""
|
||||
default_encoding = sys.getdefaultencoding()
|
||||
try:
|
||||
ostype = subprocess.check_output(
|
||||
['uname', '-s']).strip().decode(default_encoding)
|
||||
cputype = subprocess.check_output(
|
||||
['uname', '-m']).strip().decode(default_encoding)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
if sys.platform == 'win32':
|
||||
return 'x86_64-pc-windows-msvc'
|
||||
err = "uname not found"
|
||||
sys.exit(err)
|
||||
|
||||
# The goal here is to come up with the same triple as LLVM would,
|
||||
# at least for the subset of platforms we're willing to target.
|
||||
ostype_mapper = {
|
||||
'Bitrig': 'unknown-bitrig',
|
||||
'Darwin': 'apple-darwin',
|
||||
'DragonFly': 'unknown-dragonfly',
|
||||
'FreeBSD': 'unknown-freebsd',
|
||||
'Haiku': 'unknown-haiku',
|
||||
'NetBSD': 'unknown-netbsd',
|
||||
'OpenBSD': 'unknown-openbsd'
|
||||
}
|
||||
|
||||
# Consider the direct transformation first and then the special cases
|
||||
if ostype in ostype_mapper:
|
||||
ostype = ostype_mapper[ostype]
|
||||
elif ostype == 'Linux':
|
||||
os_from_sp = subprocess.check_output(
|
||||
['uname', '-o']).strip().decode(default_encoding)
|
||||
if os_from_sp == 'Android':
|
||||
ostype = 'linux-android'
|
||||
else:
|
||||
ostype = 'unknown-linux-gnu'
|
||||
elif ostype == 'SunOS':
|
||||
ostype = 'sun-solaris'
|
||||
# On Solaris, uname -m will return a machine classification instead
|
||||
# of a cpu type, so uname -p is recommended instead. However, the
|
||||
# output from that option is too generic for our purposes (it will
|
||||
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
|
||||
# must be used instead.
|
||||
try:
|
||||
cputype = subprocess.check_output(
|
||||
['isainfo', '-k']).strip().decode(default_encoding)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
err = "isainfo not found"
|
||||
sys.exit(err)
|
||||
elif ostype.startswith('MINGW'):
|
||||
# msys' `uname` does not print gcc configuration, but prints msys
|
||||
# configuration. so we cannot believe `uname -m`:
|
||||
# msys1 is always i686 and msys2 is always x86_64.
|
||||
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
|
||||
# MINGW64 on x86_64.
|
||||
ostype = 'pc-windows-gnu'
|
||||
cputype = 'i686'
|
||||
if os.environ.get('MSYSTEM') == 'MINGW64':
|
||||
cputype = 'x86_64'
|
||||
elif ostype.startswith('MSYS'):
|
||||
ostype = 'pc-windows-gnu'
|
||||
elif ostype.startswith('CYGWIN_NT'):
|
||||
cputype = 'i686'
|
||||
if ostype.endswith('WOW64'):
|
||||
cputype = 'x86_64'
|
||||
ostype = 'pc-windows-gnu'
|
||||
else:
|
||||
err = "unknown OS type: {}".format(ostype)
|
||||
sys.exit(err)
|
||||
|
||||
cputype_mapper = {
|
||||
'BePC': 'i686',
|
||||
'aarch64': 'aarch64',
|
||||
'amd64': 'x86_64',
|
||||
'arm64': 'aarch64',
|
||||
'i386': 'i686',
|
||||
'i486': 'i686',
|
||||
'i686': 'i686',
|
||||
'i786': 'i686',
|
||||
'powerpc': 'powerpc',
|
||||
'powerpc64': 'powerpc64',
|
||||
'powerpc64le': 'powerpc64le',
|
||||
'ppc': 'powerpc',
|
||||
'ppc64': 'powerpc64',
|
||||
'ppc64le': 'powerpc64le',
|
||||
's390x': 's390x',
|
||||
'x64': 'x86_64',
|
||||
'x86': 'i686',
|
||||
'x86-64': 'x86_64',
|
||||
'x86_64': 'x86_64'
|
||||
}
|
||||
|
||||
# Consider the direct transformation first and then the special cases
|
||||
if cputype in cputype_mapper:
|
||||
cputype = cputype_mapper[cputype]
|
||||
elif cputype in {'xscale', 'arm'}:
|
||||
cputype = 'arm'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
elif cputype == 'armv6l':
|
||||
cputype = 'arm'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
else:
|
||||
ostype += 'eabihf'
|
||||
elif cputype in {'armv7l', 'armv8l'}:
|
||||
cputype = 'armv7'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
else:
|
||||
ostype += 'eabihf'
|
||||
elif cputype == 'mips':
|
||||
if sys.byteorder == 'big':
|
||||
cputype = 'mips'
|
||||
elif sys.byteorder == 'little':
|
||||
cputype = 'mipsel'
|
||||
else:
|
||||
raise ValueError("unknown byteorder: {}".format(sys.byteorder))
|
||||
elif cputype == 'mips64':
|
||||
if sys.byteorder == 'big':
|
||||
cputype = 'mips64'
|
||||
elif sys.byteorder == 'little':
|
||||
cputype = 'mips64el'
|
||||
else:
|
||||
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
|
||||
# only the n64 ABI is supported, indicate it
|
||||
ostype += 'abi64'
|
||||
elif cputype == 'sparcv9':
|
||||
pass
|
||||
else:
|
||||
err = "unknown cpu type: {}".format(cputype)
|
||||
sys.exit(err)
|
||||
|
||||
return "{}-{}".format(cputype, ostype)
|
||||
|
||||
class RustBuild(object):
|
||||
"""Provide all the methods required to build Rust"""
|
||||
def __init__(self):
|
||||
@ -177,7 +312,6 @@ class RustBuild(object):
|
||||
self.build = ''
|
||||
self.build_dir = os.path.join(os.getcwd(), "build")
|
||||
self.clean = False
|
||||
self.config_mk = ''
|
||||
self.config_toml = ''
|
||||
self.printed = False
|
||||
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
|
||||
@ -374,26 +508,6 @@ class RustBuild(object):
|
||||
return self.get_string(value) or value.strip()
|
||||
return None
|
||||
|
||||
def get_mk(self, key):
|
||||
"""Returns the value of the given key in config.mk, otherwise returns None
|
||||
|
||||
>>> rb = RustBuild()
|
||||
>>> rb.config_mk = 'key := value\\n'
|
||||
>>> rb.get_mk('key')
|
||||
'value'
|
||||
|
||||
If the key does not exists, the result is None:
|
||||
|
||||
>>> rb.get_mk('does_not_exists') == None
|
||||
True
|
||||
"""
|
||||
for line in iter(self.config_mk.splitlines()):
|
||||
if line.startswith(key + ' '):
|
||||
var = line[line.find(':=') + 2:].strip()
|
||||
if var != '':
|
||||
return var
|
||||
return None
|
||||
|
||||
def cargo(self):
|
||||
"""Return config path for cargo"""
|
||||
return self.program_config('cargo')
|
||||
@ -407,15 +521,9 @@ class RustBuild(object):
|
||||
|
||||
>>> rb = RustBuild()
|
||||
>>> rb.config_toml = 'rustc = "rustc"\\n'
|
||||
>>> rb.config_mk = 'CFG_LOCAL_RUST_ROOT := /tmp/rust\\n'
|
||||
>>> rb.program_config('rustc')
|
||||
'rustc'
|
||||
>>> cargo_path = rb.program_config('cargo')
|
||||
>>> cargo_path.rstrip(".exe") == os.path.join("/tmp/rust",
|
||||
... "bin", "cargo")
|
||||
True
|
||||
>>> rb.config_toml = ''
|
||||
>>> rb.config_mk = ''
|
||||
>>> cargo_path = rb.program_config('cargo')
|
||||
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
|
||||
... "bin", "cargo")
|
||||
@ -424,10 +532,6 @@ class RustBuild(object):
|
||||
config = self.get_toml(program)
|
||||
if config:
|
||||
return config
|
||||
config = self.get_mk('CFG_LOCAL_RUST_ROOT')
|
||||
if config:
|
||||
return os.path.join(config, "bin", "{}{}".format(
|
||||
program, self.exe_suffix()))
|
||||
return os.path.join(self.bin_root(), "bin", "{}{}".format(
|
||||
program, self.exe_suffix()))
|
||||
|
||||
@ -439,10 +543,14 @@ class RustBuild(object):
|
||||
'devel'
|
||||
"""
|
||||
start = line.find('"')
|
||||
if start == -1:
|
||||
return None
|
||||
end = start + 1 + line[start + 1:].find('"')
|
||||
return line[start + 1:end]
|
||||
if start != -1:
|
||||
end = start + 1 + line[start + 1:].find('"')
|
||||
return line[start + 1:end]
|
||||
start = line.find('\'')
|
||||
if start != -1:
|
||||
end = start + 1 + line[start + 1:].find('\'')
|
||||
return line[start + 1:end]
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def exe_suffix():
|
||||
@ -517,162 +625,19 @@ class RustBuild(object):
|
||||
|
||||
def build_triple(self):
|
||||
"""Build triple as in LLVM"""
|
||||
default_encoding = sys.getdefaultencoding()
|
||||
config = self.get_toml('build')
|
||||
if config:
|
||||
return config
|
||||
config = self.get_mk('CFG_BUILD')
|
||||
if config:
|
||||
return config
|
||||
try:
|
||||
ostype = subprocess.check_output(
|
||||
['uname', '-s']).strip().decode(default_encoding)
|
||||
cputype = subprocess.check_output(
|
||||
['uname', '-m']).strip().decode(default_encoding)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
if sys.platform == 'win32':
|
||||
return 'x86_64-pc-windows-msvc'
|
||||
err = "uname not found"
|
||||
if self.verbose:
|
||||
raise Exception(err)
|
||||
sys.exit(err)
|
||||
|
||||
# The goal here is to come up with the same triple as LLVM would,
|
||||
# at least for the subset of platforms we're willing to target.
|
||||
ostype_mapper = {
|
||||
'Bitrig': 'unknown-bitrig',
|
||||
'Darwin': 'apple-darwin',
|
||||
'DragonFly': 'unknown-dragonfly',
|
||||
'FreeBSD': 'unknown-freebsd',
|
||||
'Haiku': 'unknown-haiku',
|
||||
'NetBSD': 'unknown-netbsd',
|
||||
'OpenBSD': 'unknown-openbsd'
|
||||
}
|
||||
|
||||
# Consider the direct transformation first and then the special cases
|
||||
if ostype in ostype_mapper:
|
||||
ostype = ostype_mapper[ostype]
|
||||
elif ostype == 'Linux':
|
||||
os_from_sp = subprocess.check_output(
|
||||
['uname', '-o']).strip().decode(default_encoding)
|
||||
if os_from_sp == 'Android':
|
||||
ostype = 'linux-android'
|
||||
else:
|
||||
ostype = 'unknown-linux-gnu'
|
||||
elif ostype == 'SunOS':
|
||||
ostype = 'sun-solaris'
|
||||
# On Solaris, uname -m will return a machine classification instead
|
||||
# of a cpu type, so uname -p is recommended instead. However, the
|
||||
# output from that option is too generic for our purposes (it will
|
||||
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
|
||||
# must be used instead.
|
||||
try:
|
||||
cputype = subprocess.check_output(
|
||||
['isainfo', '-k']).strip().decode(default_encoding)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
err = "isainfo not found"
|
||||
if self.verbose:
|
||||
raise Exception(err)
|
||||
sys.exit(err)
|
||||
elif ostype.startswith('MINGW'):
|
||||
# msys' `uname` does not print gcc configuration, but prints msys
|
||||
# configuration. so we cannot believe `uname -m`:
|
||||
# msys1 is always i686 and msys2 is always x86_64.
|
||||
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
|
||||
# MINGW64 on x86_64.
|
||||
ostype = 'pc-windows-gnu'
|
||||
cputype = 'i686'
|
||||
if os.environ.get('MSYSTEM') == 'MINGW64':
|
||||
cputype = 'x86_64'
|
||||
elif ostype.startswith('MSYS'):
|
||||
ostype = 'pc-windows-gnu'
|
||||
elif ostype.startswith('CYGWIN_NT'):
|
||||
cputype = 'i686'
|
||||
if ostype.endswith('WOW64'):
|
||||
cputype = 'x86_64'
|
||||
ostype = 'pc-windows-gnu'
|
||||
else:
|
||||
err = "unknown OS type: {}".format(ostype)
|
||||
if self.verbose:
|
||||
raise ValueError(err)
|
||||
sys.exit(err)
|
||||
|
||||
cputype_mapper = {
|
||||
'BePC': 'i686',
|
||||
'aarch64': 'aarch64',
|
||||
'amd64': 'x86_64',
|
||||
'arm64': 'aarch64',
|
||||
'i386': 'i686',
|
||||
'i486': 'i686',
|
||||
'i686': 'i686',
|
||||
'i786': 'i686',
|
||||
'powerpc': 'powerpc',
|
||||
'powerpc64': 'powerpc64',
|
||||
'powerpc64le': 'powerpc64le',
|
||||
'ppc': 'powerpc',
|
||||
'ppc64': 'powerpc64',
|
||||
'ppc64le': 'powerpc64le',
|
||||
's390x': 's390x',
|
||||
'x64': 'x86_64',
|
||||
'x86': 'i686',
|
||||
'x86-64': 'x86_64',
|
||||
'x86_64': 'x86_64'
|
||||
}
|
||||
|
||||
# Consider the direct transformation first and then the special cases
|
||||
if cputype in cputype_mapper:
|
||||
cputype = cputype_mapper[cputype]
|
||||
elif cputype in {'xscale', 'arm'}:
|
||||
cputype = 'arm'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
elif cputype == 'armv6l':
|
||||
cputype = 'arm'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
else:
|
||||
ostype += 'eabihf'
|
||||
elif cputype in {'armv7l', 'armv8l'}:
|
||||
cputype = 'armv7'
|
||||
if ostype == 'linux-android':
|
||||
ostype = 'linux-androideabi'
|
||||
else:
|
||||
ostype += 'eabihf'
|
||||
elif cputype == 'mips':
|
||||
if sys.byteorder == 'big':
|
||||
cputype = 'mips'
|
||||
elif sys.byteorder == 'little':
|
||||
cputype = 'mipsel'
|
||||
else:
|
||||
raise ValueError("unknown byteorder: {}".format(sys.byteorder))
|
||||
elif cputype == 'mips64':
|
||||
if sys.byteorder == 'big':
|
||||
cputype = 'mips64'
|
||||
elif sys.byteorder == 'little':
|
||||
cputype = 'mips64el'
|
||||
else:
|
||||
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
|
||||
# only the n64 ABI is supported, indicate it
|
||||
ostype += 'abi64'
|
||||
elif cputype == 'sparcv9':
|
||||
pass
|
||||
else:
|
||||
err = "unknown cpu type: {}".format(cputype)
|
||||
if self.verbose:
|
||||
raise ValueError(err)
|
||||
sys.exit(err)
|
||||
|
||||
return "{}-{}".format(cputype, ostype)
|
||||
return default_build_triple()
|
||||
|
||||
def update_submodules(self):
|
||||
"""Update submodules"""
|
||||
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
|
||||
self.get_toml('submodules') == "false" or \
|
||||
self.get_mk('CFG_DISABLE_MANAGE_SUBMODULES') == "1":
|
||||
self.get_toml('submodules') == "false":
|
||||
return
|
||||
print('Updating submodules')
|
||||
default_encoding = sys.getdefaultencoding()
|
||||
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root)
|
||||
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root, verbose=self.verbose)
|
||||
submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
|
||||
["git", "config", "--file",
|
||||
os.path.join(self.rust_root, ".gitmodules"),
|
||||
@ -680,11 +645,9 @@ class RustBuild(object):
|
||||
).decode(default_encoding).splitlines()]
|
||||
submodules = [module for module in submodules
|
||||
if not ((module.endswith("llvm") and
|
||||
(self.get_toml('llvm-config') or
|
||||
self.get_mk('CFG_LLVM_ROOT'))) or
|
||||
self.get_toml('llvm-config')) or
|
||||
(module.endswith("jemalloc") and
|
||||
(self.get_toml('jemalloc') or
|
||||
self.get_mk('CFG_JEMALLOC_ROOT'))))]
|
||||
self.get_toml('jemalloc')))]
|
||||
run(["git", "submodule", "update",
|
||||
"--init", "--recursive"] + submodules,
|
||||
cwd=self.rust_root, verbose=self.verbose)
|
||||
@ -719,11 +682,7 @@ def bootstrap():
|
||||
try:
|
||||
with open(args.config or 'config.toml') as config:
|
||||
build.config_toml = config.read()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
build.config_mk = open('config.mk').read()
|
||||
except:
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
|
||||
if '\nverbose = 2' in build.config_toml:
|
||||
@ -731,11 +690,9 @@ def bootstrap():
|
||||
elif '\nverbose = 1' in build.config_toml:
|
||||
build.verbose = 1
|
||||
|
||||
build.use_vendored_sources = '\nvendor = true' in build.config_toml or \
|
||||
'CFG_ENABLE_VENDOR' in build.config_mk
|
||||
build.use_vendored_sources = '\nvendor = true' in build.config_toml
|
||||
|
||||
build.use_locked_deps = '\nlocked-deps = true' in build.config_toml or \
|
||||
'CFG_ENABLE_LOCKED_DEPS' in build.config_mk
|
||||
build.use_locked_deps = '\nlocked-deps = true' in build.config_toml
|
||||
|
||||
if 'SUDO_USER' in os.environ and not build.use_vendored_sources:
|
||||
if os.environ.get('USER') != os.environ['SUDO_USER']:
|
||||
|
@ -15,6 +15,7 @@ import doctest
|
||||
import unittest
|
||||
import tempfile
|
||||
import hashlib
|
||||
import sys
|
||||
|
||||
from shutil import rmtree
|
||||
|
||||
@ -110,5 +111,6 @@ if __name__ == '__main__':
|
||||
TEST_LOADER.loadTestsFromTestCase(VerifyTestCase),
|
||||
TEST_LOADER.loadTestsFromTestCase(ProgramOutOfDate)])
|
||||
|
||||
RUNNER = unittest.TextTestRunner(verbosity=2)
|
||||
RUNNER.run(SUITE)
|
||||
RUNNER = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
|
||||
result = RUNNER.run(SUITE)
|
||||
sys.exit(0 if result.wasSuccessful() else 1)
|
||||
|
@ -8,15 +8,16 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::fs;
|
||||
use std::ops::Deref;
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use compile;
|
||||
use install;
|
||||
@ -248,19 +249,19 @@ impl<'a> Builder<'a> {
|
||||
compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex,
|
||||
tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest,
|
||||
tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
|
||||
tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc,
|
||||
native::Llvm),
|
||||
tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy,
|
||||
native::Llvm, tool::Rustfmt, tool::Miri),
|
||||
Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest,
|
||||
check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Linkcheck,
|
||||
check::Cargotest, check::Cargo, check::Rls, check::Docs, check::ErrorIndex,
|
||||
check::Distcheck),
|
||||
check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Rustdoc,
|
||||
check::Linkcheck, check::Cargotest, check::Cargo, check::Rls, check::Docs,
|
||||
check::ErrorIndex, check::Distcheck, check::Rustfmt, check::Miri, check::Clippy),
|
||||
Kind::Bench => describe!(check::Crate, check::CrateLibrustc),
|
||||
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
|
||||
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
|
||||
doc::Reference, doc::Rustdoc, doc::CargoBook),
|
||||
Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
|
||||
dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo,
|
||||
dist::Rls, dist::Extended, dist::HashSign),
|
||||
dist::Rls, dist::Extended, dist::HashSign, dist::DontDistWithMiriEnabled),
|
||||
Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
|
||||
install::Analysis, install::Src, install::Rustc),
|
||||
}
|
||||
@ -305,7 +306,7 @@ impl<'a> Builder<'a> {
|
||||
Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
|
||||
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
|
||||
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
|
||||
Subcommand::Clean => panic!(),
|
||||
Subcommand::Clean { .. } => panic!(),
|
||||
};
|
||||
|
||||
let builder = Builder {
|
||||
@ -437,9 +438,14 @@ impl<'a> Builder<'a> {
|
||||
let out_dir = self.stage_out(compiler, mode);
|
||||
cargo.env("CARGO_TARGET_DIR", out_dir)
|
||||
.arg(cmd)
|
||||
.arg("-j").arg(self.jobs().to_string())
|
||||
.arg("--target").arg(target);
|
||||
|
||||
// If we were invoked from `make` then that's already got a jobserver
|
||||
// set up for us so no need to tell Cargo about jobs all over again.
|
||||
if env::var_os("MAKEFLAGS").is_none() && env::var_os("MFLAGS").is_none() {
|
||||
cargo.arg("-j").arg(self.jobs().to_string());
|
||||
}
|
||||
|
||||
// FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
|
||||
// Force cargo to output binaries with disambiguating hashes in the name
|
||||
cargo.env("__CARGO_DEFAULT_LIB_METADATA", &self.config.channel);
|
||||
@ -475,6 +481,7 @@ impl<'a> Builder<'a> {
|
||||
} else {
|
||||
PathBuf::from("/path/to/nowhere/rustdoc/not/required")
|
||||
})
|
||||
.env("TEST_MIRI", self.config.test_miri.to_string())
|
||||
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
|
||||
|
||||
if mode != Mode::Tool {
|
||||
@ -524,7 +531,10 @@ impl<'a> Builder<'a> {
|
||||
// For other crates, however, we know that we've already got a standard
|
||||
// library up and running, so we can use the normal compiler to compile
|
||||
// build scripts in that situation.
|
||||
if mode == Mode::Libstd {
|
||||
//
|
||||
// If LLVM support is disabled we need to use the snapshot compiler to compile
|
||||
// build scripts, as the new compiler doesnt support executables.
|
||||
if mode == Mode::Libstd || !self.build.config.llvm_enabled {
|
||||
cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
|
||||
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
|
||||
} else {
|
||||
|
@ -23,7 +23,7 @@
|
||||
//! 6. "cc"
|
||||
//!
|
||||
//! Some of this logic is implemented here, but much of it is farmed out to the
|
||||
//! `gcc` crate itself, so we end up having the same fallbacks as there.
|
||||
//! `cc` crate itself, so we end up having the same fallbacks as there.
|
||||
//! Similar logic is then used to find a C++ compiler, just some s/cc/c++/ is
|
||||
//! used.
|
||||
//!
|
||||
@ -35,7 +35,7 @@ use std::process::Command;
|
||||
use std::iter;
|
||||
|
||||
use build_helper::{cc2ar, output};
|
||||
use gcc;
|
||||
use cc;
|
||||
|
||||
use Build;
|
||||
use config::Target;
|
||||
@ -45,15 +45,15 @@ pub fn find(build: &mut Build) {
|
||||
// For all targets we're going to need a C compiler for building some shims
|
||||
// and such as well as for being a linker for Rust code.
|
||||
for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
|
||||
let mut cfg = gcc::Config::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).debug(false)
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
|
||||
.target(&target).host(&build.build);
|
||||
|
||||
let config = build.config.target_config.get(&target);
|
||||
if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
|
||||
cfg.compiler(cc);
|
||||
} else {
|
||||
set_compiler(&mut cfg, "gcc", target, config, build);
|
||||
set_compiler(&mut cfg, Language::C, target, config, build);
|
||||
}
|
||||
|
||||
let compiler = cfg.get_compiler();
|
||||
@ -67,14 +67,14 @@ pub fn find(build: &mut Build) {
|
||||
|
||||
// For all host triples we need to find a C++ compiler as well
|
||||
for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
|
||||
let mut cfg = gcc::Config::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true)
|
||||
let mut cfg = cc::Build::new();
|
||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
|
||||
.target(&host).host(&build.build);
|
||||
let config = build.config.target_config.get(&host);
|
||||
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
||||
cfg.compiler(cxx);
|
||||
} else {
|
||||
set_compiler(&mut cfg, "g++", host, config, build);
|
||||
set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
|
||||
}
|
||||
let compiler = cfg.get_compiler();
|
||||
build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
|
||||
@ -82,8 +82,8 @@ pub fn find(build: &mut Build) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_compiler(cfg: &mut gcc::Config,
|
||||
gnu_compiler: &str,
|
||||
fn set_compiler(cfg: &mut cc::Build,
|
||||
compiler: Language,
|
||||
target: Interned<String>,
|
||||
config: Option<&Target>,
|
||||
build: &Build) {
|
||||
@ -94,7 +94,7 @@ fn set_compiler(cfg: &mut gcc::Config,
|
||||
t if t.contains("android") => {
|
||||
if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
|
||||
let target = target.replace("armv7", "arm");
|
||||
let compiler = format!("{}-{}", target, gnu_compiler);
|
||||
let compiler = format!("{}-{}", target, compiler.clang());
|
||||
cfg.compiler(ndk.join("bin").join(compiler));
|
||||
}
|
||||
}
|
||||
@ -103,6 +103,7 @@ fn set_compiler(cfg: &mut gcc::Config,
|
||||
// which is a gcc version from ports, if this is the case.
|
||||
t if t.contains("openbsd") => {
|
||||
let c = cfg.get_compiler();
|
||||
let gnu_compiler = compiler.gcc();
|
||||
if !c.path().ends_with(gnu_compiler) {
|
||||
return
|
||||
}
|
||||
@ -145,3 +146,29 @@ fn set_compiler(cfg: &mut gcc::Config,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// The target programming language for a native compiler.
|
||||
enum Language {
|
||||
/// The compiler is targeting C.
|
||||
C,
|
||||
/// The compiler is targeting C++.
|
||||
CPlusPlus,
|
||||
}
|
||||
|
||||
impl Language {
|
||||
/// Obtains the name of a compiler in the GCC collection.
|
||||
fn gcc(self) -> &'static str {
|
||||
match self {
|
||||
Language::C => "gcc",
|
||||
Language::CPlusPlus => "g++",
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtains the name of a compiler in the clang suite.
|
||||
fn clang(self) -> &'static str {
|
||||
match self {
|
||||
Language::C => "clang",
|
||||
Language::CPlusPlus => "clang++",
|
||||
}
|
||||
}
|
||||
}
|
@ -24,12 +24,12 @@ use Build;
|
||||
use config::Config;
|
||||
|
||||
// The version number
|
||||
pub const CFG_RELEASE_NUM: &str = "1.21.0";
|
||||
pub const CFG_RELEASE_NUM: &str = "1.22.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 = ".4";
|
||||
pub const CFG_PRERELEASE_VERSION: &str = ".3";
|
||||
|
||||
pub struct GitInfo {
|
||||
inner: Option<Info>,
|
||||
|
@ -23,7 +23,7 @@ use std::path::{PathBuf, Path};
|
||||
use std::process::Command;
|
||||
use std::io::Read;
|
||||
|
||||
use build_helper::{self, output};
|
||||
use build_helper::{self, output, BuildExpectation};
|
||||
|
||||
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
|
||||
use cache::{INTERNER, Interned};
|
||||
@ -33,6 +33,7 @@ use native;
|
||||
use tool::{self, Tool};
|
||||
use util::{self, dylib_path, dylib_path_var};
|
||||
use {Build, Mode};
|
||||
use toolstate::ToolState;
|
||||
|
||||
const ADB_TEST_DIR: &str = "/data/tmp/work";
|
||||
|
||||
@ -64,22 +65,26 @@ impl fmt::Display for TestKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_run(build: &Build, cmd: &mut Command) {
|
||||
fn try_run_expecting(build: &Build, cmd: &mut Command, expect: BuildExpectation) {
|
||||
if !build.fail_fast {
|
||||
if !build.try_run(cmd) {
|
||||
let failures = build.delayed_failures.get();
|
||||
build.delayed_failures.set(failures + 1);
|
||||
if !build.try_run(cmd, expect) {
|
||||
let mut failures = build.delayed_failures.borrow_mut();
|
||||
failures.push(format!("{:?}", cmd));
|
||||
}
|
||||
} else {
|
||||
build.run(cmd);
|
||||
build.run_expecting(cmd, expect);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_run(build: &Build, cmd: &mut Command) {
|
||||
try_run_expecting(build, cmd, BuildExpectation::None)
|
||||
}
|
||||
|
||||
fn try_run_quiet(build: &Build, cmd: &mut Command) {
|
||||
if !build.fail_fast {
|
||||
if !build.try_run_quiet(cmd) {
|
||||
let failures = build.delayed_failures.get();
|
||||
build.delayed_failures.set(failures + 1);
|
||||
let mut failures = build.delayed_failures.borrow_mut();
|
||||
failures.push(format!("{:?}", cmd));
|
||||
}
|
||||
} else {
|
||||
build.run_quiet(cmd);
|
||||
@ -241,15 +246,162 @@ impl Step for Rls {
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
builder.ensure(tool::Rls { compiler, target: self.host });
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
host,
|
||||
"test",
|
||||
"src/tools/rls");
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
cargo.arg("--").args(&build.config.cmd.test_args());
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.rls.passes(ToolState::Testing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Rustfmt {
|
||||
stage: u32,
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Rustfmt {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/tools/rustfmt")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Rustfmt {
|
||||
stage: run.builder.top_stage,
|
||||
host: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs `cargo test` for rustfmt.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let stage = self.stage;
|
||||
let host = self.host;
|
||||
let compiler = builder.compiler(stage, host);
|
||||
|
||||
builder.ensure(tool::Rustfmt { compiler, target: self.host });
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/rls/Cargo.toml"));
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/rustfmt/Cargo.toml"));
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run(build, &mut cargo);
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.rustfmt.passes(ToolState::Testing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Miri {
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Miri {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let test_miri = run.builder.build.config.test_miri;
|
||||
run.path("src/tools/miri").default_condition(test_miri)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Miri {
|
||||
host: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs `cargo test` for miri.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let host = self.host;
|
||||
let compiler = builder.compiler(1, host);
|
||||
|
||||
let miri = builder.ensure(tool::Miri { compiler, target: self.host });
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/miri/Cargo.toml"));
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
// miri tests need to know about the stage sysroot
|
||||
cargo.env("MIRI_SYSROOT", builder.sysroot(compiler));
|
||||
cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler));
|
||||
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
|
||||
cargo.env("MIRI_PATH", miri);
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.miri.passes(ToolState::Testing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Clippy {
|
||||
host: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Clippy {
|
||||
type Output = ();
|
||||
const ONLY_HOSTS: bool = true;
|
||||
const DEFAULT: bool = false;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/tools/clippy")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(Clippy {
|
||||
host: run.target,
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs `cargo test` for clippy.
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let host = self.host;
|
||||
let compiler = builder.compiler(1, host);
|
||||
|
||||
let _clippy = builder.ensure(tool::Clippy { compiler, target: self.host });
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
|
||||
cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml"));
|
||||
|
||||
// Don't build tests dynamically, just a pain to work with
|
||||
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
|
||||
// clippy tests need to know about the stage sysroot
|
||||
cargo.env("SYSROOT", builder.sysroot(compiler));
|
||||
|
||||
builder.add_rustc_lib_path(compiler, &mut cargo);
|
||||
|
||||
try_run_expecting(
|
||||
build,
|
||||
&mut cargo,
|
||||
builder.build.config.toolstate.clippy.passes(ToolState::Testing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -900,7 +1052,6 @@ impl Step for CrateLibrustc {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Crate {
|
||||
compiler: Compiler,
|
||||
@ -1080,6 +1231,75 @@ impl Step for Crate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Rustdoc {
|
||||
host: Interned<String>,
|
||||
test_kind: TestKind,
|
||||
}
|
||||
|
||||
impl Step for Rustdoc {
|
||||
type Output = ();
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/librustdoc").path("src/tools/rustdoc")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
let builder = run.builder;
|
||||
|
||||
let test_kind = if builder.kind == Kind::Test {
|
||||
TestKind::Test
|
||||
} else if builder.kind == Kind::Bench {
|
||||
TestKind::Bench
|
||||
} else {
|
||||
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
|
||||
};
|
||||
|
||||
builder.ensure(Rustdoc {
|
||||
host: run.host,
|
||||
test_kind,
|
||||
});
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder) {
|
||||
let build = builder.build;
|
||||
let test_kind = self.test_kind;
|
||||
|
||||
let compiler = builder.compiler(builder.top_stage, self.host);
|
||||
let target = compiler.host;
|
||||
|
||||
let mut cargo = tool::prepare_tool_cargo(builder,
|
||||
compiler,
|
||||
target,
|
||||
test_kind.subcommand(),
|
||||
"src/tools/rustdoc");
|
||||
let _folder = build.fold_output(|| {
|
||||
format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage)
|
||||
});
|
||||
println!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage,
|
||||
&compiler.host, target);
|
||||
|
||||
if test_kind.subcommand() == "test" && !build.fail_fast {
|
||||
cargo.arg("--no-fail-fast");
|
||||
}
|
||||
|
||||
cargo.arg("-p").arg("rustdoc:0.0.0");
|
||||
|
||||
cargo.arg("--");
|
||||
cargo.args(&build.config.cmd.test_args());
|
||||
|
||||
if build.config.quiet_tests {
|
||||
cargo.arg("--quiet");
|
||||
}
|
||||
|
||||
let _time = util::timeit();
|
||||
|
||||
try_run(build, &mut cargo);
|
||||
}
|
||||
}
|
||||
|
||||
fn envify(s: &str) -> String {
|
||||
s.chars().map(|c| {
|
||||
match c {
|
||||
|
@ -13,7 +13,7 @@
|
||||
//! Responsible for cleaning out a build directory of all old and stale
|
||||
//! artifacts to prepare for a fresh build. Currently doesn't remove the
|
||||
//! `build/cache` directory (download cache) or the `build/$target/llvm`
|
||||
//! directory as we want that cached between builds.
|
||||
//! directory unless the --all flag is present.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{self, ErrorKind};
|
||||
@ -21,24 +21,29 @@ use std::path::Path;
|
||||
|
||||
use Build;
|
||||
|
||||
pub fn clean(build: &Build) {
|
||||
pub fn clean(build: &Build, all: bool) {
|
||||
rm_rf("tmp".as_ref());
|
||||
rm_rf(&build.out.join("tmp"));
|
||||
rm_rf(&build.out.join("dist"));
|
||||
|
||||
for host in &build.hosts {
|
||||
let entries = match build.out.join(host).read_dir() {
|
||||
Ok(iter) => iter,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if all {
|
||||
rm_rf(&build.out);
|
||||
} else {
|
||||
rm_rf(&build.out.join("tmp"));
|
||||
rm_rf(&build.out.join("dist"));
|
||||
|
||||
for entry in entries {
|
||||
let entry = t!(entry);
|
||||
if entry.file_name().to_str() == Some("llvm") {
|
||||
continue
|
||||
for host in &build.hosts {
|
||||
let entries = match build.out.join(host).read_dir() {
|
||||
Ok(iter) => iter,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
for entry in entries {
|
||||
let entry = t!(entry);
|
||||
if entry.file_name().to_str() == Some("llvm") {
|
||||
continue
|
||||
}
|
||||
let path = t!(entry.path().canonicalize());
|
||||
rm_rf(&path);
|
||||
}
|
||||
let path = t!(entry.path().canonicalize());
|
||||
rm_rf(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,12 @@
|
||||
|
||||
//! Serialized configuration of a build.
|
||||
//!
|
||||
//! This module implements parsing `config.mk` and `config.toml` configuration
|
||||
//! files to tweak how the build runs.
|
||||
//! This module implements parsing `config.toml` configuration files to tweak
|
||||
//! how the build runs.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
@ -23,10 +23,11 @@ use std::cmp;
|
||||
|
||||
use num_cpus;
|
||||
use toml;
|
||||
use util::{exe, push_exe_path};
|
||||
use util::exe;
|
||||
use cache::{INTERNER, Interned};
|
||||
use flags::Flags;
|
||||
pub use flags::Subcommand;
|
||||
use toolstate::ToolStates;
|
||||
|
||||
/// Global configuration for the entire build and/or bootstrap.
|
||||
///
|
||||
@ -111,6 +112,7 @@ pub struct Config {
|
||||
pub low_priority: bool,
|
||||
pub channel: String,
|
||||
pub quiet_tests: bool,
|
||||
pub test_miri: bool,
|
||||
// Fallback musl-root for all targets
|
||||
pub musl_root: Option<PathBuf>,
|
||||
pub prefix: Option<PathBuf>,
|
||||
@ -124,14 +126,14 @@ pub struct Config {
|
||||
pub nodejs: Option<PathBuf>,
|
||||
pub gdb: Option<PathBuf>,
|
||||
pub python: Option<PathBuf>,
|
||||
pub configure_args: Vec<String>,
|
||||
pub openssl_static: bool,
|
||||
|
||||
pub configure_args: Vec<String>,
|
||||
|
||||
// These are either the stage0 downloaded binaries or the locally installed ones.
|
||||
pub initial_cargo: PathBuf,
|
||||
pub initial_rustc: PathBuf,
|
||||
|
||||
pub toolstate: ToolStates,
|
||||
}
|
||||
|
||||
/// Per-target configuration stored in the global configuration structure.
|
||||
@ -190,6 +192,8 @@ struct Build {
|
||||
sanitizers: Option<bool>,
|
||||
profiler: Option<bool>,
|
||||
openssl_static: Option<bool>,
|
||||
configure_args: Option<Vec<String>>,
|
||||
local_rebuild: Option<bool>,
|
||||
}
|
||||
|
||||
/// TOML representation of various global install decisions.
|
||||
@ -219,6 +223,7 @@ struct Llvm {
|
||||
targets: Option<String>,
|
||||
experimental_targets: Option<String>,
|
||||
link_jobs: Option<u32>,
|
||||
link_shared: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
@ -265,6 +270,10 @@ struct Rust {
|
||||
debuginfo_tests: Option<bool>,
|
||||
codegen_tests: Option<bool>,
|
||||
ignore_git: Option<bool>,
|
||||
debug: Option<bool>,
|
||||
dist_src: Option<bool>,
|
||||
quiet_tests: Option<bool>,
|
||||
test_miri: Option<bool>,
|
||||
}
|
||||
|
||||
/// TOML representation of how each build target is configured.
|
||||
@ -300,6 +309,7 @@ impl Config {
|
||||
config.codegen_tests = true;
|
||||
config.ignore_git = false;
|
||||
config.rust_dist_src = true;
|
||||
config.test_miri = false;
|
||||
|
||||
config.on_fail = flags.on_fail;
|
||||
config.stage = flags.stage;
|
||||
@ -326,6 +336,18 @@ impl Config {
|
||||
}
|
||||
}).unwrap_or_else(|| TomlConfig::default());
|
||||
|
||||
let toolstate_toml_path = config.src.join("src/tools/toolstate.toml");
|
||||
let parse_toolstate = || -> Result<_, Box<::std::error::Error>> {
|
||||
let mut f = File::open(toolstate_toml_path)?;
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
Ok(toml::from_str(&contents)?)
|
||||
};
|
||||
config.toolstate = parse_toolstate().unwrap_or_else(|err| {
|
||||
println!("failed to parse TOML configuration 'toolstate.toml': {}", err);
|
||||
process::exit(2);
|
||||
});
|
||||
|
||||
let build = toml.build.clone().unwrap_or(Build::default());
|
||||
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
|
||||
set(&mut config.build, flags.build);
|
||||
@ -374,6 +396,8 @@ impl Config {
|
||||
set(&mut config.sanitizers, build.sanitizers);
|
||||
set(&mut config.profiler, build.profiler);
|
||||
set(&mut config.openssl_static, build.openssl_static);
|
||||
set(&mut config.configure_args, build.configure_args);
|
||||
set(&mut config.local_rebuild, build.local_rebuild);
|
||||
config.verbose = cmp::max(config.verbose, flags.verbose);
|
||||
|
||||
if let Some(ref install) = toml.install {
|
||||
@ -385,6 +409,18 @@ impl Config {
|
||||
config.mandir = install.mandir.clone().map(PathBuf::from);
|
||||
}
|
||||
|
||||
// Store off these values as options because if they're not provided
|
||||
// we'll infer default values for them later
|
||||
let mut llvm_assertions = None;
|
||||
let mut debuginfo_lines = None;
|
||||
let mut debuginfo_only_std = None;
|
||||
let mut debug = None;
|
||||
let mut debug_jemalloc = None;
|
||||
let mut debuginfo = None;
|
||||
let mut debug_assertions = None;
|
||||
let mut optimize = None;
|
||||
let mut ignore_git = None;
|
||||
|
||||
if let Some(ref llvm) = toml.llvm {
|
||||
match llvm.ccache {
|
||||
Some(StringOrBool::String(ref s)) => {
|
||||
@ -397,31 +433,36 @@ impl Config {
|
||||
}
|
||||
set(&mut config.ninja, llvm.ninja);
|
||||
set(&mut config.llvm_enabled, llvm.enabled);
|
||||
set(&mut config.llvm_assertions, llvm.assertions);
|
||||
llvm_assertions = llvm.assertions;
|
||||
set(&mut config.llvm_optimize, llvm.optimize);
|
||||
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
|
||||
set(&mut config.llvm_version_check, llvm.version_check);
|
||||
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
|
||||
set(&mut config.llvm_link_shared, llvm.link_shared);
|
||||
config.llvm_targets = llvm.targets.clone();
|
||||
config.llvm_experimental_targets = llvm.experimental_targets.clone();
|
||||
config.llvm_link_jobs = llvm.link_jobs;
|
||||
}
|
||||
|
||||
if let Some(ref rust) = toml.rust {
|
||||
set(&mut config.rust_debug_assertions, rust.debug_assertions);
|
||||
set(&mut config.rust_debuginfo, rust.debuginfo);
|
||||
set(&mut config.rust_debuginfo_lines, rust.debuginfo_lines);
|
||||
set(&mut config.rust_debuginfo_only_std, rust.debuginfo_only_std);
|
||||
set(&mut config.rust_optimize, rust.optimize);
|
||||
debug = rust.debug;
|
||||
debug_assertions = rust.debug_assertions;
|
||||
debuginfo = rust.debuginfo;
|
||||
debuginfo_lines = rust.debuginfo_lines;
|
||||
debuginfo_only_std = rust.debuginfo_only_std;
|
||||
optimize = rust.optimize;
|
||||
ignore_git = rust.ignore_git;
|
||||
debug_jemalloc = rust.debug_jemalloc;
|
||||
set(&mut config.rust_optimize_tests, rust.optimize_tests);
|
||||
set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
|
||||
set(&mut config.codegen_tests, rust.codegen_tests);
|
||||
set(&mut config.rust_rpath, rust.rpath);
|
||||
set(&mut config.debug_jemalloc, rust.debug_jemalloc);
|
||||
set(&mut config.use_jemalloc, rust.use_jemalloc);
|
||||
set(&mut config.backtrace, rust.backtrace);
|
||||
set(&mut config.channel, rust.channel.clone());
|
||||
set(&mut config.ignore_git, rust.ignore_git);
|
||||
set(&mut config.rust_dist_src, rust.dist_src);
|
||||
set(&mut config.quiet_tests, rust.quiet_tests);
|
||||
set(&mut config.test_miri, rust.test_miri);
|
||||
config.rustc_default_linker = rust.default_linker.clone();
|
||||
config.rustc_default_ar = rust.default_ar.clone();
|
||||
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
|
||||
@ -476,226 +517,31 @@ impl Config {
|
||||
None => stage0_root.join(exe("cargo", &config.build)),
|
||||
};
|
||||
|
||||
// compat with `./configure` while we're still using that
|
||||
if fs::metadata("config.mk").is_ok() {
|
||||
config.update_with_config_mk();
|
||||
}
|
||||
// Now that we've reached the end of our configuration, infer the
|
||||
// default values for all options that we haven't otherwise stored yet.
|
||||
|
||||
let default = config.channel == "nightly";
|
||||
config.llvm_assertions = llvm_assertions.unwrap_or(default);
|
||||
|
||||
let default = match &config.channel[..] {
|
||||
"stable" | "beta" | "nightly" => true,
|
||||
_ => false,
|
||||
};
|
||||
config.rust_debuginfo_lines = debuginfo_lines.unwrap_or(default);
|
||||
config.rust_debuginfo_only_std = debuginfo_only_std.unwrap_or(default);
|
||||
|
||||
let default = debug == Some(true);
|
||||
config.debug_jemalloc = debug_jemalloc.unwrap_or(default);
|
||||
config.rust_debuginfo = debuginfo.unwrap_or(default);
|
||||
config.rust_debug_assertions = debug_assertions.unwrap_or(default);
|
||||
config.rust_optimize = optimize.unwrap_or(!default);
|
||||
|
||||
let default = config.channel == "dev";
|
||||
config.ignore_git = ignore_git.unwrap_or(default);
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
/// "Temporary" routine to parse `config.mk` into this configuration.
|
||||
///
|
||||
/// While we still have `./configure` this implements the ability to decode
|
||||
/// that configuration into this. This isn't exactly a full-blown makefile
|
||||
/// parser, but hey it gets the job done!
|
||||
fn update_with_config_mk(&mut self) {
|
||||
let mut config = String::new();
|
||||
File::open("config.mk").unwrap().read_to_string(&mut config).unwrap();
|
||||
for line in config.lines() {
|
||||
let mut parts = line.splitn(2, ":=").map(|s| s.trim());
|
||||
let key = parts.next().unwrap();
|
||||
let value = match parts.next() {
|
||||
Some(n) if n.starts_with('\"') => &n[1..n.len() - 1],
|
||||
Some(n) => n,
|
||||
None => continue
|
||||
};
|
||||
|
||||
macro_rules! check {
|
||||
($(($name:expr, $val:expr),)*) => {
|
||||
if value == "1" {
|
||||
$(
|
||||
if key == concat!("CFG_ENABLE_", $name) {
|
||||
$val = true;
|
||||
continue
|
||||
}
|
||||
if key == concat!("CFG_DISABLE_", $name) {
|
||||
$val = false;
|
||||
continue
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check! {
|
||||
("MANAGE_SUBMODULES", self.submodules),
|
||||
("COMPILER_DOCS", self.compiler_docs),
|
||||
("DOCS", self.docs),
|
||||
("LLVM_ASSERTIONS", self.llvm_assertions),
|
||||
("LLVM_RELEASE_DEBUGINFO", self.llvm_release_debuginfo),
|
||||
("OPTIMIZE_LLVM", self.llvm_optimize),
|
||||
("LLVM_VERSION_CHECK", self.llvm_version_check),
|
||||
("LLVM_STATIC_STDCPP", self.llvm_static_stdcpp),
|
||||
("LLVM_LINK_SHARED", self.llvm_link_shared),
|
||||
("OPTIMIZE", self.rust_optimize),
|
||||
("DEBUG_ASSERTIONS", self.rust_debug_assertions),
|
||||
("DEBUGINFO", self.rust_debuginfo),
|
||||
("DEBUGINFO_LINES", self.rust_debuginfo_lines),
|
||||
("DEBUGINFO_ONLY_STD", self.rust_debuginfo_only_std),
|
||||
("JEMALLOC", self.use_jemalloc),
|
||||
("DEBUG_JEMALLOC", self.debug_jemalloc),
|
||||
("RPATH", self.rust_rpath),
|
||||
("OPTIMIZE_TESTS", self.rust_optimize_tests),
|
||||
("DEBUGINFO_TESTS", self.rust_debuginfo_tests),
|
||||
("QUIET_TESTS", self.quiet_tests),
|
||||
("LOCAL_REBUILD", self.local_rebuild),
|
||||
("NINJA", self.ninja),
|
||||
("CODEGEN_TESTS", self.codegen_tests),
|
||||
("LOCKED_DEPS", self.locked_deps),
|
||||
("VENDOR", self.vendor),
|
||||
("FULL_BOOTSTRAP", self.full_bootstrap),
|
||||
("EXTENDED", self.extended),
|
||||
("SANITIZERS", self.sanitizers),
|
||||
("PROFILER", self.profiler),
|
||||
("DIST_SRC", self.rust_dist_src),
|
||||
("CARGO_OPENSSL_STATIC", self.openssl_static),
|
||||
}
|
||||
|
||||
match key {
|
||||
"CFG_BUILD" if value.len() > 0 => self.build = INTERNER.intern_str(value),
|
||||
"CFG_HOST" if value.len() > 0 => {
|
||||
self.hosts.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
|
||||
|
||||
}
|
||||
"CFG_TARGET" if value.len() > 0 => {
|
||||
self.targets.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
|
||||
}
|
||||
"CFG_EXPERIMENTAL_TARGETS" if value.len() > 0 => {
|
||||
self.llvm_experimental_targets = Some(value.to_string());
|
||||
}
|
||||
"CFG_MUSL_ROOT" if value.len() > 0 => {
|
||||
self.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_X86_64" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("x86_64-unknown-linux-musl");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_I686" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("i686-unknown-linux-musl");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARM" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-musleabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARMHF" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-musleabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_MUSL_ROOT_ARMV7" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("armv7-unknown-linux-musleabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.musl_root = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_DEFAULT_AR" if value.len() > 0 => {
|
||||
self.rustc_default_ar = Some(value.to_string());
|
||||
}
|
||||
"CFG_DEFAULT_LINKER" if value.len() > 0 => {
|
||||
self.rustc_default_linker = Some(value.to_string());
|
||||
}
|
||||
"CFG_GDB" if value.len() > 0 => {
|
||||
self.gdb = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_RELEASE_CHANNEL" => {
|
||||
self.channel = value.to_string();
|
||||
}
|
||||
"CFG_PREFIX" => {
|
||||
self.prefix = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_SYSCONFDIR" => {
|
||||
self.sysconfdir = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_DOCDIR" => {
|
||||
self.docdir = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_BINDIR" => {
|
||||
self.bindir = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_LIBDIR" => {
|
||||
self.libdir = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_LIBDIR_RELATIVE" => {
|
||||
self.libdir_relative = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_MANDIR" => {
|
||||
self.mandir = Some(PathBuf::from(value));
|
||||
}
|
||||
"CFG_LLVM_ROOT" if value.len() > 0 => {
|
||||
let target = self.target_config.entry(self.build.clone())
|
||||
.or_insert(Target::default());
|
||||
let root = parse_configure_path(value);
|
||||
target.llvm_config = Some(push_exe_path(root, &["bin", "llvm-config"]));
|
||||
}
|
||||
"CFG_JEMALLOC_ROOT" if value.len() > 0 => {
|
||||
let target = self.target_config.entry(self.build.clone())
|
||||
.or_insert(Target::default());
|
||||
target.jemalloc = Some(parse_configure_path(value).join("libjemalloc_pic.a"));
|
||||
}
|
||||
"CFG_ARM_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("arm-linux-androideabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_ARMV7_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("armv7-linux-androideabi");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_I686_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("i686-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_AARCH64_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("aarch64-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_X86_64_LINUX_ANDROID_NDK" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("x86_64-linux-android");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.ndk = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_LOCAL_RUST_ROOT" if value.len() > 0 => {
|
||||
let path = parse_configure_path(value);
|
||||
self.initial_rustc = push_exe_path(path.clone(), &["bin", "rustc"]);
|
||||
self.initial_cargo = push_exe_path(path, &["bin", "cargo"]);
|
||||
}
|
||||
"CFG_PYTHON" if value.len() > 0 => {
|
||||
let path = parse_configure_path(value);
|
||||
self.python = Some(path);
|
||||
}
|
||||
"CFG_ENABLE_CCACHE" if value == "1" => {
|
||||
self.ccache = Some(exe("ccache", &self.build));
|
||||
}
|
||||
"CFG_ENABLE_SCCACHE" if value == "1" => {
|
||||
self.ccache = Some(exe("sccache", &self.build));
|
||||
}
|
||||
"CFG_CONFIGURE_ARGS" if value.len() > 0 => {
|
||||
self.configure_args = value.split_whitespace()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
}
|
||||
"CFG_QEMU_ARMHF_ROOTFS" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("arm-unknown-linux-gnueabihf");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.qemu_rootfs = Some(parse_configure_path(value));
|
||||
}
|
||||
"CFG_QEMU_AARCH64_ROOTFS" if value.len() > 0 => {
|
||||
let target = INTERNER.intern_str("aarch64-unknown-linux-gnu");
|
||||
let target = self.target_config.entry(target).or_insert(Target::default());
|
||||
target.qemu_rootfs = Some(parse_configure_path(value));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose(&self) -> bool {
|
||||
self.verbose > 0
|
||||
}
|
||||
@ -705,30 +551,6 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn parse_configure_path(path: &str) -> PathBuf {
|
||||
path.into()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn parse_configure_path(path: &str) -> PathBuf {
|
||||
// on windows, configure produces unix style paths e.g. /c/some/path but we
|
||||
// only want real windows paths
|
||||
|
||||
use std::process::Command;
|
||||
use build_helper;
|
||||
|
||||
// '/' is invalid in windows paths, so we can detect unix paths by the presence of it
|
||||
if !path.contains('/') {
|
||||
return path.into();
|
||||
}
|
||||
|
||||
let win_path = build_helper::output(Command::new("cygpath").arg("-w").arg(path));
|
||||
let win_path = win_path.trim();
|
||||
|
||||
win_path.into()
|
||||
}
|
||||
|
||||
fn set<T>(field: &mut T, val: Option<T>) {
|
||||
if let Some(v) = val {
|
||||
*field = v;
|
||||
|
417
src/bootstrap/configure.py
Executable file
417
src/bootstrap/configure.py
Executable file
@ -0,0 +1,417 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
# ignore-tidy-linelength
|
||||
|
||||
import sys
|
||||
import os
|
||||
rust_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
rust_dir = os.path.dirname(rust_dir)
|
||||
rust_dir = os.path.dirname(rust_dir)
|
||||
sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))
|
||||
import bootstrap
|
||||
|
||||
class Option:
|
||||
def __init__(self, name, rustbuild, desc, value):
|
||||
self.name = name
|
||||
self.rustbuild = rustbuild
|
||||
self.desc = desc
|
||||
self.value = value
|
||||
|
||||
options = []
|
||||
|
||||
def o(*args):
|
||||
options.append(Option(*args, value=False))
|
||||
|
||||
def v(*args):
|
||||
options.append(Option(*args, value=True))
|
||||
|
||||
o("debug", "rust.debug", "debug mode; disables optimization unless `--enable-optimize` given")
|
||||
o("docs", "build.docs", "build standard library documentation")
|
||||
o("compiler-docs", "build.compiler-docs", "build compiler documentation")
|
||||
o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
|
||||
o("test-miri", "rust.test-miri", "run miri's test suite")
|
||||
o("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
|
||||
o("quiet-tests", "rust.quiet-tests", "enable quieter output when running tests")
|
||||
o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
|
||||
o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
|
||||
o("local-rust", None, "use an installed rustc rather than downloading a snapshot")
|
||||
v("local-rust-root", None, "set prefix for local rust binary")
|
||||
o("local-rebuild", "build.local-rebuild", "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version")
|
||||
o("llvm-static-stdcpp", "llvm.static-libstdcpp", "statically link to libstdc++ for LLVM")
|
||||
o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)")
|
||||
o("rpath", "rust.rpath", "build rpaths into rustc itself")
|
||||
o("llvm-version-check", "llvm.version-check", "check if the LLVM version is supported, build anyway")
|
||||
o("codegen-tests", "rust.codegen-tests", "run the src/test/codegen tests")
|
||||
o("option-checking", None, "complain about unrecognized options in this configure script")
|
||||
o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)")
|
||||
o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")
|
||||
o("vendor", "build.vendor", "enable usage of vendored Rust crates")
|
||||
o("sanitizers", "build.sanitizers", "build the sanitizer runtimes (asan, lsan, msan, tsan)")
|
||||
o("dist-src", "rust.dist-src", "when building tarballs enables building a source tarball")
|
||||
o("cargo-openssl-static", "build.openssl-static", "static openssl in cargo")
|
||||
o("profiler", "build.profiler", "build the profiler runtime")
|
||||
|
||||
# Optimization and debugging options. These may be overridden by the release
|
||||
# channel, etc.
|
||||
o("optimize", "rust.optimize", "build optimized rust code")
|
||||
o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
|
||||
o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
|
||||
o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
|
||||
o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata")
|
||||
o("debuginfo", "rust.debuginfo", "build with debugger metadata")
|
||||
o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
|
||||
o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
|
||||
o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill")
|
||||
|
||||
v("prefix", "install.prefix", "set installation prefix")
|
||||
v("localstatedir", "install.localstatedir", "local state directory")
|
||||
v("datadir", "install.datadir", "install data")
|
||||
v("sysconfdir", "install.sysconfdir", "install system configuration files")
|
||||
v("infodir", "install.infodir", "install additional info")
|
||||
v("libdir", "install.libdir", "install libraries")
|
||||
v("mandir", "install.mandir", "install man pages in PATH")
|
||||
v("docdir", "install.docdir", "install documentation in PATH")
|
||||
v("bindir", "install.bindir", "install binaries")
|
||||
|
||||
v("llvm-root", None, "set LLVM root")
|
||||
v("python", "build.python", "set path to python")
|
||||
v("jemalloc-root", None, "set directory where libjemalloc_pic.a is located")
|
||||
v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
|
||||
"Android NDK standalone path (deprecated)")
|
||||
v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
|
||||
"i686-linux-android NDK standalone path")
|
||||
v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk",
|
||||
"arm-linux-androideabi NDK standalone path")
|
||||
v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk",
|
||||
"armv7-linux-androideabi NDK standalone path")
|
||||
v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk",
|
||||
"aarch64-linux-android NDK standalone path")
|
||||
v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk",
|
||||
"x86_64-linux-android NDK standalone path")
|
||||
v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
|
||||
"MUSL root installation directory (deprecated)")
|
||||
v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
|
||||
"x86_64-unknown-linux-musl install directory")
|
||||
v("musl-root-i686", "target.i686-unknown-linux-musl.musl-root",
|
||||
"i686-unknown-linux-musl install directory")
|
||||
v("musl-root-arm", "target.arm-unknown-linux-musleabi.musl-root",
|
||||
"arm-unknown-linux-musleabi install directory")
|
||||
v("musl-root-armhf", "target.arm-unknown-linux-musleabihf.musl-root",
|
||||
"arm-unknown-linux-musleabihf install directory")
|
||||
v("musl-root-armv7", "target.armv7-unknown-linux-musleabihf.musl-root",
|
||||
"armv7-unknown-linux-musleabihf install directory")
|
||||
v("musl-root-aarch64", "target.aarch64-unknown-linux-musl.musl-root",
|
||||
"aarch64-unknown-linux-musl install directory")
|
||||
v("qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs",
|
||||
"rootfs in qemu testing, you probably don't want to use this")
|
||||
v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs",
|
||||
"rootfs in qemu testing, you probably don't want to use this")
|
||||
v("experimental-targets", "llvm.experimental-targets",
|
||||
"experimental LLVM targets to build")
|
||||
v("release-channel", "rust.channel", "the name of the release channel to build")
|
||||
|
||||
# Used on systems where "cc" and "ar" are unavailable
|
||||
v("default-linker", "rust.default-linker", "the default linker")
|
||||
v("default-ar", "rust.default-ar", "the default ar")
|
||||
|
||||
# Many of these are saved below during the "writing configuration" step
|
||||
# (others are conditionally saved).
|
||||
o("manage-submodules", "build.submodules", "let the build manage the git submodules")
|
||||
o("jemalloc", "rust.use-jemalloc", "build liballoc with jemalloc")
|
||||
o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
|
||||
o("extended", "build.extended", "build an extended rust tool set")
|
||||
|
||||
v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
|
||||
v("host", None, "GNUs ./configure syntax LLVM host triples")
|
||||
v("target", None, "GNUs ./configure syntax LLVM target triples")
|
||||
|
||||
v("set", None, "set arbitrary key/value pairs in TOML configuration")
|
||||
|
||||
def p(msg):
|
||||
print("configure: " + msg)
|
||||
|
||||
def err(msg):
|
||||
print("configure: error: " + msg)
|
||||
sys.exit(1)
|
||||
|
||||
if '--help' in sys.argv or '-h' in sys.argv:
|
||||
print('Usage: ./configure [options]')
|
||||
print('')
|
||||
print('Options')
|
||||
for option in options:
|
||||
if 'android' in option.name:
|
||||
# no one needs to know about these obscure options
|
||||
continue
|
||||
if option.value:
|
||||
print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc))
|
||||
else:
|
||||
print('\t{:30} {}'.format('--enable-{}'.format(option.name), option.desc))
|
||||
print('')
|
||||
print('This configure script is a thin configuration shim over the true')
|
||||
print('configuration system, `config.toml`. You can explore the comments')
|
||||
print('in `config.toml.example` next to this configure script to see')
|
||||
print('more information about what each option is. Additionally you can')
|
||||
print('pass `--set` as an argument to set arbitrary key/value pairs')
|
||||
print('in the TOML configuration if desired')
|
||||
print('')
|
||||
print('Also note that all options which take `--enable` can similarly')
|
||||
print('be passed with `--disable-foo` to forcibly disable the option')
|
||||
sys.exit(0)
|
||||
|
||||
# Parse all command line arguments into one of these three lists, handling
|
||||
# boolean and value-based options separately
|
||||
unknown_args = []
|
||||
need_value_args = []
|
||||
known_args = {}
|
||||
|
||||
p("processing command line")
|
||||
i = 1
|
||||
while i < len(sys.argv):
|
||||
arg = sys.argv[i]
|
||||
i += 1
|
||||
if not arg.startswith('--'):
|
||||
unknown_args.append(arg)
|
||||
continue
|
||||
|
||||
found = False
|
||||
for option in options:
|
||||
value = None
|
||||
if option.value:
|
||||
keyval = arg[2:].split('=', 1)
|
||||
key = keyval[0]
|
||||
if option.name != key:
|
||||
continue
|
||||
|
||||
if len(keyval) > 1:
|
||||
value = keyval[1]
|
||||
elif i < len(sys.argv):
|
||||
value = sys.argv[i]
|
||||
i += 1
|
||||
else:
|
||||
need_value_args.append(arg)
|
||||
continue
|
||||
else:
|
||||
if arg[2:] == 'enable-' + option.name:
|
||||
value = True
|
||||
elif arg[2:] == 'disable-' + option.name:
|
||||
value = False
|
||||
else:
|
||||
continue
|
||||
|
||||
found = True
|
||||
if not option.name in known_args:
|
||||
known_args[option.name] = []
|
||||
known_args[option.name].append((option, value))
|
||||
break
|
||||
|
||||
if not found:
|
||||
unknown_args.append(arg)
|
||||
p("")
|
||||
|
||||
if 'option-checking' not in known_args or known_args['option-checking'][1]:
|
||||
if len(unknown_args) > 0:
|
||||
err("Option '" + unknown_args[0] + "' is not recognized")
|
||||
if len(need_value_args) > 0:
|
||||
err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
|
||||
|
||||
# Parse all known arguments into a configuration structure that reflects the
|
||||
# TOML we're going to write out
|
||||
config = {}
|
||||
|
||||
def build():
|
||||
if 'build' in known_args:
|
||||
return known_args['build'][0][1]
|
||||
return bootstrap.default_build_triple()
|
||||
|
||||
def set(key, value):
|
||||
s = "{:20} := {}".format(key, value)
|
||||
if len(s) < 70:
|
||||
p(s)
|
||||
else:
|
||||
p(s[:70] + " ...")
|
||||
|
||||
arr = config
|
||||
parts = key.split('.')
|
||||
for i, part in enumerate(parts):
|
||||
if i == len(parts) - 1:
|
||||
arr[part] = value
|
||||
else:
|
||||
if not part in arr:
|
||||
arr[part] = {}
|
||||
arr = arr[part]
|
||||
|
||||
for key in known_args:
|
||||
# The `set` option is special and can be passed a bunch of times
|
||||
if key == 'set':
|
||||
for option, value in known_args[key]:
|
||||
keyval = value.split('=', 1)
|
||||
if len(keyval) == 1 or keyval[1] == "true":
|
||||
value = True
|
||||
elif keyval[1] == "false":
|
||||
value = False
|
||||
else:
|
||||
value = keyval[1]
|
||||
set(keyval[0], value)
|
||||
continue
|
||||
|
||||
# Ensure each option is only passed once
|
||||
arr = known_args[key]
|
||||
if len(arr) > 1:
|
||||
err("Option '{}' provided more than once".format(key))
|
||||
option, value = arr[0]
|
||||
|
||||
# If we have a clear avenue to set our value in rustbuild, do so
|
||||
if option.rustbuild is not None:
|
||||
set(option.rustbuild, value)
|
||||
continue
|
||||
|
||||
# Otherwise we're a "special" option and need some extra handling, so do
|
||||
# that here.
|
||||
if option.name == 'sccache':
|
||||
set('llvm.ccache', 'sccache')
|
||||
elif option.name == 'local-rust':
|
||||
for path in os.environ['PATH'].split(os.pathsep):
|
||||
if os.path.exists(path + '/rustc'):
|
||||
set('build.rustc', path + '/rustc')
|
||||
break
|
||||
for path in os.environ['PATH'].split(os.pathsep):
|
||||
if os.path.exists(path + '/cargo'):
|
||||
set('build.cargo', path + '/cargo')
|
||||
break
|
||||
elif option.name == 'local-rust-root':
|
||||
set('build.rustc', value + '/bin/rustc')
|
||||
set('build.cargo', value + '/bin/cargo')
|
||||
elif option.name == 'llvm-root':
|
||||
set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
|
||||
elif option.name == 'jemalloc-root':
|
||||
set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
|
||||
elif option.name == 'host':
|
||||
set('build.host', value.split(','))
|
||||
elif option.name == 'target':
|
||||
set('build.target', value.split(','))
|
||||
elif option.name == 'option-checking':
|
||||
# this was handled above
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError("unhandled option {}".format(option.name))
|
||||
|
||||
set('build.configure-args', sys.argv[1:])
|
||||
|
||||
# "Parse" the `config.toml.example` file into the various sections, and we'll
|
||||
# use this as a template of a `config.toml` to write out which preserves
|
||||
# all the various comments and whatnot.
|
||||
#
|
||||
# Note that the `target` section is handled separately as we'll duplicate it
|
||||
# per configure dtarget, so there's a bit of special handling for that here.
|
||||
sections = {}
|
||||
cur_section = None
|
||||
sections[None] = []
|
||||
section_order = [None]
|
||||
targets = {}
|
||||
|
||||
for line in open(rust_dir + '/config.toml.example').read().split("\n"):
|
||||
if line.startswith('['):
|
||||
cur_section = line[1:-1]
|
||||
if cur_section.startswith('target'):
|
||||
cur_section = 'target'
|
||||
elif '.' in cur_section:
|
||||
raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
|
||||
sections[cur_section] = [line]
|
||||
section_order.append(cur_section)
|
||||
else:
|
||||
sections[cur_section].append(line)
|
||||
|
||||
# Fill out the `targets` array by giving all configured targets a copy of the
|
||||
# `target` section we just loaded from the example config
|
||||
configured_targets = [build()]
|
||||
if 'build' in config:
|
||||
if 'host' in config['build']:
|
||||
configured_targets += config['build']['host']
|
||||
if 'target' in config['build']:
|
||||
configured_targets += config['build']['target']
|
||||
if 'target' in config:
|
||||
for target in config['target']:
|
||||
configured_targets.append(target)
|
||||
for target in configured_targets:
|
||||
targets[target] = sections['target'][:]
|
||||
targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", target)
|
||||
|
||||
# Here we walk through the constructed configuration we have from the parsed
|
||||
# command line arguemnts. We then apply each piece of configuration by
|
||||
# basically just doing a `sed` to change the various configuration line to what
|
||||
# we've got configure.
|
||||
def to_toml(value):
|
||||
if isinstance(value, bool):
|
||||
if value:
|
||||
return "true"
|
||||
else:
|
||||
return "false"
|
||||
elif isinstance(value, list):
|
||||
return '[' + ', '.join(map(to_toml, value)) + ']'
|
||||
elif isinstance(value, str):
|
||||
return "'" + value + "'"
|
||||
else:
|
||||
raise 'no toml'
|
||||
|
||||
def configure_section(lines, config):
|
||||
for key in config:
|
||||
value = config[key]
|
||||
found = False
|
||||
for i, line in enumerate(lines):
|
||||
if not line.startswith('#' + key + ' = '):
|
||||
continue
|
||||
found = True
|
||||
lines[i] = "{} = {}".format(key, to_toml(value))
|
||||
break
|
||||
if not found:
|
||||
raise RuntimeError("failed to find config line for {}".format(key))
|
||||
|
||||
for section_key in config:
|
||||
section_config = config[section_key]
|
||||
if not section_key in sections:
|
||||
raise RuntimeError("config key {} not in sections".format(key))
|
||||
|
||||
if section_key == 'target':
|
||||
for target in section_config:
|
||||
configure_section(targets[target], section_config[target])
|
||||
else:
|
||||
configure_section(sections[section_key], section_config)
|
||||
|
||||
# Now that we've built up our `config.toml`, write it all out in the same
|
||||
# order that we read it in.
|
||||
p("")
|
||||
p("writing `config.toml` in current directory")
|
||||
with open('config.toml', 'w') as f:
|
||||
for section in section_order:
|
||||
if section == 'target':
|
||||
for target in targets:
|
||||
for line in targets[target]:
|
||||
f.write(line + "\n")
|
||||
else:
|
||||
for line in sections[section]:
|
||||
f.write(line + "\n")
|
||||
|
||||
with open('Makefile', 'w') as f:
|
||||
contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
|
||||
contents = open(contents).read()
|
||||
contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
|
||||
contents = contents.replace("$(CFG_PYTHON)", sys.executable)
|
||||
f.write(contents)
|
||||
|
||||
# Finally, clean up with a bit of a help message
|
||||
relpath = os.path.dirname(__file__)
|
||||
if relpath == '':
|
||||
relpath = '.'
|
||||
|
||||
p("")
|
||||
p("run `python {}/x.py --help`".format(relpath))
|
||||
p("")
|
@ -20,7 +20,7 @@
|
||||
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
@ -365,6 +365,9 @@ impl Step for Rustc {
|
||||
// tiny morsel of metadata is used by rust-packaging
|
||||
let version = build.rust_version();
|
||||
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
|
||||
if let Some(sha) = build.rust_sha() {
|
||||
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
|
||||
}
|
||||
|
||||
// On MinGW we've got a few runtime DLL dependencies that we need to
|
||||
// include. The first argument to this script is where to put these DLLs
|
||||
@ -429,7 +432,7 @@ impl Step for Rustc {
|
||||
|
||||
// Man pages
|
||||
t!(fs::create_dir_all(image.join("share/man/man1")));
|
||||
cp_r(&build.src.join("man"), &image.join("share/man/man1"));
|
||||
cp_r(&build.src.join("src/doc/man"), &image.join("share/man/man1"));
|
||||
|
||||
// Debugger scripts
|
||||
builder.ensure(DebuggerScripts {
|
||||
@ -724,6 +727,9 @@ impl Step for Src {
|
||||
let dst_src = dst.join("rust");
|
||||
t!(fs::create_dir_all(&dst_src));
|
||||
|
||||
let src_files = [
|
||||
"src/Cargo.lock",
|
||||
];
|
||||
// This is the reduced set of paths which will become the rust-src component
|
||||
// (essentially libstd and all of its path dependencies)
|
||||
let std_src_dirs = [
|
||||
@ -754,11 +760,14 @@ impl Step for Src {
|
||||
"src/libprofiler_builtins",
|
||||
];
|
||||
let std_src_dirs_exclude = [
|
||||
"src/compiler-rt/test",
|
||||
"src/libcompiler_builtins/compiler-rt/test",
|
||||
"src/jemalloc/test/unit",
|
||||
];
|
||||
|
||||
copy_src_dirs(build, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src);
|
||||
for file in src_files.iter() {
|
||||
copy(&build.src.join(file), &dst_src.join(file));
|
||||
}
|
||||
|
||||
// Create source tarball in rust-installer format
|
||||
let mut cmd = rust_installer(builder);
|
||||
@ -822,9 +831,9 @@ impl Step for PlainSourceTarball {
|
||||
"RELEASES.md",
|
||||
"configure",
|
||||
"x.py",
|
||||
"config.toml.example",
|
||||
];
|
||||
let src_dirs = [
|
||||
"man",
|
||||
"src",
|
||||
];
|
||||
|
||||
@ -837,6 +846,9 @@ impl Step for PlainSourceTarball {
|
||||
|
||||
// Create the version file
|
||||
write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes());
|
||||
if let Some(sha) = build.rust_sha() {
|
||||
write_file(&plain_dst_src.join("git-commit-hash"), sha.as_bytes());
|
||||
}
|
||||
|
||||
// If we're building from git sources, we need to vendor a complete distribution.
|
||||
if build.rust_info.is_git() {
|
||||
@ -887,7 +899,12 @@ impl Step for PlainSourceTarball {
|
||||
fn install(src: &Path, dstdir: &Path, perms: u32) {
|
||||
let dst = dstdir.join(src.file_name().unwrap());
|
||||
t!(fs::create_dir_all(dstdir));
|
||||
t!(fs::copy(src, &dst));
|
||||
drop(fs::remove_file(&dst));
|
||||
{
|
||||
let mut s = t!(fs::File::open(&src));
|
||||
let mut d = t!(fs::File::create(&dst));
|
||||
io::copy(&mut s, &mut d).expect("failed to copy");
|
||||
}
|
||||
chmod(&dst, perms);
|
||||
}
|
||||
|
||||
@ -1081,19 +1098,39 @@ impl Step for Rls {
|
||||
.arg("--output-dir").arg(&distdir(build))
|
||||
.arg("--non-installed-overlay").arg(&overlay)
|
||||
.arg(format!("--package-name={}-{}", name, target))
|
||||
.arg("--legacy-manifest-dirs=rustlib,cargo");
|
||||
|
||||
if build.config.channel == "nightly" {
|
||||
cmd.arg("--component-name=rls");
|
||||
} else {
|
||||
cmd.arg("--component-name=rls-preview");
|
||||
}
|
||||
.arg("--legacy-manifest-dirs=rustlib,cargo")
|
||||
.arg("--component-name=rls-preview");
|
||||
|
||||
build.run(&mut cmd);
|
||||
distdir(build).join(format!("{}-{}.tar.gz", name, target))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DontDistWithMiriEnabled;
|
||||
|
||||
impl Step for DontDistWithMiriEnabled {
|
||||
type Output = PathBuf;
|
||||
const DEFAULT: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
let build_miri = run.builder.build.config.test_miri;
|
||||
run.default_condition(build_miri)
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig) {
|
||||
run.builder.ensure(DontDistWithMiriEnabled);
|
||||
}
|
||||
|
||||
fn run(self, _: &Builder) -> PathBuf {
|
||||
panic!("Do not distribute with miri enabled.\n\
|
||||
The distributed libraries would include all MIR (increasing binary size).
|
||||
The distributed MIR would include validation statements.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Extended {
|
||||
stage: u32,
|
||||
@ -1156,6 +1193,9 @@ impl Step for Extended {
|
||||
install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
|
||||
let version = build.rust_version();
|
||||
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
|
||||
if let Some(sha) = build.rust_sha() {
|
||||
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
|
||||
}
|
||||
install(&etc.join("README.md"), &overlay, 0o644);
|
||||
|
||||
// When rust-std package split from rustc, we needed to ensure that during
|
||||
@ -1163,7 +1203,10 @@ impl Step for Extended {
|
||||
// the std files during uninstall. To do this ensure that rustc comes
|
||||
// before rust-std in the list below.
|
||||
let mut tarballs = vec![rustc_installer, cargo_installer, rls_installer,
|
||||
analysis_installer, docs_installer, std_installer];
|
||||
analysis_installer, std_installer];
|
||||
if build.config.docs {
|
||||
tarballs.push(docs_installer);
|
||||
}
|
||||
if target.contains("pc-windows-gnu") {
|
||||
tarballs.push(mingw_installer.unwrap());
|
||||
}
|
||||
@ -1285,12 +1328,8 @@ impl Step for Extended {
|
||||
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target))
|
||||
.join(format!("rust-std-{}", target)),
|
||||
&exe.join("rust-std"));
|
||||
let rls_path = if build.config.channel == "nightly" {
|
||||
work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls")
|
||||
} else {
|
||||
work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview")
|
||||
};
|
||||
cp_r(&rls_path, &exe.join("rls"));
|
||||
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview"),
|
||||
&exe.join("rls"));
|
||||
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-analysis"), target))
|
||||
.join(format!("rust-analysis-{}", target)),
|
||||
&exe.join("rust-analysis"));
|
||||
|
@ -669,11 +669,6 @@ impl Step for ErrorIndex {
|
||||
let build = builder.build;
|
||||
let target = self.target;
|
||||
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: builder.compiler(0, build.build),
|
||||
target,
|
||||
});
|
||||
|
||||
println!("Documenting error index ({})", target);
|
||||
let out = build.doc_out(target);
|
||||
t!(fs::create_dir_all(&out));
|
||||
|
@ -60,7 +60,9 @@ pub enum Subcommand {
|
||||
paths: Vec<PathBuf>,
|
||||
test_args: Vec<String>,
|
||||
},
|
||||
Clean,
|
||||
Clean {
|
||||
all: bool,
|
||||
},
|
||||
Dist {
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
@ -136,7 +138,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
None => {
|
||||
// No subcommand -- show the general usage and subcommand help
|
||||
println!("{}\n", subcommand_help);
|
||||
process::exit(0);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -147,6 +149,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
|
||||
opts.optmulti("", "test-args", "extra arguments", "ARGS");
|
||||
},
|
||||
"bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
|
||||
"clean" => { opts.optflag("", "all", "clean all build artifacts"); },
|
||||
_ => { },
|
||||
};
|
||||
|
||||
@ -250,7 +253,7 @@ Arguments:
|
||||
}
|
||||
});
|
||||
|
||||
// All subcommands can have an optional "Available paths" section
|
||||
// All subcommands except `clean` can have an optional "Available paths" section
|
||||
if matches.opt_present("verbose") {
|
||||
let config = Config::parse(&["build".to_string()]);
|
||||
let mut build = Build::new(config);
|
||||
@ -258,9 +261,10 @@ Arguments:
|
||||
|
||||
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
|
||||
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
|
||||
} else {
|
||||
extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
|
||||
subcommand).as_str());
|
||||
} else if subcommand.as_str() != "clean" {
|
||||
extra_help.push_str(format!(
|
||||
"Run `./x.py {} -h -v` to see a list of available paths.",
|
||||
subcommand).as_str());
|
||||
}
|
||||
|
||||
// User passed in -h/--help?
|
||||
@ -290,10 +294,13 @@ Arguments:
|
||||
}
|
||||
"clean" => {
|
||||
if paths.len() > 0 {
|
||||
println!("\nclean takes no arguments\n");
|
||||
println!("\nclean does not take a path argument\n");
|
||||
usage(1, &opts, &subcommand_help, &extra_help);
|
||||
}
|
||||
Subcommand::Clean
|
||||
|
||||
Subcommand::Clean {
|
||||
all: matches.opt_present("all"),
|
||||
}
|
||||
}
|
||||
"dist" => {
|
||||
Subcommand::Dist {
|
||||
|
@ -126,7 +126,7 @@ extern crate lazy_static;
|
||||
extern crate serde_json;
|
||||
extern crate cmake;
|
||||
extern crate filetime;
|
||||
extern crate gcc;
|
||||
extern crate cc;
|
||||
extern crate getopts;
|
||||
extern crate num_cpus;
|
||||
extern crate toml;
|
||||
@ -134,20 +134,21 @@ extern crate toml;
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::Command;
|
||||
use std::process::{self, Command};
|
||||
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};
|
||||
|
||||
mod cc;
|
||||
mod cc_detect;
|
||||
mod channel;
|
||||
mod check;
|
||||
mod clean;
|
||||
@ -164,6 +165,7 @@ pub mod util;
|
||||
mod builder;
|
||||
mod cache;
|
||||
mod tool;
|
||||
mod toolstate;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod job;
|
||||
@ -239,13 +241,13 @@ pub struct Build {
|
||||
|
||||
// Runtime state filled in later on
|
||||
// target -> (cc, ar)
|
||||
cc: HashMap<Interned<String>, (gcc::Tool, Option<PathBuf>)>,
|
||||
cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>,
|
||||
// host -> (cc, ar)
|
||||
cxx: HashMap<Interned<String>, gcc::Tool>,
|
||||
cxx: HashMap<Interned<String>, cc::Tool>,
|
||||
crates: HashMap<Interned<String>, Crate>,
|
||||
is_sudo: bool,
|
||||
ci_env: CiEnv,
|
||||
delayed_failures: Cell<usize>,
|
||||
delayed_failures: RefCell<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -327,7 +329,7 @@ impl Build {
|
||||
lldb_python_dir: None,
|
||||
is_sudo,
|
||||
ci_env: CiEnv::current(),
|
||||
delayed_failures: Cell::new(0),
|
||||
delayed_failures: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,12 +345,12 @@ impl Build {
|
||||
job::setup(self);
|
||||
}
|
||||
|
||||
if let Subcommand::Clean = self.config.cmd {
|
||||
return clean::clean(self);
|
||||
if let Subcommand::Clean { all } = self.config.cmd {
|
||||
return clean::clean(self, all);
|
||||
}
|
||||
|
||||
self.verbose("finding compilers");
|
||||
cc::find(self);
|
||||
cc_detect::find(self);
|
||||
self.verbose("running sanity check");
|
||||
sanity::check(self);
|
||||
// If local-rust is the same major.minor as the current version, then force a local-rebuild
|
||||
@ -366,6 +368,16 @@ impl Build {
|
||||
metadata::build(self);
|
||||
|
||||
builder::Builder::run(&self);
|
||||
|
||||
// Check for postponed failures from `test --no-fail-fast`.
|
||||
let failures = self.delayed_failures.borrow();
|
||||
if failures.len() > 0 {
|
||||
println!("\n{} command(s) did not execute successfully:\n", failures.len());
|
||||
for failure in failures.iter() {
|
||||
println!(" - {}\n", failure);
|
||||
}
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear out `dir` if `input` is newer.
|
||||
@ -542,24 +554,31 @@ impl Build {
|
||||
.join(libdir(&self.config.build))
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if its build
|
||||
/// status is not the expected one
|
||||
fn run_expecting(&self, cmd: &mut Command, expect: BuildExpectation) {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_silent(cmd, expect)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run(&self, cmd: &mut Command) {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_silent(cmd)
|
||||
self.run_expecting(cmd, BuildExpectation::None)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
fn run_quiet(&self, cmd: &mut Command) {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
run_suppressed(cmd)
|
||||
run_suppressed(cmd, BuildExpectation::None)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
/// Exits if the command failed to execute at all, otherwise returns its
|
||||
/// `status.success()`.
|
||||
fn try_run(&self, cmd: &mut Command) -> bool {
|
||||
/// Runs a command, printing out nice contextual information if its build
|
||||
/// status is not the expected one.
|
||||
/// Exits if the command failed to execute at all, otherwise returns whether
|
||||
/// the expectation was met
|
||||
fn try_run(&self, cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_silent(cmd)
|
||||
try_run_silent(cmd, expect)
|
||||
}
|
||||
|
||||
/// Runs a command, printing out nice contextual information if it fails.
|
||||
@ -567,7 +586,7 @@ impl Build {
|
||||
/// `status.success()`.
|
||||
fn try_run_quiet(&self, cmd: &mut Command) -> bool {
|
||||
self.verbose(&format!("running: {:?}", cmd));
|
||||
try_run_suppressed(cmd)
|
||||
try_run_suppressed(cmd, BuildExpectation::None)
|
||||
}
|
||||
|
||||
pub fn is_verbose(&self) -> bool {
|
||||
@ -600,7 +619,7 @@ impl Build {
|
||||
/// specified.
|
||||
fn cflags(&self, target: Interned<String>) -> Vec<String> {
|
||||
// Filter out -O and /O (the optimization flags) that we picked up from
|
||||
// gcc-rs because the build scripts will determine that for themselves.
|
||||
// cc-rs because the build scripts will determine that for themselves.
|
||||
let mut base = self.cc[&target].0.args().iter()
|
||||
.map(|s| s.to_string_lossy().into_owned())
|
||||
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
|
||||
@ -717,7 +736,7 @@ impl Build {
|
||||
fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
|
||||
!self.config.full_bootstrap &&
|
||||
compiler.stage >= 2 &&
|
||||
self.hosts.iter().any(|h| *h == target)
|
||||
(self.hosts.iter().any(|h| *h == target) || target == self.build)
|
||||
}
|
||||
|
||||
/// Returns the directory that OpenSSL artifacts are compiled into if
|
||||
@ -797,6 +816,11 @@ impl Build {
|
||||
self.rust_info.version(self, channel::CFG_RELEASE_NUM)
|
||||
}
|
||||
|
||||
/// Return the full commit hash
|
||||
fn rust_sha(&self) -> Option<&str> {
|
||||
self.rust_info.sha()
|
||||
}
|
||||
|
||||
/// Returns the `a.b.c` version that the given package is at.
|
||||
fn release_num(&self, package: &str) -> String {
|
||||
let mut toml = String::new();
|
||||
|
@ -8,8 +8,6 @@
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
include config.mk
|
||||
|
||||
ifdef VERBOSE
|
||||
Q :=
|
||||
BOOTSTRAP_ARGS := -v
|
||||
@ -57,6 +55,8 @@ check-aux:
|
||||
src/tools/cargotest \
|
||||
src/tools/cargo \
|
||||
src/tools/rls \
|
||||
src/tools/rustfmt \
|
||||
src/tools/miri \
|
||||
src/test/pretty \
|
||||
src/test/run-pass/pretty \
|
||||
src/test/run-fail/pretty \
|
||||
|
@ -27,7 +27,7 @@ use std::process::Command;
|
||||
|
||||
use build_helper::output;
|
||||
use cmake;
|
||||
use gcc;
|
||||
use cc;
|
||||
|
||||
use Build;
|
||||
use util;
|
||||
@ -289,7 +289,7 @@ impl Step for TestHelpers {
|
||||
let _folder = build.fold_output(|| "build_test_helpers");
|
||||
println!("Building test helpers");
|
||||
t!(fs::create_dir_all(&dst));
|
||||
let mut cfg = gcc::Config::new();
|
||||
let mut cfg = cc::Build::new();
|
||||
|
||||
// We may have found various cross-compilers a little differently due to our
|
||||
// extra configuration, so inform gcc of these compilers. Note, though, that
|
||||
@ -306,6 +306,7 @@ impl Step for TestHelpers {
|
||||
.target(&target)
|
||||
.host(&build.build)
|
||||
.opt_level(0)
|
||||
.warnings(false)
|
||||
.debug(false)
|
||||
.file(build.src.join("src/rt/rust_test_helpers.c"))
|
||||
.compile("librust_test_helpers.a");
|
||||
@ -366,7 +367,7 @@ impl Step for Openssl {
|
||||
if !ok {
|
||||
panic!("failed to download openssl source")
|
||||
}
|
||||
let mut shasum = if target.contains("apple") {
|
||||
let mut shasum = if target.contains("apple") || build.build.contains("netbsd") {
|
||||
let mut cmd = Command::new("shasum");
|
||||
cmd.arg("-a").arg("256");
|
||||
cmd
|
||||
@ -386,9 +387,10 @@ impl Step for Openssl {
|
||||
let dst = build.openssl_install_dir(target).unwrap();
|
||||
drop(fs::remove_dir_all(&obj));
|
||||
drop(fs::remove_dir_all(&dst));
|
||||
build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));
|
||||
build.run(Command::new("tar").arg("zxf").arg(&tarball).current_dir(&out));
|
||||
|
||||
let mut configure = Command::new(obj.join("Configure"));
|
||||
let mut configure = Command::new("perl");
|
||||
configure.arg(obj.join("Configure"));
|
||||
configure.arg(format!("--prefix={}", dst.display()));
|
||||
configure.arg("no-dso");
|
||||
configure.arg("no-ssl2");
|
||||
@ -397,6 +399,7 @@ impl Step for Openssl {
|
||||
let os = match &*target {
|
||||
"aarch64-linux-android" => "linux-aarch64",
|
||||
"aarch64-unknown-linux-gnu" => "linux-aarch64",
|
||||
"aarch64-unknown-linux-musl" => "linux-aarch64",
|
||||
"arm-linux-androideabi" => "android",
|
||||
"arm-unknown-linux-gnueabi" => "linux-armv4",
|
||||
"arm-unknown-linux-gnueabihf" => "linux-armv4",
|
||||
@ -407,6 +410,7 @@ impl Step for Openssl {
|
||||
"i686-unknown-freebsd" => "BSD-x86-elf",
|
||||
"i686-unknown-linux-gnu" => "linux-elf",
|
||||
"i686-unknown-linux-musl" => "linux-elf",
|
||||
"i686-unknown-netbsd" => "BSD-x86-elf",
|
||||
"mips-unknown-linux-gnu" => "linux-mips32",
|
||||
"mips64-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
"mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
|
||||
@ -415,6 +419,7 @@ impl Step for Openssl {
|
||||
"powerpc64-unknown-linux-gnu" => "linux-ppc64",
|
||||
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
|
||||
"s390x-unknown-linux-gnu" => "linux64-s390x",
|
||||
"sparc64-unknown-netbsd" => "BSD-sparc64",
|
||||
"x86_64-apple-darwin" => "darwin64-x86_64-cc",
|
||||
"x86_64-linux-android" => "linux-x86_64",
|
||||
"x86_64-unknown-freebsd" => "BSD-x86_64",
|
||||
@ -434,6 +439,15 @@ impl Step for Openssl {
|
||||
configure.arg("-mandroid");
|
||||
configure.arg("-fomit-frame-pointer");
|
||||
}
|
||||
if target == "sparc64-unknown-netbsd" {
|
||||
// Need -m64 to get assembly generated correctly for sparc64.
|
||||
configure.arg("-m64");
|
||||
if build.build.contains("netbsd") {
|
||||
// Disable sparc64 asm on NetBSD builders, it uses
|
||||
// m4(1)'s -B flag, which NetBSD m4 does not support.
|
||||
configure.arg("no-asm");
|
||||
}
|
||||
}
|
||||
// Make PIE binaries
|
||||
// Non-PIE linker support was removed in Lollipop
|
||||
// https://source.android.com/security/enhancements/enhancements50
|
||||
|
@ -221,8 +221,9 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
|
||||
let run = |cmd: &mut Command| {
|
||||
cmd.output().map(|output| {
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
.lines().next().unwrap()
|
||||
.to_string()
|
||||
.lines().next().unwrap_or_else(|| {
|
||||
panic!("{:?} failed {:?}", cmd, output)
|
||||
}).to_string()
|
||||
})
|
||||
};
|
||||
build.lldb_version = run(Command::new("lldb").arg("--version")).ok();
|
||||
|
@ -21,6 +21,8 @@ use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
|
||||
use native;
|
||||
use channel::GitInfo;
|
||||
use cache::Interned;
|
||||
use toolstate::ToolState;
|
||||
use build_helper::BuildExpectation;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct CleanTools {
|
||||
@ -62,7 +64,9 @@ struct ToolBuild {
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
tool: &'static str,
|
||||
path: &'static str,
|
||||
mode: Mode,
|
||||
expectation: BuildExpectation,
|
||||
}
|
||||
|
||||
impl Step for ToolBuild {
|
||||
@ -81,6 +85,8 @@ impl Step for ToolBuild {
|
||||
let compiler = self.compiler;
|
||||
let target = self.target;
|
||||
let tool = self.tool;
|
||||
let path = self.path;
|
||||
let expectation = self.expectation;
|
||||
|
||||
match self.mode {
|
||||
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
|
||||
@ -92,21 +98,22 @@ impl Step for ToolBuild {
|
||||
let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
|
||||
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, tool);
|
||||
build.run(&mut cargo);
|
||||
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
|
||||
build.run_expecting(&mut cargo, expectation);
|
||||
build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host))
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_tool_cargo(
|
||||
pub fn prepare_tool_cargo(
|
||||
builder: &Builder,
|
||||
compiler: Compiler,
|
||||
target: Interned<String>,
|
||||
tool: &'static str,
|
||||
command: &'static str,
|
||||
path: &'static str,
|
||||
) -> Command {
|
||||
let build = builder.build;
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build");
|
||||
let dir = build.src.join("src/tools").join(tool);
|
||||
let mut cargo = builder.cargo(compiler, Mode::Tool, target, command);
|
||||
let dir = build.src.join(path);
|
||||
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
|
||||
|
||||
// We don't want to build tools dynamically as they'll be running across
|
||||
@ -119,7 +126,12 @@ fn prepare_tool_cargo(
|
||||
cargo.env("LIBZ_SYS_STATIC", "1");
|
||||
}
|
||||
|
||||
// if tools are using lzma we want to force the build script to build its
|
||||
// own copy
|
||||
cargo.env("LZMA_API_STATIC", "1");
|
||||
|
||||
cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel);
|
||||
cargo.env("CFG_VERSION", build.rust_version());
|
||||
|
||||
let info = GitInfo::new(&build.config, &dir);
|
||||
if let Some(sha) = info.sha() {
|
||||
@ -145,15 +157,27 @@ macro_rules! tool {
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub fn tool_exe(&self, tool: Tool) -> PathBuf {
|
||||
let stage = self.tool_default_stage(tool);
|
||||
match tool {
|
||||
$(Tool::$name =>
|
||||
self.ensure($name {
|
||||
compiler: self.compiler(0, self.build.build),
|
||||
compiler: self.compiler(stage, self.build.build),
|
||||
target: self.build.build,
|
||||
}),
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tool_default_stage(&self, tool: Tool) -> u32 {
|
||||
// Compile the error-index in the top stage as it depends on
|
||||
// rustdoc, so we want to avoid recompiling rustdoc twice if we
|
||||
// can. Otherwise compile everything else in stage0 as there's
|
||||
// no need to rebootstrap everything
|
||||
match tool {
|
||||
Tool::ErrorIndex => self.top_stage,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
@ -183,6 +207,8 @@ macro_rules! tool {
|
||||
target: self.target,
|
||||
tool: $tool_name,
|
||||
mode: $mode,
|
||||
path: $path,
|
||||
expectation: BuildExpectation::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -200,7 +226,7 @@ tool!(
|
||||
Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest;
|
||||
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd;
|
||||
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::Libstd;
|
||||
RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::Libstd;
|
||||
RustInstaller, "src/tools/rust-installer", "fabricate", Mode::Libstd;
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
@ -229,6 +255,8 @@ impl Step for RemoteTestServer {
|
||||
target: self.target,
|
||||
tool: "remote-test-server",
|
||||
mode: Mode::Libstd,
|
||||
path: "src/tools/remote-test-server",
|
||||
expectation: BuildExpectation::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -275,7 +303,16 @@ impl Step for Rustdoc {
|
||||
let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
|
||||
println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host);
|
||||
|
||||
let mut cargo = prepare_tool_cargo(builder, build_compiler, target, "rustdoc");
|
||||
let mut cargo = prepare_tool_cargo(builder,
|
||||
build_compiler,
|
||||
target,
|
||||
"build",
|
||||
"src/tools/rustdoc");
|
||||
|
||||
// Most tools don't get debuginfo, but rustdoc should.
|
||||
cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
|
||||
.env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
|
||||
|
||||
build.run(&mut cargo);
|
||||
// Cargo adds a number of paths to the dylib search path on windows, which results in
|
||||
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
|
||||
@ -336,6 +373,48 @@ impl Step for Cargo {
|
||||
target: self.target,
|
||||
tool: "cargo",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/cargo",
|
||||
expectation: BuildExpectation::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Clippy {
|
||||
pub compiler: Compiler,
|
||||
pub target: Interned<String>,
|
||||
}
|
||||
|
||||
impl Step for Clippy {
|
||||
type Output = PathBuf;
|
||||
const DEFAULT: bool = false;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun) -> ShouldRun {
|
||||
run.path("src/tools/clippy")
|
||||
}
|
||||
|
||||
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) -> PathBuf {
|
||||
// Clippy depends on procedural macros (serde), which requires a full host
|
||||
// compiler to be available, so we need to depend on that.
|
||||
builder.ensure(compile::Rustc {
|
||||
compiler: self.compiler,
|
||||
target: builder.build.build,
|
||||
});
|
||||
builder.ensure(ToolBuild {
|
||||
compiler: self.compiler,
|
||||
target: self.target,
|
||||
tool: "clippy",
|
||||
mode: Mode::Librustc,
|
||||
path: "src/tools/clippy",
|
||||
expectation: builder.build.config.toolstate.clippy.passes(ToolState::Compiling),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -378,6 +457,79 @@ impl Step for Rls {
|
||||
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 = 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) -> 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 = 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) -> 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),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -387,7 +539,7 @@ impl<'a> Builder<'a> {
|
||||
/// `host`.
|
||||
pub fn tool_cmd(&self, tool: Tool) -> Command {
|
||||
let mut cmd = Command::new(self.tool_exe(tool));
|
||||
let compiler = self.compiler(0, self.build.build);
|
||||
let compiler = self.compiler(self.tool_default_stage(tool), self.build.build);
|
||||
self.prepare_tool_cmd(compiler, &mut cmd);
|
||||
cmd
|
||||
}
|
||||
|
51
src/bootstrap/toolstate.rs
Normal file
51
src/bootstrap/toolstate.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
use build_helper::BuildExpectation;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
/// Whether a tool can be compiled, tested or neither
|
||||
pub enum ToolState {
|
||||
/// The tool compiles successfully, but the test suite fails
|
||||
Compiling = 1,
|
||||
/// The tool compiles successfully and its test suite passes
|
||||
Testing = 2,
|
||||
/// The tool can't even be compiled
|
||||
Broken = 0,
|
||||
}
|
||||
|
||||
impl ToolState {
|
||||
/// If a tool with the current toolstate should be working on
|
||||
/// the given toolstate
|
||||
pub fn passes(self, other: ToolState) -> BuildExpectation {
|
||||
if self as usize >= other as usize {
|
||||
BuildExpectation::Succeeding
|
||||
} else {
|
||||
BuildExpectation::Failing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ToolState {
|
||||
fn default() -> Self {
|
||||
// err on the safe side
|
||||
ToolState::Broken
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
@ -34,8 +34,12 @@ pub fn staticlib(name: &str, target: &str) -> String {
|
||||
/// Copies a file from `src` to `dst`
|
||||
pub fn copy(src: &Path, dst: &Path) {
|
||||
let _ = fs::remove_file(&dst);
|
||||
let res = fs::copy(src, dst);
|
||||
if let Err(e) = res {
|
||||
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
|
||||
// windows), but if that fails just fall back to a slow `copy` operation.
|
||||
if let Ok(()) = fs::hard_link(src, dst) {
|
||||
return
|
||||
}
|
||||
if let Err(e) = fs::copy(src, dst) {
|
||||
panic!("failed to copy `{}` to `{}`: {}", src.display(),
|
||||
dst.display(), e)
|
||||
}
|
||||
@ -44,7 +48,6 @@ pub fn copy(src: &Path, dst: &Path) {
|
||||
let atime = FileTime::from_last_access_time(&metadata);
|
||||
let mtime = FileTime::from_last_modification_time(&metadata);
|
||||
t!(filetime::set_file_times(dst, atime, mtime));
|
||||
|
||||
}
|
||||
|
||||
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
|
||||
@ -279,7 +282,7 @@ pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
|
||||
ptr::null_mut());
|
||||
|
||||
let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
let mut db = data.as_mut_ptr()
|
||||
let db = data.as_mut_ptr()
|
||||
as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
|
||||
let buf = &mut (*db).ReparseTarget as *mut _;
|
||||
let mut i = 0;
|
||||
|
@ -35,55 +35,97 @@ macro_rules! t {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(cmd: &mut Command) {
|
||||
println!("running: {:?}", cmd);
|
||||
run_silent(cmd);
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum BuildExpectation {
|
||||
Succeeding,
|
||||
Failing,
|
||||
None,
|
||||
}
|
||||
|
||||
pub fn run_silent(cmd: &mut Command) {
|
||||
if !try_run_silent(cmd) {
|
||||
pub fn run(cmd: &mut Command, expect: BuildExpectation) {
|
||||
println!("running: {:?}", cmd);
|
||||
run_silent(cmd, expect);
|
||||
}
|
||||
|
||||
pub fn run_silent(cmd: &mut Command, expect: BuildExpectation) {
|
||||
if !try_run_silent(cmd, expect) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_run_silent(cmd: &mut Command) -> bool {
|
||||
pub fn try_run_silent(cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
let status = match cmd.status() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
};
|
||||
if !status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status);
|
||||
}
|
||||
status.success()
|
||||
process_status(
|
||||
cmd,
|
||||
status.success(),
|
||||
expect,
|
||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n",
|
||||
cmd,
|
||||
status))
|
||||
}
|
||||
|
||||
pub fn run_suppressed(cmd: &mut Command) {
|
||||
if !try_run_suppressed(cmd) {
|
||||
fn process_status<F: FnOnce()>(
|
||||
cmd: &Command,
|
||||
success: bool,
|
||||
expect: BuildExpectation,
|
||||
f: F,
|
||||
) -> bool {
|
||||
use BuildExpectation::*;
|
||||
match (expect, success) {
|
||||
(None, false) => { f(); false },
|
||||
// Non-tool build succeeds, everything is good
|
||||
(None, true) => true,
|
||||
// Tool expected to work and is working
|
||||
(Succeeding, true) => true,
|
||||
// Tool expected to fail and is failing
|
||||
(Failing, false) => {
|
||||
println!("This failure is expected (see `src/tools/toolstate.toml`)");
|
||||
true
|
||||
},
|
||||
// Tool expected to work, but is failing
|
||||
(Succeeding, false) => {
|
||||
f();
|
||||
println!("You can disable the tool in `src/tools/toolstate.toml`");
|
||||
false
|
||||
},
|
||||
// Tool expected to fail, but is working
|
||||
(Failing, true) => {
|
||||
println!("Expected `{:?}` to fail, but it succeeded.\n\
|
||||
Please adjust `src/tools/toolstate.toml` accordingly", cmd);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) {
|
||||
if !try_run_suppressed(cmd, expect) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_run_suppressed(cmd: &mut Command) -> bool {
|
||||
pub fn try_run_suppressed(cmd: &mut Command, expect: BuildExpectation) -> bool {
|
||||
let output = match cmd.output() {
|
||||
Ok(status) => status,
|
||||
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
|
||||
cmd, e)),
|
||||
};
|
||||
if !output.status.success() {
|
||||
println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
process_status(
|
||||
cmd,
|
||||
output.status.success(),
|
||||
expect,
|
||||
|| println!("\n\ncommand did not execute successfully: {:?}\n\
|
||||
expected success, got: {}\n\n\
|
||||
stdout ----\n{}\n\
|
||||
stderr ----\n{}\n\n",
|
||||
cmd,
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
output.status.success()
|
||||
String::from_utf8_lossy(&output.stderr)))
|
||||
}
|
||||
|
||||
pub fn gnu_target(target: &str) -> String {
|
||||
|
@ -20,7 +20,7 @@ Images will output artifacts in an `obj` dir at the root of a repository.
|
||||
|
||||
- Each directory, excluding `scripts` and `disabled`, corresponds to a docker image
|
||||
- `scripts` contains files shared by docker images
|
||||
- `disabled` contains images that are not build travis
|
||||
- `disabled` contains images that are not built on travis
|
||||
|
||||
## Cross toolchains
|
||||
|
||||
|
@ -5,21 +5,27 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm 9
|
||||
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm 14
|
||||
|
||||
# Note:
|
||||
# Do not upgrade to `openjdk-9-jre-headless`, as it will cause certificate error
|
||||
# when installing the Android SDK (see PR #45193). This is unfortunate, but
|
||||
# every search result suggested either disabling HTTPS or replacing JDK 9 by
|
||||
# JDK 8 as the solution (e.g. https://stackoverflow.com/q/41421340). :|
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libgl1-mesa-glx \
|
||||
libpulse0 \
|
||||
libstdc++6:i386 \
|
||||
openjdk-9-jre-headless \
|
||||
openjdk-8-jre-headless \
|
||||
tzdata
|
||||
|
||||
COPY scripts/android-sdk.sh /scripts/
|
||||
RUN . /scripts/android-sdk.sh && \
|
||||
download_and_create_avd tools_r25.2.5-linux.zip armeabi-v7a 18
|
||||
download_and_create_avd 4333796 armeabi-v7a 18
|
||||
|
||||
ENV PATH=$PATH:/android/sdk/emulator
|
||||
ENV PATH=$PATH:/android/sdk/tools
|
||||
ENV PATH=$PATH:/android/sdk/platform-tools
|
||||
|
||||
@ -27,7 +33,7 @@ ENV TARGETS=arm-linux-androideabi
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--target=$TARGETS \
|
||||
--arm-linux-androideabi-ndk=/android/ndk/arm-9
|
||||
--arm-linux-androideabi-ndk=/android/ndk/arm-14
|
||||
|
||||
ENV SCRIPT python2.7 ../x.py test --target $TARGETS
|
||||
|
||||
|
@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
zlib1g-dev \
|
||||
g++-arm-linux-gnueabi \
|
||||
g++-arm-linux-gnueabihf \
|
||||
g++-aarch64-linux-gnu \
|
||||
gcc-sparc64-linux-gnu \
|
||||
libc6-dev-sparc64-cross \
|
||||
bzip2 \
|
||||
@ -46,6 +47,7 @@ ENV TARGETS=$TARGETS,mipsel-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi
|
||||
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabihf
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-linux-musl
|
||||
ENV TARGETS=$TARGETS,sparc64-unknown-linux-gnu
|
||||
ENV TARGETS=$TARGETS,x86_64-unknown-redox
|
||||
|
||||
@ -62,7 +64,8 @@ ENV RUST_CONFIGURE_ARGS \
|
||||
--target=$TARGETS \
|
||||
--musl-root-arm=/usr/local/arm-linux-musleabi \
|
||||
--musl-root-armhf=/usr/local/arm-linux-musleabihf \
|
||||
--musl-root-armv7=/usr/local/armv7-linux-musleabihf
|
||||
--musl-root-armv7=/usr/local/armv7-linux-musleabihf \
|
||||
--musl-root-aarch64=/usr/local/aarch64-linux-musl
|
||||
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS
|
||||
|
||||
# sccache
|
||||
|
@ -65,11 +65,24 @@ CFLAGS="-march=armv7-a" \
|
||||
hide_output make -j$(nproc)
|
||||
hide_output make install
|
||||
cd ..
|
||||
rm -rf musl-$MUSL
|
||||
|
||||
tar xf musl-$MUSL.tar.gz
|
||||
cd musl-$MUSL
|
||||
CC=aarch64-linux-gnu-gcc \
|
||||
CFLAGS="" \
|
||||
hide_output ./configure \
|
||||
--prefix=/usr/local/aarch64-linux-musl \
|
||||
--enable-wrapper=gcc
|
||||
hide_output make -j$(nproc)
|
||||
hide_output make install
|
||||
cd ..
|
||||
rm -rf musl-$MUSL*
|
||||
|
||||
ln -nsf ../arm-linux-musleabi/bin/musl-gcc /usr/local/bin/arm-linux-musleabi-gcc
|
||||
ln -nsf ../arm-linux-musleabihf/bin/musl-gcc /usr/local/bin/arm-linux-musleabihf-gcc
|
||||
ln -nsf ../armv7-linux-musleabihf/bin/musl-gcc /usr/local/bin/armv7-linux-musleabihf-gcc
|
||||
ln -nsf ../aarch64-linux-musl/bin/musl-gcc /usr/local/bin/aarch64-unknown-linux-musl-gcc
|
||||
|
||||
curl -L https://github.com/llvm-mirror/llvm/archive/release_39.tar.gz | tar xzf -
|
||||
curl -L https://github.com/llvm-mirror/libunwind/archive/release_39.tar.gz | tar xzf -
|
||||
@ -116,5 +129,19 @@ cp lib/libunwind.a /usr/local/armv7-linux-musleabihf/lib
|
||||
cd ..
|
||||
rm -rf libunwind-build
|
||||
|
||||
mkdir libunwind-build
|
||||
cd libunwind-build
|
||||
cmake ../libunwind-release_39 \
|
||||
-DLLVM_PATH=/tmp/llvm-release_39 \
|
||||
-DLIBUNWIND_ENABLE_SHARED=0 \
|
||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
|
||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
||||
-DCMAKE_C_FLAGS="" \
|
||||
-DCMAKE_CXX_FLAGS=""
|
||||
make -j$(nproc)
|
||||
cp lib/libunwind.a /usr/local/aarch64-linux-musl/lib
|
||||
cd ..
|
||||
rm -rf libunwind-build
|
||||
|
||||
rm -rf libunwind-release_39
|
||||
rm -rf llvm-release_39
|
||||
|
52
src/ci/docker/cross2/Dockerfile
Normal file
52
src/ci/docker/cross2/Dockerfile
Normal file
@ -0,0 +1,52 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
COPY scripts/cross-apt-packages.sh /scripts/
|
||||
RUN sh /scripts/cross-apt-packages.sh
|
||||
|
||||
RUN apt-get build-dep -y clang llvm && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
libedit-dev \
|
||||
libgmp-dev \
|
||||
libisl-dev \
|
||||
libmpc-dev \
|
||||
libmpfr-dev \
|
||||
ninja-build \
|
||||
nodejs \
|
||||
python2.7-dev \
|
||||
software-properties-common \
|
||||
unzip
|
||||
|
||||
RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486
|
||||
RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2-testing main'
|
||||
|
||||
WORKDIR /tmp
|
||||
COPY cross2/shared.sh cross2/build-fuchsia-toolchain.sh /tmp/
|
||||
COPY cross2/build-solaris-toolchain.sh /tmp/
|
||||
RUN /tmp/build-fuchsia-toolchain.sh
|
||||
RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386
|
||||
RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV \
|
||||
AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \
|
||||
CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \
|
||||
CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \
|
||||
AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \
|
||||
CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \
|
||||
CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++ \
|
||||
AR_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-ar \
|
||||
CC_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-gcc \
|
||||
CXX_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-g++ \
|
||||
AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \
|
||||
CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \
|
||||
CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++
|
||||
|
||||
ENV TARGETS=x86_64-unknown-fuchsia
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia
|
||||
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
|
||||
ENV TARGETS=$TARGETS,x86_64-sun-solaris
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended
|
||||
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS
|
65
src/ci/docker/cross2/build-fuchsia-toolchain.sh
Executable file
65
src/ci/docker/cross2/build-fuchsia-toolchain.sh
Executable file
@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
# ignore-tidy-linelength
|
||||
|
||||
set -ex
|
||||
source shared.sh
|
||||
|
||||
ZIRCON=e9a26dbc70d631029f8ee9763103910b7e3a2fe1
|
||||
|
||||
mkdir -p zircon
|
||||
pushd zircon > /dev/null
|
||||
|
||||
# Download sources
|
||||
git init
|
||||
git remote add origin https://fuchsia.googlesource.com/zircon
|
||||
git fetch --depth=1 origin $ZIRCON
|
||||
git reset --hard FETCH_HEAD
|
||||
|
||||
# Download toolchain
|
||||
./scripts/download-toolchain
|
||||
chmod -R a+rx prebuilt/downloads/clang+llvm-x86_64-linux
|
||||
cp -a prebuilt/downloads/clang+llvm-x86_64-linux/. /usr/local
|
||||
|
||||
build() {
|
||||
local arch="$1"
|
||||
|
||||
case "${arch}" in
|
||||
x86_64) tgt="zircon-pc-x86-64" ;;
|
||||
aarch64) tgt="zircon-qemu-arm64" ;;
|
||||
esac
|
||||
|
||||
hide_output make -j$(getconf _NPROCESSORS_ONLN) $tgt
|
||||
dst=/usr/local/${arch}-unknown-fuchsia
|
||||
mkdir -p $dst
|
||||
cp -a build-${tgt}/sysroot/include $dst/
|
||||
cp -a build-${tgt}/sysroot/lib $dst/
|
||||
}
|
||||
|
||||
# Build sysroot
|
||||
for arch in x86_64 aarch64; do
|
||||
build ${arch}
|
||||
done
|
||||
|
||||
popd > /dev/null
|
||||
rm -rf zircon
|
||||
|
||||
for arch in x86_64 aarch64; do
|
||||
for tool in clang clang++; do
|
||||
cat >/usr/local/bin/${arch}-unknown-fuchsia-${tool} <<EOF
|
||||
#!/bin/sh
|
||||
${tool} --target=${arch}-unknown-fuchsia --sysroot=/usr/local/${arch}-unknown-fuchsia "\$@"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/${arch}-unknown-fuchsia-${tool}
|
||||
done
|
||||
ln -s /usr/local/bin/llvm-ar /usr/local/bin/${arch}-unknown-fuchsia-ar
|
||||
done
|
107
src/ci/docker/cross2/build-solaris-toolchain.sh
Executable file
107
src/ci/docker/cross2/build-solaris-toolchain.sh
Executable file
@ -0,0 +1,107 @@
|
||||
#!/bin/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
|
||||
source shared.sh
|
||||
|
||||
ARCH=$1
|
||||
LIB_ARCH=$2
|
||||
APT_ARCH=$3
|
||||
BINUTILS=2.28.1
|
||||
GCC=6.4.0
|
||||
|
||||
# First up, build binutils
|
||||
mkdir binutils
|
||||
cd binutils
|
||||
|
||||
curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.xz | tar xJf -
|
||||
mkdir binutils-build
|
||||
cd binutils-build
|
||||
hide_output ../binutils-$BINUTILS/configure --target=$ARCH-sun-solaris2.10
|
||||
hide_output make -j10
|
||||
hide_output make install
|
||||
|
||||
cd ../..
|
||||
rm -rf binutils
|
||||
|
||||
# Next, download and install the relevant solaris packages
|
||||
mkdir solaris
|
||||
cd solaris
|
||||
|
||||
dpkg --add-architecture $APT_ARCH
|
||||
apt-get update
|
||||
apt-get download $(apt-cache depends --recurse --no-replaces \
|
||||
libc-dev:$APT_ARCH \
|
||||
libm-dev:$APT_ARCH \
|
||||
libpthread-dev:$APT_ARCH \
|
||||
libresolv-dev:$APT_ARCH \
|
||||
librt-dev:$APT_ARCH \
|
||||
libsocket-dev:$APT_ARCH \
|
||||
system-crt:$APT_ARCH \
|
||||
system-header:$APT_ARCH \
|
||||
| grep "^\w")
|
||||
|
||||
for deb in *$APT_ARCH.deb; do
|
||||
dpkg -x $deb .
|
||||
done
|
||||
|
||||
# Remove Solaris 11 functions that are optionally used by libbacktrace.
|
||||
# This is for Solaris 10 compatibility.
|
||||
rm usr/include/link.h
|
||||
patch -p0 << 'EOF'
|
||||
--- usr/include/string.h
|
||||
+++ usr/include/string10.h
|
||||
@@ -93 +92,0 @@
|
||||
-extern size_t strnlen(const char *, size_t);
|
||||
EOF
|
||||
|
||||
mkdir /usr/local/$ARCH-sun-solaris2.10/usr
|
||||
mv usr/include /usr/local/$ARCH-sun-solaris2.10/usr/include
|
||||
mv usr/lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.10/lib
|
||||
mv lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.10/lib
|
||||
|
||||
ln -s usr/include /usr/local/$ARCH-sun-solaris2.10/sys-include
|
||||
ln -s usr/include /usr/local/$ARCH-sun-solaris2.10/include
|
||||
|
||||
cd ..
|
||||
rm -rf solaris
|
||||
|
||||
# Finally, download and build gcc to target solaris
|
||||
mkdir gcc
|
||||
cd gcc
|
||||
|
||||
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | tar xJf -
|
||||
cd gcc-$GCC
|
||||
|
||||
mkdir ../gcc-build
|
||||
cd ../gcc-build
|
||||
hide_output ../gcc-$GCC/configure \
|
||||
--enable-languages=c,c++ \
|
||||
--target=$ARCH-sun-solaris2.10 \
|
||||
--with-gnu-as \
|
||||
--with-gnu-ld \
|
||||
--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
|
@ -31,7 +31,7 @@ WORKDIR /build
|
||||
# The `config` config file was a previously generated config file for
|
||||
# the kernel. This file was generated by running `make defconfig`
|
||||
# followed by `make menuconfig` and then enabling the IPv6 protocol page.
|
||||
COPY disabled/aarch64-gnu/config /build/.config
|
||||
COPY aarch64-gnu/config /build/.config
|
||||
RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.42.tar.xz | \
|
||||
tar xJf - && \
|
||||
cd /build/linux-4.4.42 && \
|
||||
|
@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm64 21
|
||||
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm64 21
|
||||
|
||||
ENV PATH=$PATH:/android/ndk/arm64-21/bin
|
||||
|
||||
|
@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_ndk android-ndk-r13b-linux-x86_64.zip && \
|
||||
make_standalone_toolchain arm 9 && \
|
||||
download_ndk android-ndk-r15c-linux-x86_64.zip && \
|
||||
make_standalone_toolchain arm 14 && \
|
||||
make_standalone_toolchain arm 21 && \
|
||||
remove_ndk
|
||||
|
||||
RUN chmod 777 /android/ndk && \
|
||||
ln -s /android/ndk/arm-21 /android/ndk/arm
|
||||
|
||||
ENV PATH=$PATH:/android/ndk/arm-9/bin
|
||||
ENV PATH=$PATH:/android/ndk/arm-14/bin
|
||||
|
||||
ENV DEP_Z_ROOT=/android/ndk/arm-9/sysroot/usr/
|
||||
ENV DEP_Z_ROOT=/android/ndk/arm-14/sysroot/usr/
|
||||
|
||||
ENV HOSTS=armv7-linux-androideabi
|
||||
|
||||
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
|
||||
--enable-extended \
|
||||
--enable-cargo-openssl-static
|
||||
|
||||
# We support api level 9, but api level 21 is required to build llvm. To
|
||||
# We support api level 14, but api level 21 is required to build llvm. To
|
||||
# overcome this problem we use a ndk with api level 21 to build llvm and then
|
||||
# switch to a ndk with api level 9 to complete the build. When the linker is
|
||||
# switch to a ndk with api level 14 to complete the build. When the linker is
|
||||
# invoked there are missing symbols (like sigsetempty, not available with api
|
||||
# level 9), the default linker behavior is to generate an error, to allow the
|
||||
# level 14), the default linker behavior is to generate an error, to allow the
|
||||
# build to finish we use --warn-unresolved-symbols. Note that the missing
|
||||
# symbols does not affect std, only the compiler (llvm) and cargo (openssl).
|
||||
ENV SCRIPT \
|
||||
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
|
||||
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
|
||||
rm /android/ndk/arm && \
|
||||
ln -s /android/ndk/arm-9 /android/ndk/arm && \
|
||||
ln -s /android/ndk/arm-14 /android/ndk/arm && \
|
||||
python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
|
@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_ndk android-ndk-r13b-linux-x86_64.zip && \
|
||||
make_standalone_toolchain x86 9 && \
|
||||
download_ndk android-ndk-r15c-linux-x86_64.zip && \
|
||||
make_standalone_toolchain x86 14 && \
|
||||
make_standalone_toolchain x86 21 && \
|
||||
remove_ndk
|
||||
|
||||
RUN chmod 777 /android/ndk && \
|
||||
ln -s /android/ndk/x86-21 /android/ndk/x86
|
||||
|
||||
ENV PATH=$PATH:/android/ndk/x86-9/bin
|
||||
ENV PATH=$PATH:/android/ndk/x86-14/bin
|
||||
|
||||
ENV DEP_Z_ROOT=/android/ndk/x86-9/sysroot/usr/
|
||||
ENV DEP_Z_ROOT=/android/ndk/x86-14/sysroot/usr/
|
||||
|
||||
ENV HOSTS=i686-linux-android
|
||||
|
||||
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
|
||||
--enable-extended \
|
||||
--enable-cargo-openssl-static
|
||||
|
||||
# We support api level 9, but api level 21 is required to build llvm. To
|
||||
# We support api level 14, but api level 21 is required to build llvm. To
|
||||
# overcome this problem we use a ndk with api level 21 to build llvm and then
|
||||
# switch to a ndk with api level 9 to complete the build. When the linker is
|
||||
# switch to a ndk with api level 14 to complete the build. When the linker is
|
||||
# invoked there are missing symbols (like sigsetempty, not available with api
|
||||
# level 9), the default linker behavior is to generate an error, to allow the
|
||||
# level 14), the default linker behavior is to generate an error, to allow the
|
||||
# build to finish we use --warn-unresolved-symbols. Note that the missing
|
||||
# symbols does not affect std, only the compiler (llvm) and cargo (openssl).
|
||||
ENV SCRIPT \
|
||||
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
|
||||
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
|
||||
rm /android/ndk/x86 && \
|
||||
ln -s /android/ndk/x86-9 /android/ndk/x86 && \
|
||||
ln -s /android/ndk/x86-14 /android/ndk/x86 && \
|
||||
python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
|
@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip x86_64 21
|
||||
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip x86_64 21
|
||||
|
||||
ENV PATH=$PATH:/android/ndk/x86_64-21/bin
|
||||
|
||||
|
49
src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile
Normal file
49
src/ci/docker/disabled/dist-x86_64-haiku/Dockerfile
Normal file
@ -0,0 +1,49 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
automake \
|
||||
bison \
|
||||
bzip2 \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
curl \
|
||||
file \
|
||||
flex \
|
||||
g++ \
|
||||
gawk \
|
||||
git \
|
||||
libcurl4-openssl-dev \
|
||||
libssl-dev \
|
||||
make \
|
||||
nasm \
|
||||
pkg-config \
|
||||
python2.7 \
|
||||
sudo \
|
||||
texinfo \
|
||||
wget \
|
||||
xz-utils \
|
||||
zlib1g-dev
|
||||
|
||||
COPY dist-x86_64-haiku/llvm-config.sh /bin/llvm-config-haiku
|
||||
|
||||
ENV ARCH=x86_64
|
||||
|
||||
WORKDIR /tmp
|
||||
COPY dist-x86_64-haiku/build-toolchain.sh /tmp/
|
||||
RUN /tmp/build-toolchain.sh $ARCH
|
||||
|
||||
COPY dist-x86_64-haiku/fetch-packages.sh /tmp/
|
||||
RUN /tmp/fetch-packages.sh
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV HOST=x86_64-unknown-haiku
|
||||
ENV TARGET=target.$HOST
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --host=$HOST --target=$HOST --disable-jemalloc \
|
||||
--set=$TARGET.cc=x86_64-unknown-haiku-gcc \
|
||||
--set=$TARGET.cxx=x86_64-unknown-haiku-g++ \
|
||||
--set=$TARGET.llvm-config=/bin/llvm-config-haiku
|
||||
ENV SCRIPT python2.7 ../x.py dist
|
74
src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh
Executable file
74
src/ci/docker/disabled/dist-x86_64-haiku/build-toolchain.sh
Executable file
@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set -ex
|
||||
|
||||
ARCH=$1
|
||||
|
||||
TOP=$(pwd)
|
||||
|
||||
BUILDTOOLS=$TOP/buildtools
|
||||
HAIKU=$TOP/haiku
|
||||
OUTPUT=/tools
|
||||
SYSROOT=$OUTPUT/cross-tools-$ARCH/sysroot
|
||||
PACKAGE_ROOT=/system
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# First up, build a cross-compiler
|
||||
git clone --depth=1 https://git.haiku-os.org/haiku
|
||||
git clone --depth=1 https://git.haiku-os.org/buildtools
|
||||
cd $BUILDTOOLS/jam
|
||||
hide_output make
|
||||
hide_output ./jam0 install
|
||||
mkdir -p $OUTPUT
|
||||
cd $OUTPUT
|
||||
hide_output $HAIKU/configure --build-cross-tools $ARCH $TOP/buildtools
|
||||
|
||||
# Set up sysroot to redirect to /system
|
||||
mkdir -p $SYSROOT/boot
|
||||
mkdir -p $PACKAGE_ROOT
|
||||
ln -s $PACKAGE_ROOT $SYSROOT/boot/system
|
||||
|
||||
# Build needed packages and tools for the cross-compiler
|
||||
hide_output jam -q haiku.hpkg haiku_devel.hpkg '<build>package'
|
||||
|
||||
# Set up our sysroot
|
||||
cp $OUTPUT/objects/linux/lib/*.so /lib/x86_64-linux-gnu
|
||||
cp $OUTPUT/objects/linux/x86_64/release/tools/package/package /bin/
|
||||
find $SYSROOT/../bin/ -type f -exec ln -s {} /bin/ \;
|
||||
|
||||
# Extract packages
|
||||
package extract -C $PACKAGE_ROOT $OUTPUT/objects/haiku/$ARCH/packaging/packages/haiku.hpkg
|
||||
package extract -C $PACKAGE_ROOT $OUTPUT/objects/haiku/$ARCH/packaging/packages/haiku_devel.hpkg
|
||||
find $OUTPUT/download/ -name '*.hpkg' -exec package extract -C $PACKAGE_ROOT {} \;
|
||||
|
||||
# Fix libgcc_s so we can link to it
|
||||
cd $PACKAGE_ROOT/develop/lib
|
||||
ln -s ../../lib/libgcc_s.so libgcc_s.so
|
||||
|
||||
# Clean up
|
||||
rm -rf $BUILDTOOLS $HAIKU $OUTPUT/Jamfile $OUTPUT/attributes $OUTPUT/build \
|
||||
$OUTPUT/build_packages $OUTPUT/download $OUTPUT/objects
|
18
src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh
Executable file
18
src/ci/docker/disabled/dist-x86_64-haiku/fetch-packages.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
wget http://packages.haiku-os.org/haikuports/master/hpkg/llvm-4.0.1-2-x86_64.hpkg
|
||||
wget http://packages.haiku-os.org/haikuports/master/hpkg/llvm_libs-4.0.1-2-x86_64.hpkg
|
||||
|
||||
package extract -C /system llvm-4.0.1-2-x86_64.hpkg
|
||||
package extract -C /system llvm_libs-4.0.1-2-x86_64.hpkg
|
||||
|
||||
rm -f *.hpkg
|
67
src/ci/docker/disabled/dist-x86_64-haiku/llvm-config.sh
Executable file
67
src/ci/docker/disabled/dist-x86_64-haiku/llvm-config.sh
Executable file
@ -0,0 +1,67 @@
|
||||
#!/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.
|
||||
|
||||
case $1 in
|
||||
--version) echo 4.0.1;;
|
||||
--prefix) echo $SCRATCH/haiku-cross/sysroot/boot/system;;
|
||||
--bindir) echo $SCRATCH/haiku-cross/sysroot/boot/system/bin;;
|
||||
--includedir) echo $SCRATCH/haiku-cross/sysroot/boot/system/develop/headers;;
|
||||
--libdir) echo $SCRATCH/haiku-/cross/sysroot/boot/system/develop/lib;;
|
||||
--cmakedir) echo $SCRATCH/haiku-/cross/sysroot/boot/system/develop/lib/cmake/llvm;;
|
||||
--cppflags) echo -I$SCRATCH/haiku-/cross/sysroot/boot/system/develop/headers \
|
||||
-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS;;
|
||||
--cflags) echo -I$SCRATCH/haiku-cross/sysroot/boot/system/develop/headers \
|
||||
-fPIC -Wall -W -Wno-unused-parameter -Wwrite-strings \
|
||||
-Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-comment \
|
||||
-Werror=date-time -ffunction-sections -fdata-sections -O3 -DNDEBUG \
|
||||
-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS;;
|
||||
--cxxflags) echo -I/$SCRATCH/haiku-cross/sysroot/boot/system/develop/headers \
|
||||
-fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter \
|
||||
-Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic \
|
||||
-Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor \
|
||||
-Wno-comment -Werror=date-time -std=c++11 -ffunction-sections \
|
||||
-fdata-sections -O3 -DNDEBUG -fno-exceptions \
|
||||
-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS;;
|
||||
--ldflags) echo -L$SCRATCH/haiku-cross/sysroot/boot/system/develop/lib ;;
|
||||
--system-libs) echo ;;
|
||||
--libs) echo -lLLVM-4.0;;
|
||||
--libfiles) echo $SCRATCH/haiku-cross/sysroot/boot/system/develop/lib/libLLVM-4.0.so;;
|
||||
--components) echo aarch64 aarch64asmparser aarch64asmprinter aarch64codegen \
|
||||
aarch64desc aarch64disassembler aarch64info aarch64utils all \
|
||||
all-targets amdgpu amdgpuasmparser amdgpuasmprinter amdgpucodegen \
|
||||
amdgpudesc amdgpudisassembler amdgpuinfo amdgpuutils analysis arm \
|
||||
armasmparser armasmprinter armcodegen armdesc armdisassembler \
|
||||
arminfo asmparser asmprinter bitreader bitwriter bpf bpfasmprinter \
|
||||
bpfcodegen bpfdesc bpfdisassembler bpfinfo codegen core coroutines \
|
||||
coverage debuginfocodeview debuginfodwarf debuginfomsf debuginfopdb \
|
||||
demangle engine executionengine globalisel hexagon hexagonasmparser \
|
||||
hexagoncodegen hexagondesc hexagondisassembler hexagoninfo \
|
||||
instcombine instrumentation interpreter ipo irreader lanai \
|
||||
lanaiasmparser lanaicodegen lanaidesc lanaidisassembler lanaiinfo \
|
||||
lanaiinstprinter libdriver lineeditor linker lto mc mcdisassembler \
|
||||
mcjit mcparser mips mipsasmparser mipsasmprinter mipscodegen \
|
||||
mipsdesc mipsdisassembler mipsinfo mirparser msp430 msp430asmprinter \
|
||||
msp430codegen msp430desc msp430info native nativecodegen nvptx \
|
||||
nvptxasmprinter nvptxcodegen nvptxdesc nvptxinfo objcarcopts object \
|
||||
objectyaml option orcjit passes powerpc powerpcasmparser \
|
||||
powerpcasmprinter powerpccodegen powerpcdesc powerpcdisassembler \
|
||||
powerpcinfo profiledata riscv riscvcodegen riscvdesc riscvinfo \
|
||||
runtimedyld scalaropts selectiondag sparc sparcasmparser \
|
||||
sparcasmprinter sparccodegen sparcdesc sparcdisassembler sparcinfo \
|
||||
support symbolize systemz systemzasmparser systemzasmprinter \
|
||||
systemzcodegen systemzdesc systemzdisassembler systemzinfo tablegen \
|
||||
target transformutils vectorize x86 x86asmparser x86asmprinter \
|
||||
x86codegen x86desc x86disassembler x86info x86utils xcore \
|
||||
xcoreasmprinter xcorecodegen xcoredesc xcoredisassembler xcoreinfo;;
|
||||
--host-target) echo x86_64-unknown-haiku;;
|
||||
--has-rtti) echo YES;;
|
||||
--shared-mode) echo shared;;
|
||||
esac
|
@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
||||
# emscripten
|
||||
COPY scripts/emscripten-wasm.sh /scripts/
|
||||
COPY disabled/wasm32-exp/node.sh /usr/local/bin/node
|
||||
COPY wasm32-exp/node.sh /usr/local/bin/node
|
||||
RUN bash /scripts/emscripten-wasm.sh
|
||||
|
||||
# cache
|
||||
|
@ -6,9 +6,9 @@ RUN sh /scripts/android-base-apt-get.sh
|
||||
# ndk
|
||||
COPY scripts/android-ndk.sh /scripts/
|
||||
RUN . /scripts/android-ndk.sh && \
|
||||
download_ndk android-ndk-r13b-linux-x86_64.zip && \
|
||||
make_standalone_toolchain arm 9 && \
|
||||
make_standalone_toolchain x86 9 && \
|
||||
download_ndk android-ndk-r15c-linux-x86_64.zip && \
|
||||
make_standalone_toolchain arm 14 && \
|
||||
make_standalone_toolchain x86 14 && \
|
||||
make_standalone_toolchain arm64 21 && \
|
||||
make_standalone_toolchain x86_64 21 && \
|
||||
remove_ndk
|
||||
@ -23,9 +23,9 @@ ENV TARGETS=$TARGETS,x86_64-linux-android
|
||||
ENV RUST_CONFIGURE_ARGS \
|
||||
--target=$TARGETS \
|
||||
--enable-extended \
|
||||
--arm-linux-androideabi-ndk=/android/ndk/arm-9 \
|
||||
--armv7-linux-androideabi-ndk=/android/ndk/arm-9 \
|
||||
--i686-linux-android-ndk=/android/ndk/x86-9 \
|
||||
--arm-linux-androideabi-ndk=/android/ndk/arm-14 \
|
||||
--armv7-linux-androideabi-ndk=/android/ndk/arm-14 \
|
||||
--i686-linux-android-ndk=/android/ndk/x86-14 \
|
||||
--aarch64-linux-android-ndk=/android/ndk/arm64-21 \
|
||||
--x86_64-linux-android-ndk=/android/ndk/x86_64-21
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ \
|
||||
make \
|
||||
ninja-build \
|
||||
file \
|
||||
curl \
|
||||
ca-certificates \
|
||||
python2.7-dev \
|
||||
git \
|
||||
sudo \
|
||||
bzip2 \
|
||||
xz-utils \
|
||||
swig \
|
||||
libedit-dev \
|
||||
libncurses5-dev \
|
||||
patch
|
||||
|
||||
RUN curl -L https://cmake.org/files/v3.8/cmake-3.8.0-rc1-Linux-x86_64.tar.gz | \
|
||||
tar xzf - -C /usr/local --strip-components=1
|
||||
|
||||
WORKDIR /tmp
|
||||
COPY dist-fuchsia/shared.sh dist-fuchsia/build-toolchain.sh dist-fuchsia/compiler-rt-dso-handle.patch /tmp/
|
||||
RUN /tmp/build-toolchain.sh
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV \
|
||||
AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \
|
||||
CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \
|
||||
CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \
|
||||
AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \
|
||||
CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \
|
||||
CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++
|
||||
|
||||
ENV TARGETS=x86_64-unknown-fuchsia
|
||||
ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended
|
||||
ENV SCRIPT python2.7 ../x.py dist --target $TARGETS
|
@ -1,126 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution and at
|
||||
# http://rust-lang.org/COPYRIGHT.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
# ignore-tidy-linelength
|
||||
|
||||
set -ex
|
||||
source shared.sh
|
||||
|
||||
# Download sources
|
||||
SRCS=(
|
||||
"https://fuchsia.googlesource.com/magenta magenta d17073dc8de344ead3b65e8cc6a12280dec38c84"
|
||||
"https://llvm.googlesource.com/llvm llvm 3f58a16d8eec385e2b3ebdfbb84ff9d3bf27e025"
|
||||
"https://llvm.googlesource.com/clang llvm/tools/clang 727ea63e6e82677f6e10e05e08bc7d6bdbae3111"
|
||||
"https://llvm.googlesource.com/lld llvm/tools/lld a31286c1366e5e89b8872803fded13805a1a084b"
|
||||
"https://llvm.googlesource.com/lldb llvm/tools/lldb 0b2384abec4cb99ad66687712e07dee4dd9d187e"
|
||||
"https://llvm.googlesource.com/compiler-rt llvm/runtimes/compiler-rt 9093a35c599fe41278606a20b51095ea8bd5a081"
|
||||
"https://llvm.googlesource.com/libcxx llvm/runtimes/libcxx 607e0c71ec4f7fd377ad3f6c47b08dbe89f66eaa"
|
||||
"https://llvm.googlesource.com/libcxxabi llvm/runtimes/libcxxabi 0a3a1a8a5ca5ef69e0f6b7d5b9d13e63e6fd2c19"
|
||||
"https://llvm.googlesource.com/libunwind llvm/runtimes/libunwind e128003563d99d9ee62247c4cee40f07d21c03e3"
|
||||
)
|
||||
|
||||
fetch() {
|
||||
mkdir -p $2
|
||||
pushd $2 > /dev/null
|
||||
git init
|
||||
git remote add origin $1
|
||||
git fetch --depth=1 origin $3
|
||||
git reset --hard FETCH_HEAD
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
for i in "${SRCS[@]}"; do
|
||||
fetch $i
|
||||
done
|
||||
|
||||
# Remove this once https://reviews.llvm.org/D28791 is resolved
|
||||
cd llvm/runtimes/compiler-rt
|
||||
patch -Np1 < /tmp/compiler-rt-dso-handle.patch
|
||||
cd ../../..
|
||||
|
||||
# Build toolchain
|
||||
cd llvm
|
||||
mkdir build
|
||||
cd build
|
||||
hide_output cmake -GNinja \
|
||||
-DFUCHSIA_SYSROOT=${PWD}/../../magenta/third_party/ulib/musl \
|
||||
-DLLVM_ENABLE_LTO=OFF \
|
||||
-DCLANG_BOOTSTRAP_PASSTHROUGH=LLVM_ENABLE_LTO \
|
||||
-C ../tools/clang/cmake/caches/Fuchsia.cmake \
|
||||
..
|
||||
hide_output ninja stage2-distribution
|
||||
hide_output ninja stage2-install-distribution
|
||||
cd ../..
|
||||
|
||||
# Build sysroot
|
||||
rm -rf llvm/runtimes/compiler-rt
|
||||
./magenta/scripts/download-toolchain
|
||||
|
||||
build_sysroot() {
|
||||
local arch="$1"
|
||||
|
||||
case "${arch}" in
|
||||
x86_64) tgt="magenta-pc-x86-64" ;;
|
||||
aarch64) tgt="magenta-qemu-arm64" ;;
|
||||
esac
|
||||
|
||||
hide_output make -C magenta -j$(getconf _NPROCESSORS_ONLN) $tgt
|
||||
dst=/usr/local/${arch}-unknown-fuchsia
|
||||
mkdir -p $dst
|
||||
cp -r magenta/build-${tgt}/sysroot/include $dst/
|
||||
cp -r magenta/build-${tgt}/sysroot/lib $dst/
|
||||
|
||||
cd llvm
|
||||
mkdir build-runtimes-${arch}
|
||||
cd build-runtimes-${arch}
|
||||
hide_output cmake -GNinja \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_AR=/usr/local/bin/llvm-ar \
|
||||
-DCMAKE_RANLIB=/usr/local/bin/llvm-ranlib \
|
||||
-DCMAKE_INSTALL_PREFIX= \
|
||||
-DLLVM_MAIN_SRC_DIR=${PWD}/.. \
|
||||
-DLLVM_BINARY_DIR=${PWD}/../build \
|
||||
-DLLVM_ENABLE_WERROR=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_INCLUDE_TESTS=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Fuchsia \
|
||||
-DCMAKE_C_COMPILER_TARGET=${arch}-fuchsia \
|
||||
-DCMAKE_CXX_COMPILER_TARGET=${arch}-fuchsia \
|
||||
-DUNIX=1 \
|
||||
-DLIBCXX_HAS_MUSL_LIBC=ON \
|
||||
-DLIBCXXABI_USE_LLVM_UNWINDER=ON \
|
||||
-DCMAKE_SYSROOT=${dst} \
|
||||
-DCMAKE_C_COMPILER_FORCED=TRUE \
|
||||
-DCMAKE_CXX_COMPILER_FORCED=TRUE \
|
||||
-DLLVM_ENABLE_LIBCXX=ON \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-nodefaultlibs -lc" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="$(clang --target=${arch}-fuchsia -print-libgcc-file-name)" \
|
||||
../runtimes
|
||||
hide_output env DESTDIR="${dst}" ninja install
|
||||
cd ../..
|
||||
}
|
||||
|
||||
build_sysroot "x86_64"
|
||||
build_sysroot "aarch64"
|
||||
|
||||
rm -rf magenta llvm
|
||||
|
||||
for arch in x86_64 aarch64; do
|
||||
for tool in clang clang++; do
|
||||
cat >/usr/local/bin/${arch}-unknown-fuchsia-${tool} <<EOF
|
||||
#!/bin/sh
|
||||
${tool} --target=${arch}-unknown-fuchsia --sysroot=/usr/local/${arch}-unknown-fuchsia "\$@"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/${arch}-unknown-fuchsia-${tool}
|
||||
done
|
||||
ln -s /usr/local/bin/llvm-ar /usr/local/bin/${arch}-unknown-fuchsia-ar
|
||||
done
|
@ -1,41 +0,0 @@
|
||||
diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt
|
||||
index fc4384af2..b442264c0 100644
|
||||
--- a/lib/builtins/CMakeLists.txt
|
||||
+++ b/lib/builtins/CMakeLists.txt
|
||||
@@ -194,6 +194,12 @@ if(APPLE)
|
||||
atomic_thread_fence.c)
|
||||
endif()
|
||||
|
||||
+if(FUCHSIA)
|
||||
+ set(GENERIC_SOURCES
|
||||
+ ${GENERIC_SOURCES}
|
||||
+ dso_handle.c)
|
||||
+endif()
|
||||
+
|
||||
if(NOT WIN32 OR MINGW)
|
||||
set(GENERIC_SOURCES
|
||||
${GENERIC_SOURCES}
|
||||
diff --git a/lib/builtins/dso_handle.c b/lib/builtins/dso_handle.c
|
||||
new file mode 100644
|
||||
index 000000000..7766cd0aa
|
||||
--- /dev/null
|
||||
+++ b/lib/builtins/dso_handle.c
|
||||
@@ -0,0 +1,18 @@
|
||||
+/* ===-- dso_handle.c - Provide __dso_handle -------------------------------===
|
||||
+ *
|
||||
+ * The LLVM Compiler Infrastructure
|
||||
+ *
|
||||
+ * This file is dual licensed under the MIT and the University of Illinois Open
|
||||
+ * Source Licenses. See LICENSE.TXT for details.
|
||||
+ *
|
||||
+ * ===----------------------------------------------------------------------===
|
||||
+ */
|
||||
+
|
||||
+/* __dso_handle symbol is mandated by C++ ABI with a value which is an address
|
||||
+ * in one of the object's segments, and as such this symbol has to be included
|
||||
+ * statically and cannot be a part of a shared library. Traditionally, it has
|
||||
+ * been defined in crtbegin.o but there's no principled reason for it to be
|
||||
+ * there. We defined this symbol in the builtin library which is built as a
|
||||
+ * static library and always included in the final link.
|
||||
+ */
|
||||
+__attribute__((visibility("hidden"))) void *const __dso_handle;
|
@ -23,7 +23,7 @@ SYSROOT=/usr/local/$TARGET/sysroot
|
||||
mkdir -p $SYSROOT
|
||||
pushd $SYSROOT
|
||||
|
||||
centos_base=http://mirror.centos.org/altarch/7.3.1611/os/ppc64le/Packages
|
||||
centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages
|
||||
glibc_v=2.17-157.el7
|
||||
kernel_v=3.10.0-514.el7
|
||||
for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do
|
||||
|
@ -36,12 +36,14 @@ elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
|
||||
echo Cannot run disabled images on travis!
|
||||
exit 1
|
||||
fi
|
||||
retry docker \
|
||||
# retry messes with the pipe from tar to docker. Not needed on non-travis
|
||||
# Transform changes the context of disabled Dockerfiles to match the enabled ones
|
||||
tar --transform 's#^./disabled/#./#' -C $docker_dir -c . | docker \
|
||||
build \
|
||||
--rm \
|
||||
-t rust-ci \
|
||||
-f "$docker_dir/disabled/$image/Dockerfile" \
|
||||
"$docker_dir"
|
||||
-f "$image/Dockerfile" \
|
||||
-
|
||||
else
|
||||
echo Invalid image: $image
|
||||
exit 1
|
||||
|
@ -10,40 +10,40 @@
|
||||
|
||||
set -ex
|
||||
|
||||
URL=https://dl.google.com/android/repository
|
||||
export ANDROID_HOME=/android/sdk
|
||||
PATH=$PATH:"${ANDROID_HOME}/tools/bin"
|
||||
|
||||
download_sdk() {
|
||||
mkdir -p /android/sdk
|
||||
cd /android/sdk
|
||||
curl -fO $URL/$1
|
||||
unzip -q $1
|
||||
rm -rf $1
|
||||
mkdir -p /android
|
||||
curl -fo sdk.zip "https://dl.google.com/android/repository/sdk-tools-linux-$1.zip"
|
||||
unzip -q sdk.zip -d "$ANDROID_HOME"
|
||||
rm -f sdk.zip
|
||||
}
|
||||
|
||||
download_sysimage() {
|
||||
# See https://developer.android.com/studio/tools/help/android.html
|
||||
abi=$1
|
||||
api=$2
|
||||
|
||||
filter="platform-tools,android-$api"
|
||||
filter="$filter,sys-img-$abi-android-$api"
|
||||
|
||||
# Keep printing yes to accept the licenses
|
||||
while true; do echo yes; sleep 10; done | \
|
||||
/android/sdk/tools/android update sdk -a --no-ui \
|
||||
--filter "$filter"
|
||||
# See https://developer.android.com/studio/command-line/sdkmanager.html for
|
||||
# usage of `sdkmanager`.
|
||||
#
|
||||
# The output from sdkmanager is so noisy that it will occupy all of the 4 MB
|
||||
# log extremely quickly. Thus we must silence all output.
|
||||
yes | sdkmanager --licenses > /dev/null
|
||||
sdkmanager platform-tools emulator \
|
||||
"platforms;android-$api" \
|
||||
"system-images;android-$api;default;$abi" > /dev/null
|
||||
}
|
||||
|
||||
create_avd() {
|
||||
# See https://developer.android.com/studio/tools/help/android.html
|
||||
abi=$1
|
||||
api=$2
|
||||
|
||||
echo no | \
|
||||
/android/sdk/tools/android create avd \
|
||||
--name $abi-$api \
|
||||
--target android-$api \
|
||||
--abi $abi
|
||||
# See https://developer.android.com/studio/command-line/avdmanager.html for
|
||||
# usage of `avdmanager`.
|
||||
echo no | avdmanager create avd \
|
||||
-n "$abi-$api" \
|
||||
-k "system-images;android-$api;default;$abi"
|
||||
}
|
||||
|
||||
download_and_create_avd() {
|
||||
@ -51,3 +51,15 @@ download_and_create_avd() {
|
||||
download_sysimage $2 $3
|
||||
create_avd $2 $3
|
||||
}
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# setup_android_sdk 4333796 armeabi-v7a 18
|
||||
#
|
||||
# 4333796 =>
|
||||
# SDK tool version.
|
||||
# Copy from https://developer.android.com/studio/index.html#command-tools
|
||||
# armeabi-v7a =>
|
||||
# System image ABI
|
||||
# 18 =>
|
||||
# Android API Level (18 = Android 4.3 = Jelly Bean MR2)
|
||||
|
@ -17,5 +17,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-test-miri
|
||||
ENV RUST_CHECK_TARGET check-aux
|
||||
|
@ -18,6 +18,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
|
||||
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
|
||||
ENV SCRIPT python2.7 ../x.py test distcheck
|
||||
ENV DIST_SRC 1
|
||||
|
@ -52,7 +52,11 @@ if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
|
||||
fi
|
||||
else
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
|
||||
# We almost always want debug assertions enabled, but sometimes this takes too
|
||||
# long for too little benefit, so we just turn them off.
|
||||
if [ "$NO_DEBUG_ASSERTIONS" = "" ]; then
|
||||
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
|
||||
fi
|
||||
|
||||
# In general we always want to run tests with LLVM assertions enabled, but not
|
||||
# all platforms currently support that, so we have an option to disable.
|
||||
|
@ -1,4 +1,5 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: rust
|
||||
cache: cargo
|
||||
rust:
|
||||
|
@ -19,12 +19,14 @@ starting with the second edition.
|
||||
|
||||
## Requirements
|
||||
|
||||
Building the book requires [mdBook] >= v0.0.13. To get it:
|
||||
Building the book requires [mdBook], ideally the same version that
|
||||
[rust-lang/rust uses in this file][rust-mdbook]. To get it:
|
||||
|
||||
[mdBook]: https://github.com/azerupi/mdBook
|
||||
[rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml
|
||||
|
||||
```bash
|
||||
$ cargo install mdbook
|
||||
$ cargo install mdbook --vers [version-num]
|
||||
```
|
||||
|
||||
## Building
|
||||
|
@ -9,13 +9,16 @@ aggregator
|
||||
AGraph
|
||||
aliasability
|
||||
alignof
|
||||
allocator
|
||||
Amir
|
||||
anotherusername
|
||||
APIs
|
||||
app's
|
||||
aren
|
||||
args
|
||||
associativity
|
||||
async
|
||||
atomics
|
||||
AveragedCollection
|
||||
backend
|
||||
backtrace
|
||||
@ -50,6 +53,8 @@ ChangeColorMessage
|
||||
charset
|
||||
chXX
|
||||
chYY
|
||||
coercions
|
||||
combinator
|
||||
ConcreteType
|
||||
config
|
||||
Config
|
||||
@ -57,6 +62,7 @@ const
|
||||
constant's
|
||||
copyeditor
|
||||
couldn
|
||||
CPUs
|
||||
cratesio
|
||||
CRLF
|
||||
cryptographically
|
||||
@ -64,11 +70,15 @@ CStr
|
||||
CString
|
||||
ctrl
|
||||
Ctrl
|
||||
customizable
|
||||
CustomSmartPointer
|
||||
CustomSmartPointers
|
||||
deallocate
|
||||
deallocated
|
||||
deallocating
|
||||
deallocation
|
||||
debuginfo
|
||||
decrementing
|
||||
deps
|
||||
deref
|
||||
Deref
|
||||
@ -84,12 +94,14 @@ destructured
|
||||
destructures
|
||||
destructuring
|
||||
Destructuring
|
||||
deterministically
|
||||
didn
|
||||
Dobrý
|
||||
doccargo
|
||||
doccratesio
|
||||
DOCTYPE
|
||||
doesn
|
||||
disambiguating
|
||||
DraftPost
|
||||
DSTs
|
||||
ebooks
|
||||
@ -104,6 +116,7 @@ enums
|
||||
enum's
|
||||
Enums
|
||||
eprintln
|
||||
Erlang
|
||||
ErrorKind
|
||||
Executables
|
||||
extern
|
||||
@ -124,6 +137,7 @@ FnOnce
|
||||
formatter
|
||||
FromIterator
|
||||
frontend
|
||||
getter
|
||||
GGraph
|
||||
GitHub
|
||||
gitignore
|
||||
@ -149,7 +163,10 @@ html
|
||||
Iceburgh
|
||||
IEEE
|
||||
impl
|
||||
implementor
|
||||
implementors
|
||||
ImportantExcerpt
|
||||
incrementing
|
||||
indices
|
||||
init
|
||||
inline
|
||||
@ -157,6 +174,7 @@ instantiation
|
||||
internet
|
||||
IntoIterator
|
||||
InvalidDigit
|
||||
invariants
|
||||
ioerror
|
||||
iokind
|
||||
ioresult
|
||||
@ -180,6 +198,7 @@ librarys
|
||||
libreoffice
|
||||
libstd
|
||||
lifecycle
|
||||
LimitTracker
|
||||
lobally
|
||||
locators
|
||||
login
|
||||
@ -197,7 +216,9 @@ Mibbit
|
||||
minigrep
|
||||
mixup
|
||||
mkdir
|
||||
MockMessenger
|
||||
modifiability
|
||||
modularity
|
||||
monomorphization
|
||||
Monomorphization
|
||||
monomorphized
|
||||
@ -206,10 +227,12 @@ Mozilla
|
||||
mpsc
|
||||
multithreaded
|
||||
mutex
|
||||
mutex's
|
||||
Mutex
|
||||
mutexes
|
||||
Mutexes
|
||||
MutexGuard
|
||||
MyBox
|
||||
namespace
|
||||
namespaced
|
||||
namespaces
|
||||
@ -230,6 +253,7 @@ OCaml
|
||||
offsetof
|
||||
online
|
||||
OpenGL
|
||||
optimizations
|
||||
OptionalFloatingPointNumber
|
||||
OptionalNumber
|
||||
OsStr
|
||||
@ -245,6 +269,7 @@ PartialOrd
|
||||
PendingReview
|
||||
PendingReviewPost
|
||||
PlaceholderType
|
||||
polymorphism
|
||||
PoolCreationError
|
||||
portia
|
||||
powershell
|
||||
@ -277,12 +302,15 @@ RefMut
|
||||
refutability
|
||||
reimplement
|
||||
repr
|
||||
representable
|
||||
request's
|
||||
resizes
|
||||
resizing
|
||||
retweet
|
||||
rewordings
|
||||
rint
|
||||
ripgrep
|
||||
runnable
|
||||
runtime
|
||||
runtimes
|
||||
Rustacean
|
||||
@ -301,6 +329,7 @@ shouldn
|
||||
Simula
|
||||
situps
|
||||
sizeof
|
||||
Smalltalk
|
||||
someproject
|
||||
someusername
|
||||
SPDX
|
||||
@ -322,6 +351,7 @@ Struct
|
||||
structs
|
||||
struct's
|
||||
Structs
|
||||
subclasses
|
||||
subcommand
|
||||
subcommands
|
||||
subdirectories
|
||||
@ -364,9 +394,11 @@ unary
|
||||
Unary
|
||||
uncomment
|
||||
Uncomment
|
||||
unevaluated
|
||||
Uninstalling
|
||||
uninstall
|
||||
unix
|
||||
unpopulated
|
||||
unoptimized
|
||||
UnsafeCell
|
||||
unsafety
|
||||
|
@ -20,7 +20,7 @@ game will print congratulations and exit.
|
||||
To set up a new project, go to the *projects* directory that you created in
|
||||
Chapter 1, and make a new project using Cargo, like so:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo new guessing_game --bin
|
||||
$ cd guessing_game
|
||||
```
|
||||
@ -34,7 +34,7 @@ Look at the generated *Cargo.toml* file:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```toml
|
||||
```
|
||||
[package]
|
||||
name = "guessing_game"
|
||||
version = "0.1.0"
|
||||
@ -51,7 +51,7 @@ you. Check out the *src/main.rs* file:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
@ -60,9 +60,10 @@ fn main() {
|
||||
Now let’s compile this “Hello, world!” program and run it in the same step
|
||||
using the `cargo run` command:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Hello, world!
|
||||
```
|
||||
@ -81,7 +82,7 @@ to input a guess. Enter the code in Listing 2-1 into *src/main.rs*.
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
@ -98,51 +99,51 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 2-1: Code to get a guess from the user and print it out
|
||||
</caption>
|
||||
Listing 2-1: Code to get a guess from the user and print
|
||||
it out
|
||||
|
||||
This code contains a lot of information, so let’s go over it bit by bit. To
|
||||
obtain user input and then print the result as output, we need to import the
|
||||
`io` (input/output) library from the standard library (which is known as `std`):
|
||||
obtain user input and then print the result as output, we need to bring the
|
||||
`io` (input/output) library into scope. The `io` library comes from the
|
||||
standard library (which is known as `std`):
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
use std::io;
|
||||
```
|
||||
|
||||
By default, Rust imports only a few types into every program in the
|
||||
*prelude*. If a type you want to use isn’t in the
|
||||
prelude, you have to import that type into your program explicitly with a `use`
|
||||
By default, Rust brings only a few types into the scope of every program in
|
||||
the *prelude*. If a type you want to use isn’t in the
|
||||
prelude, you have to bring that type into scope explicitly with a `use`
|
||||
statement. Using the `std::io` library provides you with a number of useful
|
||||
`io`-related features, including the functionality to accept user input.
|
||||
|
||||
As you saw in Chapter 1, the `main` function is the entry point into the
|
||||
program:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
```
|
||||
|
||||
The `fn` syntax declares a new function, the `()` indicate there are no
|
||||
arguments, and `{` starts the body of the function.
|
||||
parameters, and `{` starts the body of the function.
|
||||
|
||||
As you also learned in Chapter 1, `println!` is a macro that prints a string to
|
||||
the screen:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
println!("Guess the number!");
|
||||
|
||||
println!("Please input your guess.");
|
||||
```
|
||||
|
||||
This code is just printing a prompt stating what the game is and requesting
|
||||
input from the user.
|
||||
This code is printing a prompt stating what the game is and requesting input
|
||||
from the user.
|
||||
|
||||
### Storing Values with Variables
|
||||
|
||||
Next, we’ll create a place to store the user input, like this:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let mut guess = String::new();
|
||||
```
|
||||
|
||||
@ -150,7 +151,7 @@ Now the program is getting interesting! There’s a lot going on in this little
|
||||
line. Notice that this is a `let` statement, which is used to create
|
||||
*variables*. Here’s another example:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let foo = bar;
|
||||
```
|
||||
|
||||
@ -158,7 +159,7 @@ This line will create a new variable named `foo` and bind it to the value
|
||||
`bar`. In Rust, variables are immutable by default. The following example shows
|
||||
how to use `mut` before the variable name to make a variable mutable:
|
||||
|
||||
```rust
|
||||
```
|
||||
let foo = 5; // immutable
|
||||
let mut bar = 5; // mutable
|
||||
```
|
||||
@ -189,7 +190,7 @@ Recall that we included the input/output functionality from the standard
|
||||
library with `use std::io;` on the first line of the program. Now we’ll call an
|
||||
associated function, `stdin`, on `io`:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
io::stdin().read_line(&mut guess)
|
||||
.expect("Failed to read line");
|
||||
```
|
||||
@ -222,7 +223,7 @@ We’re not quite done with this line of code. Although it’s a single line of
|
||||
text, it’s only the first part of the single logical line of code. The second
|
||||
part is this method:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
.expect("Failed to read line");
|
||||
```
|
||||
|
||||
@ -230,7 +231,7 @@ When you call a method with the `.foo()` syntax, it’s often wise to introduce
|
||||
newline and other whitespace to help break up long lines. We could have
|
||||
written this code as:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
```
|
||||
|
||||
@ -264,32 +265,35 @@ argument to `expect`. If the `read_line` method returns an `Err`, it would
|
||||
likely be the result of an error coming from the underlying operating system.
|
||||
If this instance of `io::Result` is an `Ok` value, `expect` will take the
|
||||
return value that `Ok` is holding and return just that value to you so you
|
||||
could use it. In this case, that value is the number of characters the user
|
||||
could use it. In this case, that value is the number of bytes in what the user
|
||||
entered into standard input.
|
||||
|
||||
If we don’t call `expect`, the program will compile, but we’ll get a warning:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
src/main.rs:10:5: 10:39 warning: unused result which must be used,
|
||||
#[warn(unused_must_use)] on by default
|
||||
src/main.rs:10 io::stdin().read_line(&mut guess);
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
warning: unused `std::result::Result` which must be used
|
||||
--> src/main.rs:10:5
|
||||
|
|
||||
10 | io::stdin().read_line(&mut guess);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: #[warn(unused_must_use)] on by default
|
||||
```
|
||||
|
||||
Rust warns that we haven’t used the `Result` value returned from `read_line`,
|
||||
indicating that the program hasn’t handled a possible error. The right way to
|
||||
suppress the warning is to actually write error handling, but since we just
|
||||
want to crash this program when a problem occurs, we can use `expect`. You’ll
|
||||
learn about recovering from errors in Chapter 9.
|
||||
suppress the warning is to actually write error handling, but since we want to
|
||||
crash this program when a problem occurs, we can use `expect`. You’ll learn
|
||||
about recovering from errors in Chapter 9.
|
||||
|
||||
### Printing Values with `println!` Placeholders
|
||||
|
||||
Aside from the closing curly brace, there’s only one more line to discuss in
|
||||
Aside from the closing curly brackets, there’s only one more line to discuss in
|
||||
the code added so far, which is the following:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
println!("You guessed: {}", guess);
|
||||
```
|
||||
|
||||
@ -299,7 +303,7 @@ using `{}`: the first set of `{}` holds the first value listed after the format
|
||||
string, the second set holds the second value, and so on. Printing out multiple
|
||||
values in one call to `println!` would look like this:
|
||||
|
||||
```rust
|
||||
```
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
|
||||
@ -310,11 +314,13 @@ This code would print out `x = 5 and y = 10`.
|
||||
|
||||
### Testing the First Part
|
||||
|
||||
Let’s test the first part of the guessing game. You can run it using `cargo run`:
|
||||
Let’s test the first part of the guessing game. You can run it using
|
||||
`cargo run`:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
Please input your guess.
|
||||
@ -348,7 +354,7 @@ you:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```toml
|
||||
```
|
||||
[dependencies]
|
||||
|
||||
rand = "0.3.14"
|
||||
@ -367,7 +373,7 @@ version 0.3.14.”
|
||||
Now, without changing any of the code, let’s build the project, as shown in
|
||||
Listing 2-2:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo build
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
Downloading rand v0.3.14
|
||||
@ -375,12 +381,11 @@ $ cargo build
|
||||
Compiling libc v0.2.14
|
||||
Compiling rand v0.3.14
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 2-2: The output from running `cargo build` after adding the rand crate
|
||||
as a dependency
|
||||
</caption>
|
||||
Listing 2-2: The output from running `cargo build` after
|
||||
adding the rand crate as a dependency
|
||||
|
||||
You may see different version numbers (but they will all be compatible with
|
||||
the code, thanks to SemVer!), and the lines may be in a different order.
|
||||
@ -405,7 +410,7 @@ doesn’t recompile that either. With nothing to do, it simply exits. If you ope
|
||||
up the *src/main.rs* file, make a trivial change, then save it and build again,
|
||||
you’ll only see one line of output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
```
|
||||
@ -448,7 +453,7 @@ But by default, Cargo will only look for versions larger than `0.3.0` and
|
||||
smaller than `0.4.0`. If the `rand` crate has released two new versions,
|
||||
`0.3.15` and `0.4.0`, you would see the following if you ran `cargo update`:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo update
|
||||
Updating registry `https://github.com/rust-lang/crates.io-index`
|
||||
Updating rand v0.3.14 -> v0.3.15
|
||||
@ -460,7 +465,7 @@ that the version of the `rand` crate you are now using is `0.3.15`.
|
||||
If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x`
|
||||
series, you’d have to update the *Cargo.toml* file to look like this instead:
|
||||
|
||||
```toml
|
||||
```
|
||||
[dependencies]
|
||||
|
||||
rand = "0.4.0"
|
||||
@ -471,7 +476,7 @@ available and reevaluate your `rand` requirements according to the new version
|
||||
you specified.
|
||||
|
||||
There’s a lot more to say about Cargo and its
|
||||
ecosystem that Chapter XX will discuss, but for
|
||||
ecosystem that Chapter 14 will discuss, but for
|
||||
now, that’s all you need to know. Cargo makes it very easy to reuse libraries,
|
||||
so Rustaceans are able to write smaller projects that are assembled from a
|
||||
number of packages.
|
||||
@ -483,7 +488,7 @@ in Listing 2-3:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -507,9 +512,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 2-3: Code changes needed in order to generate a random number
|
||||
</caption>
|
||||
Listing 2-3: Code changes needed in order to generate a
|
||||
random number
|
||||
|
||||
We’re adding a `extern crate rand;` line to the top that lets Rust know we’ll be
|
||||
using that external dependency. This also does the equivalent of calling `use
|
||||
@ -530,7 +534,7 @@ numbers as arguments and generates a random number between them. It’s inclusiv
|
||||
on the lower bound but exclusive on the upper bound, so we need to specify `1`
|
||||
and `101` to request a number between 1 and 100.
|
||||
|
||||
Knowing which traits to import and which functions and methods to use from a
|
||||
Knowing which traits to use and which functions and methods to call from a
|
||||
crate isn’t something that you’ll just *know*. Instructions for using a crate
|
||||
are in each crate’s documentation. Another neat feature of Cargo is that you
|
||||
can run the `cargo doc --open` command that will build documentation provided
|
||||
@ -545,9 +549,10 @@ the answer as soon as it starts!
|
||||
|
||||
Try running the program a few times:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
@ -573,7 +578,7 @@ step is shown in Listing 2-4:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -604,9 +609,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 2-4: Handling the possible return values of comparing two numbers
|
||||
</caption>
|
||||
Listing 2-4: Handling the possible return values of
|
||||
comparing two numbers
|
||||
|
||||
The first new bit here is another `use`, bringing a type called
|
||||
`std::cmp::Ordering` into scope from the standard library. `Ordering` is
|
||||
@ -616,7 +620,7 @@ compare two values.
|
||||
|
||||
Then we add five new lines at the bottom that use the `Ordering` type:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
@ -627,7 +631,7 @@ match guess.cmp(&secret_number) {
|
||||
The `cmp` method compares two values and can be called on anything that can be
|
||||
compared. It takes a reference to whatever you want to compare with: here it’s
|
||||
comparing the `guess` to the `secret_number`. `cmp` returns a variant of the
|
||||
`Ordering` enum we imported with the `use` statement. We use a
|
||||
`Ordering` enum we brought into scope with the `use` statement. We use a
|
||||
`match` expression to decide what to do next based on
|
||||
which variant of `Ordering` was returned from the call to `cmp` with the values
|
||||
in `guess` and `secret_number`.
|
||||
@ -638,7 +642,7 @@ expression fits that arm’s pattern. Rust takes the value given to `match` and
|
||||
looks through each arm’s pattern in turn. The `match` construct and patterns
|
||||
are powerful features in Rust that let you express a variety of situations your
|
||||
code might encounter and helps ensure that you handle them all. These features
|
||||
will be covered in detail in Chapter 6 and Chapter XX, respectively.
|
||||
will be covered in detail in Chapter 6 and Chapter 18, respectively.
|
||||
|
||||
Let’s walk through an example of what would happen with the `match` expression
|
||||
used here. Say that the user has guessed 50, and the randomly generated secret
|
||||
@ -646,7 +650,7 @@ number this time is 38. When the code compares 50 to 38, the `cmp` method will
|
||||
return `Ordering::Greater`, because 50 is greater than 38. `Ordering::Greater`
|
||||
is the value that the `match` expression gets. It looks at the first arm’s
|
||||
pattern, `Ordering::Less`, but the value `Ordering::Greater` does not match
|
||||
`Ordering::Less`. So it ignores the code in that arm and moves to the next arm.
|
||||
`Ordering::Less`, so it ignores the code in that arm and moves to the next arm.
|
||||
The next arm’s pattern, `Ordering::Greater`, *does* match
|
||||
`Ordering::Greater`! The associated code in that arm will execute and print
|
||||
`Too big!` to the screen. The `match` expression ends because it has no need to
|
||||
@ -654,7 +658,7 @@ look at the last arm in this particular scenario.
|
||||
|
||||
However, the code in Listing 2-4 won’t compile yet. Let’s try it:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
error[E0308]: mismatched types
|
||||
@ -687,7 +691,7 @@ that by adding the following two lines to the `main` function body:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -723,7 +727,7 @@ fn main() {
|
||||
|
||||
The two new lines are:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let guess: u32 = guess.trim().parse()
|
||||
.expect("Please type a number!");
|
||||
```
|
||||
@ -740,11 +744,12 @@ We bind `guess` to the expression `guess.trim().parse()`. The `guess` in the
|
||||
expression refers to the original `guess` that was a `String` with the input in
|
||||
it. The `trim` method on a `String` instance will eliminate any whitespace at
|
||||
the beginning and end. `u32` can only contain numerical characters, but the
|
||||
user must press the Return key to satisfy `read_line`. When the user presses
|
||||
Return, a newline character is added to the string. For example, if the user
|
||||
types 5 and presses return, `guess` looks like this: `5\n`. The `\n` represents
|
||||
“newline,” the return key. The `trim` method eliminates `\n`, resulting in just
|
||||
`5`.
|
||||
user must press the <span class="keystroke">enter</span> key to satisfy
|
||||
`read_line`. When the user presses <span class="keystroke">enter</span>, a
|
||||
newline character is added to the string. For example, if the user types <span
|
||||
class="keystroke">5</span> and presses <span class="keystroke"> enter</span>,
|
||||
`guess` looks like this: `5\n`. The `\n` represents “newline,” the enter key.
|
||||
The `trim` method eliminates `\n`, resulting in just `5`.
|
||||
|
||||
The `parse` method on strings parses a string into some
|
||||
kind of number. Because this method can parse a variety of number types, we
|
||||
@ -761,7 +766,7 @@ The call to `parse` could easily cause an error. If, for example, the string
|
||||
contained `A👍%`, there would be no way to convert that to a number. Because it
|
||||
might fail, the `parse` method returns a `Result` type, much like the
|
||||
`read_line` method does as discussed earlier in “Handling Potential Failure
|
||||
with the Result Type” on page XX. We’ll treat this `Result` the same way by
|
||||
with the Result Type”. We’ll treat this `Result` the same way by
|
||||
using the `expect` method again. If `parse` returns an `Err` `Result` variant
|
||||
because it couldn’t create a number from the string, the `expect` call will
|
||||
crash the game and print the message we give it. If `parse` can successfully
|
||||
@ -770,9 +775,10 @@ and `expect` will return the number that we want from the `Ok` value.
|
||||
|
||||
Let’s run the program now!
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
@ -797,7 +803,7 @@ chances at guessing the number:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -839,13 +845,13 @@ program again. Notice that there is a new problem because the program is doing
|
||||
exactly what we told it to do: ask for another guess forever! It doesn’t seem
|
||||
like the user can quit!
|
||||
|
||||
The user could always halt the program by using the keyboard shortcut `Ctrl-C`.
|
||||
But there’s another way to escape this insatiable monster that we mentioned in
|
||||
the `parse` discussion in “Comparing the Guesses” on page XX: if the user
|
||||
enters a non-number answer, the program will crash. The user can take advantage
|
||||
of that in order to quit, as shown here:
|
||||
The user could always halt the program by using the keyboard shortcut
|
||||
<span class="keystroke">ctrl-C</span>. But there’s another way to escape this
|
||||
insatiable monster that we mentioned in the `parse` discussion in “Comparing the
|
||||
Guess to the Secret Number”: if the user enters a non-number answer, the program
|
||||
will crash. The user can take advantage of that in order to quit, as shown here:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
@ -880,7 +886,7 @@ Let’s program the game to quit when the user wins by adding a `break`:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -930,7 +936,7 @@ the user inputs a non-number, let’s make the game ignore a non-number so the
|
||||
user can continue guessing. We can do that by altering the line where `guess` is
|
||||
converted from a `String` to a `u32`:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let guess: u32 = match guess.trim().parse() {
|
||||
Ok(num) => num,
|
||||
Err(_) => continue,
|
||||
@ -962,7 +968,7 @@ might encounter!
|
||||
Now everything in the program should work as expected. Let’s try it by running
|
||||
`cargo run`:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Running `target/guessing_game`
|
||||
@ -991,7 +997,7 @@ secret number. Listing 2-5 shows the final code:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
extern crate rand;
|
||||
|
||||
use std::io;
|
||||
@ -1030,9 +1036,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 2-5: Complete code of the guessing game
|
||||
</caption>
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -12,19 +12,15 @@ Specifically, you’ll learn about variables, basic types, functions, comments,
|
||||
and control flow. These foundations will be in every Rust program, and learning
|
||||
them early will give you a strong core to start from.
|
||||
|
||||
PROD: START BOX
|
||||
|
||||
### Keywords
|
||||
|
||||
The Rust language has a set of *keywords* that have been reserved for use by
|
||||
the language only, much like other languages do. Keep in mind that you cannot
|
||||
use these words as names of variables or functions. Most of the keywords have
|
||||
special meanings, and you’ll be using them to do various tasks in your Rust
|
||||
programs; a few have no current functionality associated with them but have
|
||||
been reserved for functionality that might be added to Rust in the future. You
|
||||
can find a list of the keywords in Appendix A.
|
||||
|
||||
PROD: END BOX
|
||||
> ### Keywords
|
||||
>
|
||||
> The Rust language has a set of *keywords* that have been reserved for use by
|
||||
> the language only, much like other languages do. Keep in mind that you cannot
|
||||
> use these words as names of variables or functions. Most of the keywords have
|
||||
> special meanings, and you’ll be using them to do various tasks in your Rust
|
||||
> programs; a few have no current functionality associated with them but have
|
||||
> been reserved for functionality that might be added to Rust in the future. You
|
||||
> can find a list of the keywords in Appendix A.
|
||||
|
||||
## Variables and Mutability
|
||||
|
||||
@ -43,7 +39,7 @@ code with the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
@ -97,7 +93,7 @@ For example, change *src/main.rs* to the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let mut x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
@ -108,9 +104,10 @@ fn main() {
|
||||
|
||||
When we run this program, we get the following:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 5
|
||||
The value of x is: 6
|
||||
@ -161,7 +158,7 @@ const MAX_POINTS: u32 = 100_000;
|
||||
|
||||
Constants are valid for the entire time a program runs, within the scope they
|
||||
were declared in, making them a useful choice for values in your application
|
||||
domain that multiple part of the program might need to know about, such as the
|
||||
domain that multiple parts of the program might need to know about, such as the
|
||||
maximum number of points any player of a game is allowed to earn or the speed
|
||||
of light.
|
||||
|
||||
@ -172,8 +169,8 @@ hardcoded value needed to be updated in the future.
|
||||
|
||||
### Shadowing
|
||||
|
||||
As we saw in the guessing game tutorial in Chapter 2, we can declare new
|
||||
variables with the same name as a previous variables, and the new variable
|
||||
As we saw in the guessing game tutorial in Chapter 2, we can declare a new
|
||||
variable with the same name as a previous variable, and the new variable
|
||||
*shadows* the previous variable. Rustaceans say that the first variable is
|
||||
*shadowed* by the second, which means that the second variable’s value is what
|
||||
we’ll see when we use the variable. We can shadow a variable by using the same
|
||||
@ -181,7 +178,7 @@ variable’s name and repeating the use of the `let` keyword as follows:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
||||
@ -199,9 +196,10 @@ repeating `let x =`, taking the original value and adding `1` so the value of
|
||||
previous value and multiplying it by `2` to give `x` a final value of `12`.
|
||||
When you run this program, it will output the following:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 12
|
||||
```
|
||||
@ -217,7 +215,7 @@ change the type of the value, but reuse the same name. For example, say our
|
||||
program asks a user to show how many spaces they want between some text by
|
||||
inputting space characters, but we really want to store that input as a number:
|
||||
|
||||
```rust
|
||||
```
|
||||
let spaces = " ";
|
||||
let spaces = spaces.len();
|
||||
```
|
||||
@ -229,7 +227,7 @@ from having to come up with different names, like `spaces_str` and
|
||||
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
|
||||
try to use `mut` for this, as shown here:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let mut spaces = " ";
|
||||
spaces = spaces.len();
|
||||
```
|
||||
@ -237,7 +235,7 @@ spaces = spaces.len();
|
||||
we’ll get a compile-time error because we’re not allowed to mutate a variable’s
|
||||
type:
|
||||
|
||||
```bash
|
||||
```
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:3:14
|
||||
|
|
||||
@ -273,14 +271,15 @@ If we don’t add the type annotation here, Rust will display the following
|
||||
error, which means the compiler needs more information from us to know which
|
||||
possible type we want to use:
|
||||
|
||||
```bash
|
||||
error[E0282]: unable to infer enough type information about `_`
|
||||
```
|
||||
error[E0282]: type annotations needed
|
||||
--> src/main.rs:2:9
|
||||
|
|
||||
2 | let guess = "42".parse().expect("Not a number!");
|
||||
| ^^^^^ cannot infer type for `_`
|
||||
|
|
||||
= note: type annotations or generic parameter binding required
|
||||
| ^^^^^
|
||||
| |
|
||||
| cannot infer type for `_`
|
||||
| consider giving `guess` a type
|
||||
```
|
||||
|
||||
You’ll see different type annotations as we discuss the various data types.
|
||||
@ -295,16 +294,14 @@ work in Rust.
|
||||
#### Integer Types
|
||||
|
||||
An *integer* is a number without a fractional component. We used one integer
|
||||
type earlier in this chapter, the `i32` type. This type declaration indicates
|
||||
that the value it’s associated with should be a signed integer (hence the `i`,
|
||||
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1
|
||||
shows the built-in integer types in Rust. Each variant in the Signed and
|
||||
Unsigned columns (for example, *i32*) can be used to declare the type of an
|
||||
type earlier in this chapter, the `u32` type. This type declaration indicates
|
||||
that the value it’s associated with should be an unsigned integer (signed
|
||||
integer types start with `i` instead of `u`) that takes up 32 bits of space.
|
||||
Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
|
||||
and Unsigned columns (for example, *i16*) can be used to declare the type of an
|
||||
integer value.
|
||||
|
||||
<caption>
|
||||
Table 3-1: Integer Types in Rust
|
||||
</caption>
|
||||
|
||||
| Length | Signed | Unsigned |
|
||||
|--------|--------|----------|
|
||||
@ -325,11 +322,11 @@ Signed numbers are stored using two’s complement representation (if you’re
|
||||
unsure what this is, you can search for it online; an explanation is outside
|
||||
the scope of this book).
|
||||
|
||||
Each signed variant can store numbers from -2<sup>n - 1</sup> to 2<sup>n - 1</sup> - 1 inclusive,
|
||||
where `n` is the number of bits that variant uses. So an `i8` can store numbers
|
||||
from -2<sup>7</sup> to 2<sup>7</sup> - 1, which equals -128 to 127. Unsigned variants can store
|
||||
numbers from 0 to 2<sup>n</sup> - 1, so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which
|
||||
equals 0 to 255.
|
||||
Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
|
||||
1</sup> - 1 inclusive, where `n` is the number of bits that variant uses. So an
|
||||
`i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup> - 1, which equals
|
||||
-128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
|
||||
so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
|
||||
|
||||
Additionally, the `isize` and `usize` types depend on the kind of computer your
|
||||
program is running on: 64-bits if you’re on a 64-bit architecture and 32-bits
|
||||
@ -339,9 +336,7 @@ You can write integer literals in any of the forms shown in Table 3-2. Note
|
||||
that all number literals except the byte literal allow a type suffix, such as
|
||||
`57u8`, and `_` as a visual separator, such as `1_000`.
|
||||
|
||||
<caption>
|
||||
Table 3-2: Integer Literals in Rust
|
||||
</caption>
|
||||
|
||||
| Number literals | Example |
|
||||
|------------------|---------------|
|
||||
@ -361,18 +356,14 @@ you’d use `isize` or `usize` is when indexing some sort of collection.
|
||||
Rust also has two primitive types for *floating-point numbers*, which are
|
||||
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
||||
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
||||
because it’s roughly the same speed as `f32` but is capable of more precision.
|
||||
It’s possible to use an `f64` type on 32-bit systems, but it will be slower
|
||||
than using an `f32` type on those systems. Most of the time, trading potential
|
||||
worse performance for better precision is a reasonable initial choice, and you
|
||||
should benchmark your code if you suspect floating-point size is a problem in
|
||||
your situation.
|
||||
because on modern CPUs it’s roughly the same speed as `f32` but is capable of
|
||||
more precision.
|
||||
|
||||
Here’s an example that shows floating-point numbers in action:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let x = 2.0; // f64
|
||||
|
||||
@ -385,13 +376,13 @@ Floating-point numbers are represented according to the IEEE-754 standard. The
|
||||
|
||||
#### Numeric Operations
|
||||
|
||||
Rust supports the usual basic mathematic operations you’d expect for all of the
|
||||
Rust supports the usual basic mathematical operations you’d expect for all of the
|
||||
number types: addition, subtraction, multiplication, division, and remainder.
|
||||
The following code shows how you’d use each one in a `let` statement:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
// addition
|
||||
let sum = 5 + 10;
|
||||
@ -422,7 +413,7 @@ For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let t = true;
|
||||
|
||||
@ -431,18 +422,19 @@ fn main() {
|
||||
```
|
||||
|
||||
The main way to consume boolean values is through conditionals, such as an `if`
|
||||
statement. We’ll cover how `if` statements work in Rust in the “Control Flow”
|
||||
expression. We’ll cover how `if` expressions work in Rust in the “Control Flow”
|
||||
section.
|
||||
|
||||
#### The Character Type
|
||||
|
||||
So far we’ve only worked with numbers, but Rust supports letters too. Rust’s
|
||||
`char` type is the language’s most primitive alphabetic type, and the following
|
||||
code shows one way to use it:
|
||||
code shows one way to use it. Note that the `char` type is specified with
|
||||
single quotes, as opposed to strings that use double quotes:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let c = 'z';
|
||||
let z = 'ℤ';
|
||||
@ -476,7 +468,7 @@ type annotations in this example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let tup: (i32, f64, u8) = (500, 6.4, 1);
|
||||
}
|
||||
@ -488,7 +480,7 @@ use pattern matching to destructure a tuple value, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let tup = (500, 6.4, 1);
|
||||
|
||||
@ -510,7 +502,7 @@ value we want to access. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let x: (i32, f64, u8) = (500, 6.4, 1);
|
||||
|
||||
@ -538,7 +530,7 @@ inside square brackets:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
}
|
||||
@ -557,7 +549,7 @@ program that needs to know the names of the months of the year. It’s very
|
||||
unlikely that such a program will need to add or remove months, so you can use
|
||||
an array because you know it will always contain 12 items:
|
||||
|
||||
```rust
|
||||
```
|
||||
let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
```
|
||||
@ -569,7 +561,7 @@ elements of an array using indexing, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
|
||||
@ -589,7 +581,7 @@ the array? Say we change the example to the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
let index = 10;
|
||||
@ -602,9 +594,10 @@ fn main() {
|
||||
|
||||
Running this code using `cargo run` produces the following result:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling arrays v0.1.0 (file:///projects/arrays)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/arrays`
|
||||
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
||||
10', src/main.rs:6
|
||||
@ -636,7 +629,7 @@ Here’s a program that contains an example function definition:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
@ -649,8 +642,8 @@ fn another_function() {
|
||||
```
|
||||
|
||||
Function definitions in Rust start with `fn` and have a set of parentheses
|
||||
after the function name. The curly braces tell the compiler where the function
|
||||
body begins and ends.
|
||||
after the function name. The curly brackets tell the compiler where the
|
||||
function body begins and ends.
|
||||
|
||||
We can call any function we’ve defined by entering its name followed by a set
|
||||
of parentheses. Because `another_function` is defined in the program, it can be
|
||||
@ -663,9 +656,10 @@ Let’s start a new binary project named *functions* to explore functions
|
||||
further. Place the `another_function` example in *src/main.rs* and run it. You
|
||||
should see the following output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
|
||||
Running `target/debug/functions`
|
||||
Hello, world!
|
||||
Another function.
|
||||
@ -690,7 +684,7 @@ look like in Rust:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
another_function(5);
|
||||
}
|
||||
@ -702,16 +696,17 @@ fn another_function(x: i32) {
|
||||
|
||||
Try running this program; you should get the following output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
The declaration of `another_function` has one parameter named `x`. The type of
|
||||
`x` is specified as `i32`. When `5` is passed to `another_function`, the
|
||||
`println!` macro puts `5` where the pair of curly braces were in the format
|
||||
`println!` macro puts `5` where the pair of curly brackets were in the format
|
||||
string.
|
||||
|
||||
In function signatures, you *must* declare the type of each parameter. This is
|
||||
@ -724,7 +719,7 @@ declarations with commas, like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
another_function(5, 6);
|
||||
}
|
||||
@ -744,9 +739,10 @@ Let’s try running this code. Replace the program currently in your *function*
|
||||
project’s *src/main.rs* file with the preceding example, and run it using
|
||||
`cargo run`:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
The value of y is: 6
|
||||
@ -776,15 +772,13 @@ statement. In Listing 3-3, `let y = 6;` is a statement:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let y = 6;
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 3-3: A `main` function declaration containing one statement.
|
||||
</caption>
|
||||
|
||||
Function definitions are also statements; the entire preceding example is a
|
||||
statement in itself.
|
||||
@ -794,7 +788,7 @@ to another variable, as the following code tries to do:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let x = (let y = 6);
|
||||
}
|
||||
@ -802,7 +796,7 @@ fn main() {
|
||||
|
||||
When you run this program, you’ll get an error like this:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
error: expected expression, found statement (`let`)
|
||||
@ -830,7 +824,7 @@ new scopes, `{}`, is an expression, for example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let x = 5;
|
||||
|
||||
@ -845,7 +839,7 @@ fn main() {
|
||||
|
||||
This expression:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
{
|
||||
let x = 3;
|
||||
x + 1
|
||||
@ -853,23 +847,25 @@ This expression:
|
||||
```
|
||||
|
||||
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
||||
as part of the `let` statement. Note the line without a semicolon at the end,
|
||||
unlike most of the lines you’ve seen so far. Expressions do not include ending
|
||||
semicolons. If you add a semicolon to the end of an expression, you turn it
|
||||
into a statement, which will then not return a value. Keep this in mind as you
|
||||
explore function return values and expressions next.
|
||||
as part of the `let` statement. Note the `x + 1` line without a semicolon at
|
||||
the end, unlike most of the lines you’ve seen so far. Expressions do not
|
||||
include ending semicolons. If you add a semicolon to the end of an expression,
|
||||
you turn it into a statement, which will then not return a value. Keep this in
|
||||
mind as you explore function return values and expressions next.
|
||||
|
||||
### Functions with Return Values
|
||||
|
||||
Functions can return values to the code that calls them. We don’t name return
|
||||
values, but we do declare their type after an arrow (`->`). In Rust, the return
|
||||
value of the function is synonymous with the value of the final expression in
|
||||
the block of the body of a function. Here’s an example of a function that
|
||||
returns a value:
|
||||
the block of the body of a function. You can return early from a function by
|
||||
using the `return` keyword and specifying a value, but most functions return
|
||||
the last expression implicitly. Here’s an example of a function that returns a
|
||||
value:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn five() -> i32 {
|
||||
5
|
||||
}
|
||||
@ -886,9 +882,10 @@ function—just the number `5` by itself. That’s a perfectly valid function in
|
||||
Rust. Note that the function’s return type is specified, too, as `-> i32`. Try
|
||||
running this code; the output should look like this:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
@ -899,7 +896,7 @@ first, the line `let x = five();` shows that we’re using the return value of a
|
||||
function to initialize a variable. Because the function `five` returns a `5`,
|
||||
that line is the same as the following:
|
||||
|
||||
```rust
|
||||
```
|
||||
let x = 5;
|
||||
```
|
||||
|
||||
@ -910,7 +907,7 @@ example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
@ -928,7 +925,7 @@ expression to a statement?
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
@ -947,18 +944,14 @@ error[E0308]: mismatched types
|
||||
--> src/main.rs:7:28
|
||||
|
|
||||
7 | fn plus_one(x: i32) -> i32 {
|
||||
| ____________________________^ starting here...
|
||||
| ____________________________^
|
||||
8 | | x + 1;
|
||||
| | - help: consider removing this semicolon
|
||||
9 | | }
|
||||
| |_^ ...ending here: expected i32, found ()
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
help: consider removing this semicolon:
|
||||
--> src/main.rs:8:10
|
||||
|
|
||||
8 | x + 1;
|
||||
| ^
|
||||
```
|
||||
|
||||
The main error message, “mismatched types,” reveals the core issue with this
|
||||
@ -978,7 +971,7 @@ reading the source code may find useful.
|
||||
|
||||
Here’s a simple comment:
|
||||
|
||||
```rust
|
||||
```
|
||||
// Hello, world.
|
||||
```
|
||||
|
||||
@ -986,7 +979,7 @@ In Rust, comments must start with two slashes and continue until the end of the
|
||||
line. For comments that extend beyond a single line, you’ll need to include
|
||||
`//` on each line, like this:
|
||||
|
||||
```rust
|
||||
```
|
||||
// So we’re doing something complicated here, long enough that we need
|
||||
// multiple lines of comments to do it! Whew! Hopefully, this comment will
|
||||
// explain what’s going on.
|
||||
@ -996,7 +989,7 @@ Comments can also be placed at the end of lines containing code:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let lucky_number = 7; // I’m feeling lucky today.
|
||||
}
|
||||
@ -1007,14 +1000,15 @@ separate line above the code it’s annotating:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
// I’m feeling lucky today.
|
||||
let lucky_number = 7;
|
||||
}
|
||||
```
|
||||
|
||||
That’s all there is to comments. They’re not particularly complicated.
|
||||
Rust also has another kind of comment, documentation comments, which we’ll
|
||||
discuss in Chapter 14.
|
||||
|
||||
## Control Flow
|
||||
|
||||
@ -1035,7 +1029,7 @@ the `if` expression. In the *src/main.rs* file, input the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
@ -1047,24 +1041,27 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
|
||||
|
||||
All `if` expressions start with the keyword `if`, which is followed by a
|
||||
condition. In this case, the condition checks whether or not the variable
|
||||
`number` has a value less than 5. The block of code we want to execute if the
|
||||
condition is true is placed immediately after the condition inside curly
|
||||
braces. Blocks of code associated with the conditions in `if` expressions are
|
||||
brackets. Blocks of code associated with the conditions in `if` expressions are
|
||||
sometimes called *arms*, just like the arms in `match` expressions that we
|
||||
discussed in the “Comparing the Guess to the Secret Number” section of Chapter
|
||||
2. Optionally, we can also include an `else` expression, which we chose to do
|
||||
here, to give the program an alternative block of code to execute should the
|
||||
condition evaluate to false. If you don’t provide an `else` expression and the
|
||||
condition is false, the program will just skip the `if` block and move on to
|
||||
the next bit of code.
|
||||
discussed in the “Comparing the Guess to the Secret Number” section of
|
||||
Chapter 2. Optionally, we can also include an `else` expression, which we chose
|
||||
to do here, to give the program an alternative block of code to execute should
|
||||
the condition evaluate to false. If you don’t provide an `else` expression and
|
||||
the condition is false, the program will just skip the `if` block and move on
|
||||
to the next bit of code.
|
||||
|
||||
Try running this code; you should see the following output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was true
|
||||
```
|
||||
@ -1072,15 +1069,16 @@ condition was true
|
||||
Let’s try changing the value of `number` to a value that makes the condition
|
||||
`false` to see what happens:
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
let number = 7;
|
||||
```
|
||||
|
||||
Run the program again, and look at the output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was false
|
||||
```
|
||||
@ -1091,7 +1089,7 @@ code:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
@ -1124,7 +1122,7 @@ expression to the following:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let number = 3;
|
||||
|
||||
@ -1143,7 +1141,7 @@ expression. For example:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let number = 6;
|
||||
|
||||
@ -1162,9 +1160,10 @@ fn main() {
|
||||
This program has four possible paths it can take. After running it, you should
|
||||
see the following output:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
number is divisible by 3
|
||||
```
|
||||
@ -1187,7 +1186,7 @@ statement, for instance in Listing 3-4:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let condition = true;
|
||||
let number = if condition {
|
||||
@ -1200,16 +1199,16 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 3-4: Assigning the result of an `if` expression to a variable
|
||||
</caption>
|
||||
Listing 3-4: Assigning the result of an `if` expression
|
||||
to a variable
|
||||
|
||||
The `number` variable will be bound to a value based on the outcome of the `if`
|
||||
expression. Run this code to see what happens:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/branches`
|
||||
The value of number is: 5
|
||||
```
|
||||
@ -1224,7 +1223,7 @@ the following example?
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
let condition = true;
|
||||
|
||||
@ -1247,15 +1246,15 @@ error[E0308]: if and else have incompatible types
|
||||
--> src/main.rs:4:18
|
||||
|
|
||||
4 | let number = if condition {
|
||||
| __________________^ starting here...
|
||||
| __________________^
|
||||
5 | | 5
|
||||
6 | | } else {
|
||||
7 | | "six"
|
||||
8 | | };
|
||||
| |_____^ ...ending here: expected integral variable, found reference
|
||||
| |_____^ expected integral variable, found reference
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found type `&'static str`
|
||||
found type `&str`
|
||||
```
|
||||
|
||||
The expression in the `if` block evaluates to an integer, and the expression in
|
||||
@ -1286,7 +1285,7 @@ like this:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust,ignore
|
||||
```
|
||||
fn main() {
|
||||
loop {
|
||||
println!("again!");
|
||||
@ -1296,11 +1295,13 @@ fn main() {
|
||||
|
||||
When we run this program, we’ll see `again!` printed over and over continuously
|
||||
until we stop the program manually. Most terminals support a keyboard shortcut,
|
||||
ctrl-C, to halt a program that is stuck in a continual loop. Give it a try:
|
||||
<span class="keystroke">ctrl-C</span>, to halt a program that is stuck in a
|
||||
continual loop. Give it a try:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
|
||||
Running `target/debug/loops`
|
||||
again!
|
||||
again!
|
||||
@ -1309,9 +1310,9 @@ again!
|
||||
^Cagain!
|
||||
```
|
||||
|
||||
The symbol `^C` represents where you pressed ctrl-C. You may or may not see the
|
||||
word `again!` printed after the `^C`, depending on where the code was in the
|
||||
loop when it received the halt signal.
|
||||
The symbol `^C` represents where you pressed <span class="keystroke">ctrl-C
|
||||
</span>. You may or may not see the word `again!` printed after the `^C`,
|
||||
depending on where the code was in the loop when it received the halt signal.
|
||||
|
||||
Fortunately, Rust provides another, more reliable way to break out of a loop.
|
||||
You can place the `break` keyword within the loop to tell the program when to
|
||||
@ -1334,11 +1335,11 @@ prints another message and exits:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let mut number = 3;
|
||||
|
||||
while number != 0 {
|
||||
while number != 0 {
|
||||
println!("{}!", number);
|
||||
|
||||
number = number - 1;
|
||||
@ -1355,11 +1356,11 @@ true, the code runs; otherwise, it exits the loop.
|
||||
#### Looping Through a Collection with `for`
|
||||
|
||||
You could use the `while` construct to loop over the elements of a collection,
|
||||
such as an array. For example:
|
||||
such as an array. For example, let’s look at Listing 3-5:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
let mut index = 0;
|
||||
@ -1372,18 +1373,18 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 3-5: Looping through each element of a collection using a `while` loop
|
||||
</caption>
|
||||
Listing 3-5: Looping through each element of a collection
|
||||
using a `while` loop
|
||||
|
||||
Here, the code counts up through the elements in the array. It starts at index
|
||||
`0`, and then loops until it reaches the final index in the array (that is,
|
||||
when `index < 5` is no longer true). Running this code will print out every
|
||||
element in the array:
|
||||
|
||||
```bash
|
||||
```
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
|
||||
Running `target/debug/loops`
|
||||
the value is: 10
|
||||
the value is: 20
|
||||
@ -1402,11 +1403,11 @@ code to perform the conditional check on every element on every iteration
|
||||
through the loop.
|
||||
|
||||
As a more efficient alternative, you can use a `for` loop and execute some code
|
||||
for each item in a collection. A `for` loop looks like this:
|
||||
for each item in a collection. A `for` loop looks like this code in Listing 3-6:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
||||
@ -1416,9 +1417,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<caption>
|
||||
Listing 3-6: Looping through each element of a collection using a `for` loop
|
||||
</caption>
|
||||
Listing 3-6: Looping through each element of a collection
|
||||
using a `for` loop
|
||||
|
||||
When we run this code, we’ll see the same output as in Listing 3-5. More
|
||||
importantly, we’ve now increased the safety of the code and eliminated the
|
||||
@ -1443,7 +1443,7 @@ we’ve not yet talked about, `rev`, to reverse the range:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```rust
|
||||
```
|
||||
fn main() {
|
||||
for number in (1..4).rev() {
|
||||
println!("{}!", number);
|
||||
|
@ -8,10 +8,10 @@ together multiple related values that make up a meaningful group. If you’re
|
||||
familiar with an object-oriented language, a *struct* is like an object’s data
|
||||
attributes. In this chapter, we’ll compare and contrast tuples with structs,
|
||||
demonstrate how to use structs, and discuss how to define methods and
|
||||
associated functions on structs to specify behavior associated with a struct’s
|
||||
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the
|
||||
building blocks for creating new types in your program’s domain to take full
|
||||
advantage of Rust’s compile time type checking.
|
||||
associated functions to specify behavior associated with a struct’s data. The
|
||||
struct and *enum* (which is discussed in Chapter 6) concepts are the building
|
||||
blocks for creating new types in your program’s domain to take full advantage
|
||||
of Rust’s compile time type checking.
|
||||
|
||||
## Defining and Instantiating Structs
|
||||
|
||||
@ -61,9 +61,9 @@ Listing 5-2: Creating an instance of the `User` struct
|
||||
|
||||
To get a specific value from a struct, we can use dot notation. If we wanted
|
||||
just this user’s email address, we can use `user1.email` wherever we want to
|
||||
use this value. To change a value in a struct, if the instance is mutable, we
|
||||
can use the dot notation and assign into a particular field. Listing 5-3 shows
|
||||
how to change the value in the `email` field of a mutable `User` instance:
|
||||
use this value. If the instance is mutable, we can change a value by using the
|
||||
dot notation and assigning into a particular field. Listing 5-3 shows how to
|
||||
change the value in the `email` field of a mutable `User` instance:
|
||||
|
||||
```
|
||||
let mut user1 = User {
|
||||
@ -78,11 +78,14 @@ user1.email = String::from("anotheremail@example.com");
|
||||
|
||||
Listing 5-3: Changing the value in the `email` field of a `User` instance
|
||||
|
||||
Like any expression, we can implicitly return a new instance of a struct from a
|
||||
function by constructing the new instance as the last expression in the
|
||||
function body. Listing 5-4 shows a `build_user` function that returns a `User`
|
||||
instance with the given `email` and `username`. The `active` field gets the
|
||||
value of `true`, and the `sign_in_count` gets a value of `1`.
|
||||
Note that the entire instance must be mutable; Rust doesn’t allow us to mark
|
||||
only certain fields as mutable. Also note that with any expression, we can
|
||||
construct a new instance of the struct as the last expression in the function
|
||||
body to implicitly return that new instance.
|
||||
|
||||
Listing 5-4 shows a `build_user` function that returns a `User` instance with
|
||||
the given email and username. The `active` field gets the value of `true`, and
|
||||
the `sign_in_count` gets a value of `1`.
|
||||
|
||||
```
|
||||
fn build_user(email: String, username: String) -> User {
|
||||
@ -98,24 +101,17 @@ fn build_user(email: String, username: String) -> User {
|
||||
Listing 5-4: A `build_user` function that takes an email and username and
|
||||
returns a `User` instance
|
||||
|
||||
Repeating the `email` field name and `email` variable, and the same for
|
||||
`username`, is a bit tedious, though. It makes sense to name the function
|
||||
arguments with the same name as the struct fields, but if the struct had more
|
||||
fields, repeating each name would get even more annoying. Luckily, there's a
|
||||
convenient shorthand!
|
||||
It makes sense to name the function arguments with the same name as the struct
|
||||
fields, but having to repeat the `email` and `username` field names and
|
||||
variables is a bit tedious. If the struct had more fields, repeating each name
|
||||
would get even more annoying. Luckily, there's a convenient shorthand!
|
||||
|
||||
### Field Init Shorthand when Variables Have the Same Name as Fields
|
||||
### Using the Field Init Shorthand when Variables and Fields Have the Same Name
|
||||
|
||||
If you have variables with the same names as struct fields, you can use *field
|
||||
init shorthand*. This can make functions that create new instances of structs
|
||||
more concise.
|
||||
|
||||
In Listing 5-4, the parameter names `email` and `username` are the same as the
|
||||
`User` struct’s field names `email` and `username`. Because the names are
|
||||
exactly the same, we can write `build_user` without the repetition of `email`
|
||||
and `username` as shown in Listing 5-5. This version of `build_user` behaves
|
||||
the same way as the one in Listing 5-4. The field init syntax can make cases
|
||||
like this shorter to write, especially when structs have many fields.
|
||||
Because the parameter names and the struct field names are exactly the same in
|
||||
Listing 5-4, we can use the *field init shorthand* syntax to rewrite
|
||||
`build_user` so that it behaves exactly the same but doesn’t have the
|
||||
repetition of `email` and `username` in the way shown in Listing 5-5.
|
||||
|
||||
```
|
||||
fn build_user(email: String, username: String) -> User {
|
||||
@ -128,16 +124,23 @@ fn build_user(email: String, username: String) -> User {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 5-5: A `build_user` function that uses field init syntax since the
|
||||
Listing 5-5: A `build_user` function that uses field init shorthand since the
|
||||
`email` and `username` parameters have the same name as struct fields
|
||||
|
||||
Here, we’re creating a new instance of the `User` struct, which has a field
|
||||
named `email`. We want to set the `email` field’s value to the value in the
|
||||
`email` parameter of the `build_user` function. Because the `email` field and
|
||||
the `email` parameter have the same name, we only need to write `email` rather
|
||||
than `email: email`.
|
||||
|
||||
### Creating Instances From Other Instances With Struct Update Syntax
|
||||
|
||||
It’s often useful to create a new instance from an old instance, using most of
|
||||
the old instance’s values but changing some. Listing 5-6 shows an example of
|
||||
creating a new `User` instance in `user2` by setting the values of `email` and
|
||||
`username` but using the same values for the rest of the fields from the
|
||||
`user1` instance we created in Listing 5-2:
|
||||
It’s often useful to create a new instance of a struct that uses most of an old
|
||||
instance’s values, but changes some. We do this using *struct update syntax*.
|
||||
|
||||
First, Listing 5-6 shows how we create a new `User` instance in `user2` without
|
||||
the update syntax. We set new values for `email` and `username`, but otherwise
|
||||
use the same values from `user1` that we created in Listing 5-2:
|
||||
|
||||
```
|
||||
let user2 = User {
|
||||
@ -148,15 +151,12 @@ let user2 = User {
|
||||
};
|
||||
```
|
||||
|
||||
Listing 5-6: Creating a new `User` instance, `user2`, and setting some fields
|
||||
to the values of the same fields from `user1`
|
||||
Listing 5-6: Creating a new `User` instance using some of the values from
|
||||
`user1`
|
||||
|
||||
The *struct update syntax* achieves the same effect as the code in Listing 5-6
|
||||
using less code. The struct update syntax uses `..` to specify that the
|
||||
remaining fields not set explicitly should have the same value as the fields in
|
||||
the given instance. The code in Listing 5-7 also creates an instance in `user2`
|
||||
that has a different value for `email` and `username` but has the same values
|
||||
for the `active` and `sign_in_count` fields that `user1` has:
|
||||
Using struct update syntax, we can achieve the same effect with less code,
|
||||
shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
|
||||
explicitly set should have the same value as the fields in the given instance.
|
||||
|
||||
```
|
||||
let user2 = User {
|
||||
@ -170,14 +170,22 @@ Listing 5-7: Using struct update syntax to set a new `email` and `username`
|
||||
values for a `User` instance but use the rest of the values from the fields of
|
||||
the instance in the `user1` variable
|
||||
|
||||
The code in Listing 5-7 also creates an instance in `user2` that has a
|
||||
different value for `email` and `username` but has the same values for the
|
||||
`active` and `sign_in_count` fields from `user1`.
|
||||
|
||||
### Tuple Structs without Named Fields to Create Different Types
|
||||
|
||||
We can also define structs that look similar to tuples, called *tuple structs*,
|
||||
that have the added meaning the struct name provides, but don’t have names
|
||||
associated with their fields, just the types of the fields. The definition of a
|
||||
tuple struct still starts with the `struct` keyword and the struct name, which
|
||||
are followed by the types in the tuple. For example, here are definitions and
|
||||
usages of tuple structs named `Color` and `Point`:
|
||||
associated with their fields, just the types of the fields. Tuple structs are
|
||||
useful when you want to give the whole tuple a name and make the tuple be a
|
||||
different type than other tuples, but naming each field as in a regular struct
|
||||
would be verbose or redundant.
|
||||
|
||||
To define a tuple struct you start with the `struct` keyword and the struct
|
||||
name followed by the types in the tuple. For example, here are definitions and
|
||||
usages of two tuple structs named `Color` and `Point`:
|
||||
|
||||
```
|
||||
struct Color(i32, i32, i32);
|
||||
@ -189,8 +197,12 @@ let origin = Point(0, 0, 0);
|
||||
|
||||
Note that the `black` and `origin` values are different types, since they’re
|
||||
instances of different tuple structs. Each struct we define is its own type,
|
||||
even though the fields within the struct have the same types. Otherwise, tuple
|
||||
struct instances behave like tuples, which we covered in Chapter 3.
|
||||
even though the fields within the struct have the same types. For example, a
|
||||
function that takes a parameter of type `Color` cannot take a `Point` as an
|
||||
argument, even though both types are made up of three `i32` values. Otherwise,
|
||||
tuple struct instances behave like tuples, which we covered in Chapter 3: you
|
||||
can destructure them into their individual pieces, you can use a `.` followed
|
||||
by the index to access an individual value, and so on.
|
||||
|
||||
### Unit-Like Structs without Any Fields
|
||||
|
||||
@ -204,10 +216,10 @@ PROD: START BOX
|
||||
|
||||
### Ownership of Struct Data
|
||||
|
||||
In the `User` struct definition in Listing 5-1, we used the owned `String`
|
||||
type rather than the `&str` string slice type. This is a deliberate choice
|
||||
because we want instances of this struct to own all of its data and for that
|
||||
data to be valid for as long as the entire struct is valid.
|
||||
In the `User` struct definition in Listing 5-1, we used the owned `String` type
|
||||
rather than the `&str` string slice type. This is a deliberate choice because
|
||||
we want instances of this struct to own all of its data and for that data to be
|
||||
valid for as long as the entire struct is valid.
|
||||
|
||||
It’s possible for structs to store references to data owned by something else,
|
||||
but to do so requires the use of *lifetimes*, a Rust feature that is discussed
|
||||
@ -251,8 +263,8 @@ error[E0106]: missing lifetime specifier
|
||||
| ^ expected lifetime parameter
|
||||
```
|
||||
|
||||
We’ll discuss how to fix these errors so you can store references in structs
|
||||
in Chapter 10, but for now, we’ll fix errors like these using owned types like
|
||||
We’ll discuss how to fix these errors so you can store references in structs in
|
||||
Chapter 10, but for now, we’ll fix errors like these using owned types like
|
||||
`String` instead of references like `&str`.
|
||||
|
||||
PROD: END BOX
|
||||
@ -264,7 +276,7 @@ calculates the area of a rectangle. We’ll start with single variables, and the
|
||||
refactor the program until we’re using structs instead.
|
||||
|
||||
Let’s make a new binary project with Cargo called *rectangles* that will take
|
||||
the length and width of a rectangle specified in pixels and will calculate the
|
||||
the width and height of a rectangle specified in pixels and will calculate the
|
||||
area of the rectangle. Listing 5-8 shows a short program with one way of doing
|
||||
just that in our project’s *src/main.rs*:
|
||||
|
||||
@ -272,22 +284,22 @@ Filename: src/main.rs
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let length1 = 50;
|
||||
let width1 = 30;
|
||||
let height1 = 50;
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(length1, width1)
|
||||
area(width1, height1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(length: u32, width: u32) -> u32 {
|
||||
length * width
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
width * height
|
||||
}
|
||||
```
|
||||
|
||||
Listing 5-8: Calculating the area of a rectangle specified by its length and
|
||||
width in separate variables
|
||||
Listing 5-8: Calculating the area of a rectangle specified by its width and
|
||||
height in separate variables
|
||||
|
||||
Now, run this program using `cargo run`:
|
||||
|
||||
@ -298,20 +310,20 @@ The area of the rectangle is 1500 square pixels.
|
||||
### Refactoring with Tuples
|
||||
|
||||
Even though Listing 5-8 works and figures out the area of the rectangle by
|
||||
calling the `area` function with each dimension, we can do better. The length
|
||||
and the width are related to each other because together they describe one
|
||||
calling the `area` function with each dimension, we can do better. The width
|
||||
and the height are related to each other because together they describe one
|
||||
rectangle.
|
||||
|
||||
The issue with this method is evident in the signature of `area`:
|
||||
|
||||
```
|
||||
fn area(length: u32, width: u32) -> u32 {
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
```
|
||||
|
||||
The `area` function is supposed to calculate the area of one rectangle, but the
|
||||
function we wrote has two parameters. The parameters are related, but that’s
|
||||
not expressed anywhere in our program. It would be more readable and more
|
||||
manageable to group length and width together. We’ve already discussed one way
|
||||
manageable to group width and height together. We’ve already discussed one way
|
||||
we might do that in the Grouping Values into Tuples section of Chapter 3 on
|
||||
page XX: by using tuples. Listing 5-9 shows another version of our program that
|
||||
uses tuples:
|
||||
@ -320,7 +332,7 @@ Filename: src/main.rs
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let rect1 = (50, 30);
|
||||
let rect1 = (30, 50);
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -333,16 +345,16 @@ fn area(dimensions: (u32, u32)) -> u32 {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 5-8: Specifying the length and width of the rectangle with a tuple
|
||||
Listing 5-8: Specifying the width and height of the rectangle with a tuple
|
||||
|
||||
In one way, this program is better. Tuples let us add a bit of structure, and
|
||||
we’re now passing just one argument. But in another way this version is less
|
||||
clear: tuples don’t name their elements, so our calculation has become more
|
||||
confusing because we have to index into the parts of the tuple.
|
||||
|
||||
It doesn’t matter if we mix up length and width for the area calculation, but
|
||||
It doesn’t matter if we mix up width and height for the area calculation, but
|
||||
if we want to draw the rectangle on the screen, it would matter! We would have
|
||||
to keep in mind that `length` is the tuple index `0` and `width` is the tuple
|
||||
to keep in mind that `width` is the tuple index `0` and `height` is the tuple
|
||||
index `1`. If someone else worked on this code, they would have to figure this
|
||||
out and keep it in mind as well. It would be easy to forget or mix up these
|
||||
values and cause errors, because we haven’t conveyed the meaning of our data in
|
||||
@ -358,12 +370,12 @@ Filename: src/main.rs
|
||||
|
||||
```
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -372,16 +384,16 @@ fn main() {
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.length * rectangle.width
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
```
|
||||
|
||||
Listing 5-10: Defining a `Rectangle` struct
|
||||
|
||||
Here we’ve defined a struct and named it `Rectangle`. Inside the `{}` we
|
||||
defined the fields as `length` and `width`, both of which have type `u32`. Then
|
||||
in `main` we create a particular instance of a `Rectangle` that has a length of
|
||||
50 and a width of 30.
|
||||
defined the fields as `width` and `height`, both of which have type `u32`. Then
|
||||
in `main` we create a particular instance of a `Rectangle` that has a width of
|
||||
30 and a height of 50.
|
||||
|
||||
Our `area` function is now defined with one parameter, which we’ve named
|
||||
`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
|
||||
@ -390,10 +402,10 @@ take ownership of it. This way, `main` retains its ownership and can continue
|
||||
using `rect1`, which is the reason we use the `&` in the function signature and
|
||||
where we call the function.
|
||||
|
||||
The `area` function accesses the `length` and `width` fields of the `Rectangle`
|
||||
The `area` function accesses the `width` and `height` fields of the `Rectangle`
|
||||
instance. Our function signature for `area` now indicates exactly what we mean:
|
||||
calculate the area of a `Rectangle` using its `length` and `width` fields. This
|
||||
conveys that the length and width are related to each other, and gives
|
||||
calculate the area of a `Rectangle` using its `width` and `height` fields. This
|
||||
conveys that the width and height are related to each other, and gives
|
||||
descriptive names to the values rather than using the tuple index values of `0`
|
||||
and `1`—a win for clarity.
|
||||
|
||||
@ -408,12 +420,12 @@ Filename: src/main.rs
|
||||
|
||||
```
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {}", rect1);
|
||||
}
|
||||
@ -473,12 +485,12 @@ Filename: src/main.rs
|
||||
```
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {:?}", rect1);
|
||||
}
|
||||
@ -491,7 +503,7 @@ Now when we run the program, we won’t get any errors and we’ll see the
|
||||
following output:
|
||||
|
||||
```
|
||||
rect1 is Rectangle { length: 50, width: 30 }
|
||||
rect1 is Rectangle { width: 30, height: 50 }
|
||||
```
|
||||
|
||||
Nice! It’s not the prettiest output, but it shows the values of all the fields
|
||||
@ -502,8 +514,8 @@ When we use the `{:#?}` style in the example, the output will look like this:
|
||||
|
||||
```
|
||||
rect1 is Rectangle {
|
||||
length: 50,
|
||||
width: 30
|
||||
width: 30,
|
||||
height: 50
|
||||
}
|
||||
```
|
||||
|
||||
@ -539,18 +551,18 @@ Filename: src/main.rs
|
||||
```
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -638,9 +650,9 @@ Filename: src/main.rs
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect2 = Rectangle { length: 40, width: 10 };
|
||||
let rect3 = Rectangle { length: 45, width: 60 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
let rect2 = Rectangle { width: 10, height: 40 };
|
||||
let rect3 = Rectangle { width: 60, height: 45 };
|
||||
|
||||
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
|
||||
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
|
||||
@ -667,8 +679,8 @@ parameter will be by looking at the code that calls the method:
|
||||
read `rect2` (rather than write, which would mean we’d need a mutable borrow),
|
||||
and we want `main` to retain ownership of `rect2` so we can use it again after
|
||||
calling the `can_hold` method. The return value of `can_hold` will be a
|
||||
boolean, and the implementation will check whether the length and width of
|
||||
`self` are both greater than the length and width of the other `Rectangle`,
|
||||
boolean, and the implementation will check whether the width and height of
|
||||
`self` are both greater than the width and height of the other `Rectangle`,
|
||||
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||
Listing 5-13, shown in Listing 5-15:
|
||||
|
||||
@ -677,11 +689,11 @@ Filename: src/main.rs
|
||||
```
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.length > other.length && self.width > other.width
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -705,7 +717,7 @@ function.
|
||||
|
||||
Associated functions are often used for constructors that will return a new
|
||||
instance of the struct. For example, we could provide an associated function
|
||||
that would have one dimension parameter and use that as both length and width,
|
||||
that would have one dimension parameter and use that as both width and height,
|
||||
thus making it easier to create a square `Rectangle` rather than having to
|
||||
specify the same value twice:
|
||||
|
||||
@ -714,7 +726,7 @@ Filename: src/main.rs
|
||||
```
|
||||
impl Rectangle {
|
||||
fn square(size: u32) -> Rectangle {
|
||||
Rectangle { length: size, width: size }
|
||||
Rectangle { width: size, height: size }
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -733,13 +745,13 @@ in its own `impl` block:
|
||||
```
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.length > other.length && self.width > other.width
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
|
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
@ -14,10 +14,7 @@ advanced features to show you how to:
|
||||
* Extend Cargo with your own custom commands
|
||||
|
||||
Cargo can do even more than what we can cover in this chapter too, so for a
|
||||
full explanation, see its documentation at *http://doc.rust-lang.org/cargo/*.
|
||||
|
||||
<!--can you give a link to the documentation?-->
|
||||
<!-- done /Carol -->
|
||||
full explanation, see its documentation at *https://doc.rust-lang.org/cargo/*.
|
||||
|
||||
## Customizing Builds with Release Profiles
|
||||
|
||||
@ -26,36 +23,14 @@ different configurations, to allow the programmer more control over various
|
||||
options for compiling your code. Each profile is configured independently of
|
||||
the others.
|
||||
|
||||
<!-- To be clear, are these release profiles pre-defined profiles that you use
|
||||
for different things? Can you lay that out more explicitly, give a more
|
||||
detailed definition? That seems super useful, but I'm not sure I'm following
|
||||
what they actually are. -->
|
||||
<!-- They are pre-defined, we've tried to clarify /Carol -->
|
||||
Cargo has two main profiles you should know about: the `dev` profile Cargo uses
|
||||
when you run `cargo build`, and the `release` profile Cargo uses when you run
|
||||
`cargo build --release`. The `dev` profile is defined with good defaults for
|
||||
developing, and likewise the `release` profile has good defaults for release
|
||||
builds.
|
||||
|
||||
Cargo has four profiles defined with good default configurations for each use
|
||||
case. Cargo uses the different profiles based on which command you’re running.
|
||||
The commands correspond to the profiles as shown in Table 14-1:
|
||||
|
||||
<!-- Hm, so these profiles aren't built-in, just supported? and used for what
|
||||
for cargo build? How do you use a particular profile in a build, is it chosen
|
||||
by default? Do you have to specify? -->
|
||||
<!-- They are built in with defaults. We've tried to clarify by changing this
|
||||
to a table and adding some more explanation, is this better? /Carol -->
|
||||
|
||||
| Command | Profile |
|
||||
|-------------------------|-----------|
|
||||
| `cargo build` | `dev` |
|
||||
| `cargo build --release` | `release` |
|
||||
| `cargo test` | `test` |
|
||||
| `cargo doc` | `doc` |
|
||||
|
||||
Table 14-1: Which profile is used when you run different Cargo commands
|
||||
|
||||
This may be familiar from the output of your builds, which shows the profile
|
||||
used in the build:
|
||||
|
||||
<!-- Above-is that what you meant here? -->
|
||||
<!-- Yep! /Carol -->
|
||||
These names may be familiar from the output of your builds, which shows the
|
||||
profile used in the build:
|
||||
|
||||
```
|
||||
$ cargo build
|
||||
@ -64,31 +39,19 @@ $ cargo build --release
|
||||
Finished release [optimized] target(s) in 0.0 secs
|
||||
```
|
||||
|
||||
The “dev” and “release” notifications here indicate that the compiler is
|
||||
using different profiles.
|
||||
|
||||
<!-- which profile is "debug" associated with? As you can probably tell, I'm
|
||||
not confident in my interpretation here, I think we need more info -->
|
||||
<!-- Sorry, this was an inconsistency in cargo that we actually noticed while
|
||||
writing this section and has since been fixed, but then I think we missed
|
||||
updating this spot. `debug` should be gone. /Carol -->
|
||||
The “dev” and “release” notifications here indicate that the compiler is using
|
||||
different profiles.
|
||||
|
||||
### Customizing Release Profiles
|
||||
|
||||
<!-- Do we mean that the profiles are all already stored in Cargo.toml, or you
|
||||
have to add the entire code to cargo.toml? It seems like the former from the
|
||||
writing, but looking through toml files I've made the latter seems to be true.
|
||||
If you have multiple profiles in the toml, how do you choose which one to use?
|
||||
-->
|
||||
<!-- We've tried to clarify below. Please let me know if this is still unclear,
|
||||
I'm confused about how you're drawing your conclusions. /Carol -->
|
||||
|
||||
Cargo has default settings for each of the profiles that apply when there
|
||||
aren’t any `[profile.*]` sections in the project’s *Cargo.toml* file. By adding
|
||||
`[profile.*]` sections for any profile we want to customize, we can choose to
|
||||
override any subset of the default settings. For example, here are the default
|
||||
values for the `opt-level` setting for the `dev` and `release` profiles:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
@ -112,15 +75,6 @@ them in *Cargo.toml*. If we wanted to use optimization level 1 in the
|
||||
development profile, for example, we can add these two lines to our project’s
|
||||
*Cargo.toml*:
|
||||
|
||||
<!-- So do we choose which profile to use when? How do we do that? Or is that
|
||||
determined automatically by Rust, and if so, how? I think we need to show that
|
||||
somewhere around here -->
|
||||
<!-- Which profile is used is determined by which command you're running, which
|
||||
we tried to show above. I hope the table added above has clarified this, if
|
||||
not, please suggest further wording above, but the reader should understand
|
||||
which profile gets used when by this point and I don't think we should repeat
|
||||
it again here. /Carol -->
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
@ -134,7 +88,7 @@ will use the defaults for the `dev` profile plus our customization to
|
||||
optimizations than the default, but not as many as a release build.
|
||||
|
||||
For the full list of configuration options and defaults for each profile, see
|
||||
Cargo’s documentation at *http://doc.rust-lang.org/cargo/*.
|
||||
Cargo’s documentation at *https://doc.rust-lang.org/cargo/*.
|
||||
|
||||
## Publishing a Crate to Crates.io
|
||||
|
||||
@ -158,13 +112,9 @@ contents of documentation comments for public API items, intended for
|
||||
programmers interested in knowing how to *use* your crate, as opposed to how
|
||||
your crate is *implemented*.
|
||||
|
||||
<!-- Doc comments support markdown but don’t require markdown, is that right?
|
||||
Just wanted to make that distinction -->
|
||||
<!-- yes -->
|
||||
|
||||
Documentation comments use `///` instead of `//` and support Markdown notation
|
||||
for formatting the text if you’d like. You place documentation comments just
|
||||
before the item they are documenting. Listing 14-2 shows documentation comments
|
||||
before the item they are documenting. Listing 14-1 shows documentation comments
|
||||
for an `add_one` function in a crate named `my_crate`:
|
||||
|
||||
Filename: src/lib.rs
|
||||
@ -184,11 +134,7 @@ pub fn add_one(x: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-2: A documentation comment for a function
|
||||
|
||||
<!-- At some point, a screenshot of how this is rendered in HTML could be really
|
||||
useful here, what you do think? -->
|
||||
<!-- Yup! /Carol -->
|
||||
Listing 14-1: A documentation comment for a function
|
||||
|
||||
Here, we give a description of what the `add_one` function does, then start a
|
||||
section with the heading “Examples”, and code that demonstrates how to use the
|
||||
@ -201,36 +147,28 @@ For convenience, running `cargo doc --open` will build the HTML for your
|
||||
current crate’s documentation (as well as the documentation for all of your
|
||||
crate’s dependencies) and open the result in a web browser. Navigate to the
|
||||
`add_one` function and you’ll see how the text in the documentation comments
|
||||
gets rendered, shown here in Figure 14-3:
|
||||
gets rendered, shown here in Figure 14-2:
|
||||
|
||||
<img alt="Rendered HTML documentation for the `add_one` function of `my_crate`" src="img/trpl14-03.png" class="center" />
|
||||
|
||||
Figure 14-3: HTML documentation for the `add_one` function
|
||||
|
||||
<!--Above - I added this line to describe what we're doing, encourage good
|
||||
practice, can you add/edit where necessary? These will generate as HTML when
|
||||
the code is run, is that how it works? -->
|
||||
<!-- Not when the code is run, when the programmer runs `cargo doc`. That
|
||||
doesn't run the programmer's code, really, not in the way `cargo run` runs it
|
||||
anyway. We've tried clarifying as well as adding a screenshot. /Carol -->
|
||||
Figure 14-2: HTML documentation for the `add_one` function
|
||||
|
||||
#### Commonly Used Sections
|
||||
|
||||
We used the `# Examples` markdown heading in Listing 14-2 to create a section
|
||||
We used the `# Examples` markdown heading in Listing 14-1 to create a section
|
||||
in the HTML with the title “Examples”. Some other sections that crate authors
|
||||
commonly use in their documentation include:
|
||||
|
||||
- Panics: The scenarios in which this function could `panic!`. Callers of this
|
||||
function who don’t want their programs to panic should make sure that they
|
||||
don’t call this function in these situations.
|
||||
- Errors: If this function returns a `Result`, describing the kinds of errors
|
||||
that might occur and what conditions might cause those errors to be returned
|
||||
can be helpful to callers so that they can write code to handle the different
|
||||
kinds of errors in different ways.
|
||||
- Safety: If this function uses `unsafe` code (which we will discuss in Chapter
|
||||
19), there should be a section covering the invariants that this function
|
||||
expects callers to uphold in order for the code in `unsafe` blocks to
|
||||
function correctly.
|
||||
* **Panics**: The scenarios in which this function could `panic!`. Callers of
|
||||
this function who don’t want their programs to panic should make sure that
|
||||
they don’t call this function in these situations.
|
||||
* **Errors**: If this function returns a `Result`, describing the kinds of
|
||||
errors that might occur and what conditions might cause those errors to be
|
||||
returned can be helpful to callers so that they can write code to handle the
|
||||
different kinds of errors in different ways.
|
||||
* **Safety**: If this function is `unsafe` to call (we will discuss unsafety in
|
||||
Chapter 19), there should be a section explaining why the function is unsafe
|
||||
and covering the invariants that this function expects callers to uphold.
|
||||
|
||||
Most documentation comment sections don’t need all of these sections, but this
|
||||
is a good list to check to remind you of the kinds of things that people
|
||||
@ -244,7 +182,7 @@ running `cargo test` will run the code examples in your documentation as tests!
|
||||
Nothing is better than documentation with examples. Nothing is worse than
|
||||
examples that don’t actually work because the code has changed since the
|
||||
documentation has been written. Try running `cargo test` with the documentation
|
||||
for the `add_one` function like in Listing 14-2; you should see a section in
|
||||
for the `add_one` function like in Listing 14-1; you should see a section in
|
||||
the test results like this:
|
||||
|
||||
```
|
||||
@ -262,21 +200,16 @@ tests catch that the example and the code are out of sync from one another!
|
||||
|
||||
#### Commenting Contained Items
|
||||
|
||||
<!-- I'm not clear what this comment does that's different, what do you mean by
|
||||
"comment containing items"? The lingo might just be going over my head here -->
|
||||
<!-- we've tried to reword and we've changed the example, is this clearer?
|
||||
/Carol -->
|
||||
|
||||
There’s another style of doc comment, `//!`, that adds documentation to the
|
||||
item that contains the comments, rather than adding documentation to the items
|
||||
following the comments. These are typically used inside the crate root file
|
||||
(*src/lib.rs*) or inside a module’s root (*mod.rs*) to document the crate or
|
||||
the module as a whole.
|
||||
(*src/lib.rs* by convention) or inside a module to document the crate or the
|
||||
module as a whole.
|
||||
|
||||
For example, if we wanted to add documentation that described the purpose of
|
||||
the `my_crate` crate that contains the `add_one` function, we can add
|
||||
documentation comments that start with `//!` to the beginning of *src/lib.rs*
|
||||
as shown in Listing 14-4:
|
||||
as shown in Listing 14-3:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
@ -290,7 +223,7 @@ Filename: src/lib.rs
|
||||
// ...snip...
|
||||
```
|
||||
|
||||
Listing 14-4: Documentation for the `my_crate` crate as a whole
|
||||
Listing 14-3: Documentation for the `my_crate` crate as a whole
|
||||
|
||||
Notice there isn’t any code after the last line that begins with `//!`. Because
|
||||
we started the comments with `//!` instead of `///`, we’re documenting the item
|
||||
@ -300,22 +233,18 @@ is the crate root. These comments describe the entire crate.
|
||||
|
||||
If we run `cargo doc --open`, we’ll see these comments displayed on the front
|
||||
page of the documentation for `my_crate` above the list of public items in the
|
||||
crate, as shown in Figure 14-5:
|
||||
crate, as shown in Figure 14-4:
|
||||
|
||||
<img alt="Rendered HTML documentation with a comment for the crate as a whole" src="img/trpl14-05.png" class="center" />
|
||||
|
||||
Figure 14-5: Rendered documentation for `my_crate` including the comment
|
||||
Figure 14-4: Rendered documentation for `my_crate` including the comment
|
||||
describing the crate as a whole
|
||||
|
||||
<!-- I'm not sure what we're looking at here, that's different from just using
|
||||
///, can you point it out, talk about it? -->
|
||||
<!-- Does the screenshot help? /Carol -->
|
||||
|
||||
Documentation comments within items are useful for describing crates and
|
||||
modules especially. Use them to talk about the purpose of the container overall
|
||||
to help users of your crate understand your organization.
|
||||
|
||||
### Exporting a Convenient Public API with `pub use`
|
||||
#### Exporting a Convenient Public API with `pub use`
|
||||
|
||||
In Chapter 7, we covered how to organize our code into modules with the `mod`
|
||||
keyword, how to make items public with the `pub` keyword, and how to bring
|
||||
@ -328,11 +257,6 @@ also be annoyed at having to type `use
|
||||
my_crate::some_module::another_module::UsefulType;` rather than `use
|
||||
my_crate::UsefulType;`.
|
||||
|
||||
<!-- Can you outline why, briefly, here? Reading on, is it something like:
|
||||
because some useful functions might be buried within modules that the user is
|
||||
unaware of -->
|
||||
<!-- Yes, that's pretty much it. We've clarified above. /Carol -->
|
||||
|
||||
The structure of your public API is a major consideration when publishing a
|
||||
crate. People who use your crate are less familiar with the structure than you
|
||||
are, and might have trouble finding the pieces they want to use if the module
|
||||
@ -345,13 +269,10 @@ to your private structure, using `pub use`. Re-exporting takes a public item in
|
||||
one location and makes it public in another location as if it was defined in
|
||||
the other location instead.
|
||||
|
||||
<!-- Can you give a quick definition of "re-export" here? -->
|
||||
<!-- Yup! /Carol -->
|
||||
|
||||
For example, say we made a library named `art` for modeling artistic concepts.
|
||||
Within this library is a `kinds` module containing two enums named
|
||||
`PrimaryColor` and `SecondaryColor` and a `utils` module containing a function
|
||||
named `mix` as shown in Listing 14-6:
|
||||
named `mix` as shown in Listing 14-5:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
@ -387,16 +308,16 @@ pub mod utils {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-6: An `art` library with items organized into `kinds` and `utils`
|
||||
Listing 14-5: An `art` library with items organized into `kinds` and `utils`
|
||||
modules
|
||||
|
||||
The front page of the documentation for this crate generated by `cargo doc`
|
||||
would look like Figure 14-7:
|
||||
would look like Figure 14-6:
|
||||
|
||||
<img alt="Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules" src="img/trpl14-07.png" class="center" />
|
||||
|
||||
Figure 14-7: Front page of the documentation for `art`
|
||||
that lists the `kinds` and `utils` modules
|
||||
Figure 14-6: Front page of the documentation for `art` that lists the `kinds`
|
||||
and `utils` modules
|
||||
|
||||
Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the
|
||||
front page, nor is the `mix` function. We have to click on `kinds` and `utils`
|
||||
@ -404,7 +325,7 @@ in order to see them.
|
||||
|
||||
Another crate depending on this library would need `use` statements that import
|
||||
the items from `art` including specifying the module structure that’s currently
|
||||
defined. Listing 14-8 shows an example of a crate that uses the `PrimaryColor`
|
||||
defined. Listing 14-7 shows an example of a crate that uses the `PrimaryColor`
|
||||
and `mix` items from the `art` crate:
|
||||
|
||||
Filename: src/main.rs
|
||||
@ -422,16 +343,10 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-8: A crate using the `art` crate’s items with its internal structure
|
||||
Listing 14-7: A crate using the `art` crate’s items with its internal structure
|
||||
exported
|
||||
|
||||
<!--Below -- just to clarify, the "users of this crate" refers to people using
|
||||
the crate in 14-8 that `uses` art, is that right? I want to make sure I'm
|
||||
following accurately! -->
|
||||
<!-- No, it refers to the users of the `art` crate. I've tried to clarify
|
||||
/Carol -->
|
||||
|
||||
The author of the code in Listing 14-8 that uses the `art` crate had to figure
|
||||
The author of the code in Listing 14-7 that uses the `art` crate had to figure
|
||||
out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils`
|
||||
module. The module structure of the `art` crate is more relevant to developers
|
||||
working on the `art` crate than developers using the `art` crate. The internal
|
||||
@ -442,8 +357,8 @@ confusion in having to figure out where to look and inconvenience in having to
|
||||
specify the module names in the `use` statements.
|
||||
|
||||
To remove the internal organization from the public API, we can take the `art`
|
||||
crate code from Listing 14-6 and add `pub use` statements to re-export the
|
||||
items at the top level, as shown in Listing 14-9:
|
||||
crate code from Listing 14-5 and add `pub use` statements to re-export the
|
||||
items at the top level, as shown in Listing 14-8:
|
||||
|
||||
Filename: src/lib.rs
|
||||
|
||||
@ -465,22 +380,20 @@ pub mod utils {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-9: Adding `pub use` statements to re-export items
|
||||
|
||||
<!-- Will add ghosting in libreoffice /Carol -->
|
||||
Listing 14-8: Adding `pub use` statements to re-export items
|
||||
|
||||
The API documentation generated with `cargo doc` for this crate will now list
|
||||
and link re-exports on the front page as shown in Figure 14-10, which makes
|
||||
and link re-exports on the front page as shown in Figure 14-9, which makes
|
||||
these types easier to find.
|
||||
|
||||
<img alt="Rendered documentation for the `art` crate with the re-exports on the front page" src="img/trpl14-10.png" class="center" />
|
||||
|
||||
Figure 14-10: Front page of the documentation for `art` that lists the
|
||||
Figure 14-9: Front page of the documentation for `art` that lists the
|
||||
re-exports
|
||||
|
||||
Users of the `art` crate can still see and choose to use the internal structure
|
||||
as in Listing 14-8, or they can use the more convenient structure from Listing
|
||||
14-9, as shown in Listing 14-11:
|
||||
as in Listing 14-7, or they can use the more convenient structure from Listing
|
||||
14-8, as shown in Listing 14-10:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
@ -495,9 +408,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-11: A program using the re-exported items from the `art` crate
|
||||
|
||||
<!-- Will add ghosting in libreoffice /Carol -->
|
||||
Listing 14-10: A program using the re-exported items from the `art` crate
|
||||
|
||||
In cases where there are many nested modules, re-exporting the types at the top
|
||||
level with `pub use` can make a big difference in the experience of people who
|
||||
@ -514,7 +425,7 @@ structure differs from their public API.
|
||||
|
||||
Before you can publish any crates, you need to create an account on crates.io
|
||||
and get an API token. To do so, visit the home page at *https://crates.io* and
|
||||
log in via a GitHub account---the GitHub account is a requirement for now, but
|
||||
log in via a GitHub account—the GitHub account is a requirement for now, but
|
||||
the site may support other ways of creating an account in the future. Once
|
||||
you’re logged in, visit your account settings at *https://crates.io/me* and
|
||||
retrieve your API key. Then run the `cargo login` command with your API key,
|
||||
@ -525,7 +436,7 @@ $ cargo login abcdefghijklmnopqrstuvwxyz012345
|
||||
```
|
||||
|
||||
This command will inform Cargo of your API token and store it locally in
|
||||
*~/.cargo/credentials*. Note that this token is a **secret** and should not be
|
||||
*~/.cargo/credentials*. Note that this token is a *secret* and should not be
|
||||
shared with anyone else. If it is shared with anyone for any reason, you should
|
||||
revoke it and generate a new token on Crates.io.
|
||||
|
||||
@ -535,9 +446,6 @@ Now you have an account, and let’s say you already have a crate you want to
|
||||
publish. Before publishing, you’ll need to add some metadata to your crate by
|
||||
adding it to the `[package]` section of the crate’s *Cargo.toml*.
|
||||
|
||||
<!-- Is this right, everything here is relevant to cargo.toml?-->
|
||||
<!-- Yep /Carol -->
|
||||
|
||||
Your crate will first need a unique name. While you’re working on a crate
|
||||
locally, you may name a crate whatever you’d like. However, crate names on
|
||||
Crates.io are allocated on a first-come-first-serve basis. Once a crate name is
|
||||
@ -546,6 +454,8 @@ you’d like to use on the site to find out if it has been taken. If it hasn’t
|
||||
edit the name in *Cargo.toml* under `[package]` to have the name you want to
|
||||
use for publishing like so:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[package]
|
||||
name = "guessing_game"
|
||||
@ -575,16 +485,14 @@ Package Data Exchange (SPDX) at *http://spdx.org/licenses/* lists the
|
||||
identifiers you can use for this value. For example, to specify that you’ve
|
||||
licensed your crate using the MIT License, add the `MIT` identifier:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[package]
|
||||
name = "guessing_game"
|
||||
license = "MIT"
|
||||
```
|
||||
|
||||
<!-- Can you give an example of what a license identifier value looks like? It
|
||||
is a alphanumerical code? -->
|
||||
<!-- Mostly, yeah. /Carol -->
|
||||
|
||||
If you want to use a license that doesn’t appear in the SPDX, you need to place
|
||||
the text of that license in a file, include the file in your project, then use
|
||||
`license-file` to specify the name of that file instead of using the `license`
|
||||
@ -592,7 +500,7 @@ key.
|
||||
|
||||
Guidance on which license is right for your project is out of scope for this
|
||||
book. Many people in the Rust community choose to license their projects in the
|
||||
same way as Rust itself, with a dual license of `MIT/Apache-2.0`---this
|
||||
same way as Rust itself, with a dual license of `MIT/Apache-2.0`—this
|
||||
demonstrates that you can also specify multiple license identifiers separated
|
||||
by a slash.
|
||||
|
||||
@ -600,6 +508,8 @@ So, with a unique name, the version, and author details that `cargo new` added
|
||||
when you created the crate, your description, and the license you chose added,
|
||||
the *Cargo.toml* for a project that’s ready to publish might look like this:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[package]
|
||||
name = "guessing_game"
|
||||
@ -611,7 +521,7 @@ license = "MIT/Apache-2.0"
|
||||
[dependencies]
|
||||
```
|
||||
|
||||
Cargo's documentation at *http://doc.rust-lang.org/cargo/* describes other
|
||||
Cargo’s documentation at *https://doc.rust-lang.org/cargo/* describes other
|
||||
metadata you can specify to ensure your crate can be discovered and used more
|
||||
easily!
|
||||
|
||||
@ -648,10 +558,9 @@ anyone can easily add your crate as a dependency of their project.
|
||||
|
||||
When you’ve made changes to your crate and are ready to release a new version,
|
||||
you change the `version` value specified in your *Cargo.toml* and republish.
|
||||
Use the Semantic Versioning rules at *http://semver.org/* to decide what an appropriate next
|
||||
version number is based on the kinds of changes you’ve made. Then run `cargo
|
||||
publish` to upload the new version.
|
||||
|
||||
Use the Semantic Versioning rules at *http://semver.org/* to decide what an
|
||||
appropriate next version number is based on the kinds of changes you’ve made.
|
||||
Then run `cargo publish` to upload the new version.
|
||||
|
||||
### Removing Versions from Crates.io with `cargo yank`
|
||||
|
||||
@ -660,11 +569,11 @@ projects from adding them as a new dependency. This is useful when a version of
|
||||
a crate ends up being broken for one reason or another. For situations such as
|
||||
this, Cargo supports *yanking* a version of a crate.
|
||||
|
||||
Yanking a version prevents new projects from starting to depend on that
|
||||
version while allowing all existing projects that depend on it to continue to
|
||||
download and depend on that version. Essentially, a yank means that all
|
||||
projects with a *Cargo.lock* will not break, while any future *Cargo.lock*
|
||||
files generated will not use the yanked version.
|
||||
Yanking a version prevents new projects from starting to depend on that version
|
||||
while allowing all existing projects that depend on it to continue to download
|
||||
and depend on that version. Essentially, a yank means that all projects with a
|
||||
*Cargo.lock* will not break, while any future *Cargo.lock* files generated will
|
||||
not use the yanked version.
|
||||
|
||||
To yank a version of a crate, run `cargo yank` and specify which version you
|
||||
want to yank:
|
||||
@ -711,6 +620,8 @@ We need to modify the binary package’s *Cargo.toml* and add a `[workspace]`
|
||||
section to tell Cargo the `adder` package is a workspace. Add this at the
|
||||
bottom of the file:
|
||||
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[workspace]
|
||||
```
|
||||
@ -719,26 +630,20 @@ Like many Cargo features, workspaces support convention over configuration: we
|
||||
don’t need to add anything more than this to *Cargo.toml* to define our
|
||||
workspace as long as we follow the convention.
|
||||
|
||||
<!-- Below -- any crates what depends on, specifically? The program? -->
|
||||
<!-- They're all programs. We mean the top-level crate in the workspace here,
|
||||
I've tried to clarify. /Carol -->
|
||||
|
||||
### Specifying Workspace Dependencies
|
||||
|
||||
The workspace convention says any crates in any subdirectories that the
|
||||
top-level crate depends on are part of the workspace. Any crate, whether in a
|
||||
workspace or not, can specify that it has a dependency on a crate in a local
|
||||
directory by using the `path` attribute on the dependency specification in
|
||||
*Cargo.toml*. If a crate has the `[workspace]` key and we specify path
|
||||
dependencies where the paths are subdirectories of the crate’s directory, those
|
||||
dependent crates will be considered part of the workspace. Let’s specify in the
|
||||
*Cargo.toml* for the top-level `adder` crate that it will have a dependency on
|
||||
an `add-one` crate that will be in the `add-one` subdirectory, by changing
|
||||
*Cargo.toml* to look like this:
|
||||
By default, Cargo will include all transitive path dependencies. A *path
|
||||
dependency* is when any crate, whether in a workspace or not, specifies that it
|
||||
has a dependency on a crate in a local directory by using the `path` attribute
|
||||
on the dependency specification in *Cargo.toml*. If a crate has the
|
||||
`[workspace]` key, or if the crate is itself part of a workspace, and we
|
||||
specify path dependencies where the paths are subdirectories of the crate’s
|
||||
directory, those dependent crates will be considered part of the workspace.
|
||||
Let’s specify in the *Cargo.toml* for the top-level `adder` crate that it will
|
||||
have a dependency on an `add-one` crate that will be in the `add-one`
|
||||
subdirectory, by changing *Cargo.toml* to look like this:
|
||||
|
||||
<!-- Above, what is the path dependency actually doing here, can you fill out
|
||||
the paragraph above? -->
|
||||
<!-- done /Carol -->
|
||||
Filename: Cargo.toml
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
@ -751,10 +656,6 @@ and are assumed to come from Crates.io.
|
||||
|
||||
### Creating the Second Crate in the Workspace
|
||||
|
||||
<!-- You can see I'm adding headings, here, trying to add some more navigable
|
||||
structure -- can you improve these? I'm not sure mine are accurate -->
|
||||
<!-- Yep! /Carol -->
|
||||
|
||||
Next, while in the `adder` directory, generate an `add-one` crate:
|
||||
|
||||
```
|
||||
@ -784,12 +685,11 @@ pub fn add_one(x: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
<!-- below -- Where are we adding the extern crate line? -->
|
||||
<!-- at the top, where all the extern crate lines go and as illustrated by the listing /Carol -->
|
||||
|
||||
Open up *src/main.rs* for `adder` and add an `extern crate` line at the top of
|
||||
the file to bring the new `add-one` library crate into scope. Then change the
|
||||
`main` function to call the `add_one` function, as in Listing 14-12:
|
||||
`main` function to call the `add_one` function, as in Listing 14-11:
|
||||
|
||||
Filename: src/main.rs
|
||||
|
||||
```
|
||||
extern crate add_one;
|
||||
@ -800,7 +700,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Listing 14-12: Using the `add-one` library crate from the `adder` crate
|
||||
Listing 14-11: Using the `add-one` library crate from the `adder` crate
|
||||
|
||||
Let’s build the `adder` crate by running `cargo build` in the *adder* directory!
|
||||
|
||||
@ -836,14 +736,6 @@ its own *target* directory. By sharing one *target* directory, the crates in
|
||||
the workspace can avoid rebuilding the other crates in the workspace more than
|
||||
necessary.
|
||||
|
||||
<!-- Above -- I have no idea what this means for our project here, can you put
|
||||
it in more practical terms, or otherwise maybe just explain what this means for
|
||||
the user? -->
|
||||
<!-- I added more explanation for the target directory in this section and
|
||||
added more explanation for the Cargo.lock in the next section, since the
|
||||
Cargo.lock advantages aren't as visible until you start adding dependencies on
|
||||
external crates. What do you think? /Carol -->
|
||||
|
||||
#### Depending on an External Crate in a Workspace
|
||||
|
||||
Also notice the workspace only has one *Cargo.lock*, rather than having a
|
||||
@ -1001,9 +893,6 @@ does not have an `--all` flag or a `-p` flag, so it is necessary to change to
|
||||
each crate’s directory and run `cargo publish` on each crate in the workspace
|
||||
in order to publish them.
|
||||
|
||||
<!-- What does that mean, we have to publish them all one at a time?-->
|
||||
<!-- Yep, we've tried to clarify /Carol -->
|
||||
|
||||
Now try adding an `add-two` crate to this workspace in a similar way as the
|
||||
`add-one` crate for some more practice!
|
||||
|
||||
@ -1024,13 +913,10 @@ target that isn’t runnable on its own but is suitable for including within
|
||||
other programs. Usually, crates have information in the *README* file about
|
||||
whether a crate is a library, has a binary target, or both.
|
||||
|
||||
<!-- What is a binary target, and how do you know if a package has one? -->
|
||||
<!-- Added /Carol -->
|
||||
|
||||
All binaries from `cargo install` are put into the installation root’s *bin*
|
||||
folder. If you installed Rust using *rustup.rs* and don’t have any custom
|
||||
configurations, this will be `$HOME/.cargo/bin`. Add that directory to your
|
||||
`$PATH` to be able to run programs you’ve gotten through `cargo install`.
|
||||
configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in
|
||||
your `$PATH` to be able to run programs you’ve gotten through `cargo install`.
|
||||
|
||||
For example, we mentioned in Chapter 12 that there’s a Rust implementation of
|
||||
the `grep` tool for searching files called `ripgrep`. If we want to install
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -171,25 +171,25 @@ Any such expression always has the `unit` type.
|
||||
|
||||
#### Operator precedence
|
||||
|
||||
The precedence of Rust binary operators is ordered as follows, going from
|
||||
strong to weak:
|
||||
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.
|
||||
|
||||
```text
|
||||
as :
|
||||
* / %
|
||||
+ -
|
||||
<< >>
|
||||
&
|
||||
^
|
||||
|
|
||||
== != < > <= >=
|
||||
&&
|
||||
||
|
||||
.. ...
|
||||
<-
|
||||
=
|
||||
```
|
||||
|
||||
Operators at the same precedence level are evaluated left-to-right. Unary
|
||||
operators have the same precedence level and are stronger than any of the
|
||||
binary operators.
|
||||
| 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 |
|
||||
|
@ -92,10 +92,10 @@ says, “I’m declaring a function named `main` that has no parameters and retu
|
||||
nothing.” If there were parameters, their names would go inside the
|
||||
parentheses, `(` and `)`.
|
||||
|
||||
Also note that the function body is wrapped in curly braces, `{` and `}`. Rust
|
||||
requires these around all function bodies. It’s considered good style to put
|
||||
the opening curly brace on the same line as the function declaration, with one
|
||||
space in between.
|
||||
Also note that the function body is wrapped in curly brackets, `{` and `}`.
|
||||
Rust requires these around all function bodies. It’s considered good style to
|
||||
put the opening curly bracket on the same line as the function declaration,
|
||||
with one space in between.
|
||||
|
||||
Inside the `main` function:
|
||||
|
||||
@ -319,7 +319,7 @@ $ cargo build
|
||||
```
|
||||
|
||||
This should have created an executable file in *target/debug/hello_cargo* (or
|
||||
*target\debug\hello_cargo.exe* on Windows), which you can run with this command:
|
||||
*target\\debug\\hello_cargo.exe* on Windows), which you can run with this command:
|
||||
|
||||
```text
|
||||
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
|
||||
|
@ -60,6 +60,7 @@ using the `cargo run` command:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Hello, world!
|
||||
```
|
||||
@ -134,8 +135,8 @@ println!("Guess the number!");
|
||||
println!("Please input your guess.");
|
||||
```
|
||||
|
||||
This code is just printing a prompt stating what the game is and requesting
|
||||
input from the user.
|
||||
This code is printing a prompt stating what the game is and requesting input
|
||||
from the user.
|
||||
|
||||
### Storing Values with Variables
|
||||
|
||||
@ -284,21 +285,24 @@ If we don’t call `expect`, the program will compile, but we’ll get a warning
|
||||
```text
|
||||
$ cargo build
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
src/main.rs:10:5: 10:39 warning: unused result which must be used,
|
||||
#[warn(unused_must_use)] on by default
|
||||
src/main.rs:10 io::stdin().read_line(&mut guess);
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
warning: unused `std::result::Result` which must be used
|
||||
--> src/main.rs:10:5
|
||||
|
|
||||
10 | io::stdin().read_line(&mut guess);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: #[warn(unused_must_use)] on by default
|
||||
```
|
||||
|
||||
Rust warns that we haven’t used the `Result` value returned from `read_line`,
|
||||
indicating that the program hasn’t handled a possible error. The right way to
|
||||
suppress the warning is to actually write error handling, but since we just
|
||||
want to crash this program when a problem occurs, we can use `expect`. You’ll
|
||||
learn about recovering from errors in Chapter 9.
|
||||
suppress the warning is to actually write error handling, but since we want to
|
||||
crash this program when a problem occurs, we can use `expect`. You’ll learn
|
||||
about recovering from errors in Chapter 9.
|
||||
|
||||
### Printing Values with `println!` Placeholders
|
||||
|
||||
Aside from the closing curly brace, there’s only one more line to discuss in
|
||||
Aside from the closing curly brackets, there’s only one more line to discuss in
|
||||
the code added so far, which is the following:
|
||||
|
||||
```rust,ignore
|
||||
@ -328,6 +332,7 @@ Let’s test the first part of the guessing game. You can run it using
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
Please input your guess.
|
||||
@ -391,6 +396,7 @@ $ cargo build
|
||||
Compiling libc v0.2.14
|
||||
Compiling rand v0.3.14
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
```
|
||||
|
||||
<span class="caption">Listing 2-2: The output from running `cargo build` after
|
||||
@ -565,6 +571,7 @@ Try running the program a few times:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
@ -758,13 +765,12 @@ We bind `guess` to the expression `guess.trim().parse()`. The `guess` in the
|
||||
expression refers to the original `guess` that was a `String` with the input in
|
||||
it. The `trim` method on a `String` instance will eliminate any whitespace at
|
||||
the beginning and end. `u32` can only contain numerical characters, but the
|
||||
user must press the <span class="keystroke">return</span> key to satisfy
|
||||
`read_line`. When the user presses <span class="keystroke">return</span>, a
|
||||
newline character is added to the string. For example, if the user types
|
||||
<span class="keystroke">5</span> and presses <span class="keystroke">
|
||||
return</span>, `guess` looks like this: `5\n`. The `\n` represents “newline,”
|
||||
the <span class="keystroke">return</span> key. The `trim` method eliminates
|
||||
`\n`, resulting in just `5`.
|
||||
user must press the <span class="keystroke">enter</span> key to satisfy
|
||||
`read_line`. When the user presses <span class="keystroke">enter</span>, a
|
||||
newline character is added to the string. For example, if the user types <span
|
||||
class="keystroke">5</span> and presses <span class="keystroke"> enter</span>,
|
||||
`guess` looks like this: `5\n`. The `\n` represents “newline,” the enter key.
|
||||
The `trim` method eliminates `\n`, resulting in just `5`.
|
||||
|
||||
The [`parse` method on strings][parse]<!-- ignore --> parses a string into some
|
||||
kind of number. Because this method can parse a variety of number types, we
|
||||
@ -795,6 +801,7 @@ Let’s run the program now!
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
|
@ -83,6 +83,7 @@ When we run this program, we get the following:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 5
|
||||
The value of x is: 6
|
||||
@ -174,6 +175,7 @@ When you run this program, it will output the following:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 12
|
||||
```
|
||||
|
@ -21,13 +21,14 @@ error, which means the compiler needs more information from us to know which
|
||||
possible type we want to use:
|
||||
|
||||
```text
|
||||
error[E0282]: unable to infer enough type information about `_`
|
||||
error[E0282]: type annotations needed
|
||||
--> src/main.rs:2:9
|
||||
|
|
||||
2 | let guess = "42".parse().expect("Not a number!");
|
||||
| ^^^^^ cannot infer type for `_`
|
||||
|
|
||||
= note: type annotations or generic parameter binding required
|
||||
| ^^^^^
|
||||
| |
|
||||
| cannot infer type for `_`
|
||||
| consider giving `guess` a type
|
||||
```
|
||||
|
||||
You’ll see different type annotations as we discuss the various data types.
|
||||
@ -42,11 +43,11 @@ work in Rust.
|
||||
#### Integer Types
|
||||
|
||||
An *integer* is a number without a fractional component. We used one integer
|
||||
type earlier in this chapter, the `i32` type. This type declaration indicates
|
||||
that the value it’s associated with should be a signed integer (hence the `i`,
|
||||
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1
|
||||
shows the built-in integer types in Rust. Each variant in the Signed and
|
||||
Unsigned columns (for example, *i32*) can be used to declare the type of an
|
||||
type earlier in this chapter, the `u32` type. This type declaration indicates
|
||||
that the value it’s associated with should be an unsigned integer (signed
|
||||
integer types start with `i` instead of `u`) that takes up 32 bits of space.
|
||||
Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
|
||||
and Unsigned columns (for example, *i16*) can be used to declare the type of an
|
||||
integer value.
|
||||
|
||||
<span class="caption">Table 3-1: Integer Types in Rust</span>
|
||||
@ -104,12 +105,8 @@ you’d use `isize` or `usize` is when indexing some sort of collection.
|
||||
Rust also has two primitive types for *floating-point numbers*, which are
|
||||
numbers with decimal points. Rust’s floating-point types are `f32` and `f64`,
|
||||
which are 32 bits and 64 bits in size, respectively. The default type is `f64`
|
||||
because it’s roughly the same speed as `f32` but is capable of more precision.
|
||||
It’s possible to use an `f64` type on 32-bit systems, but it will be slower
|
||||
than using an `f32` type on those systems. Most of the time, trading potential
|
||||
worse performance for better precision is a reasonable initial choice, and you
|
||||
should benchmark your code if you suspect floating-point size is a problem in
|
||||
your situation.
|
||||
because on modern CPUs it’s roughly the same speed as `f32` but is capable of
|
||||
more precision.
|
||||
|
||||
Here’s an example that shows floating-point numbers in action:
|
||||
|
||||
@ -181,7 +178,8 @@ section.
|
||||
|
||||
So far we’ve only worked with numbers, but Rust supports letters too. Rust’s
|
||||
`char` type is the language’s most primitive alphabetic type, and the following
|
||||
code shows one way to use it:
|
||||
code shows one way to use it. Note that the `char` type is specified with
|
||||
single quotes, as opposed to strings that use double quotes:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -348,6 +346,7 @@ Running this code using `cargo run` produces the following result:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling arrays v0.1.0 (file:///projects/arrays)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/arrays`
|
||||
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
||||
10', src/main.rs:6
|
||||
|
@ -24,8 +24,8 @@ fn another_function() {
|
||||
```
|
||||
|
||||
Function definitions in Rust start with `fn` and have a set of parentheses
|
||||
after the function name. The curly braces tell the compiler where the function
|
||||
body begins and ends.
|
||||
after the function name. The curly brackets tell the compiler where the
|
||||
function body begins and ends.
|
||||
|
||||
We can call any function we’ve defined by entering its name followed by a set
|
||||
of parentheses. Because `another_function` is defined in the program, it can be
|
||||
@ -41,6 +41,7 @@ should see the following output:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
|
||||
Running `target/debug/functions`
|
||||
Hello, world!
|
||||
Another function.
|
||||
@ -80,13 +81,14 @@ Try running this program; you should get the following output:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
The declaration of `another_function` has one parameter named `x`. The type of
|
||||
`x` is specified as `i32`. When `5` is passed to `another_function`, the
|
||||
`println!` macro puts `5` where the pair of curly braces were in the format
|
||||
`println!` macro puts `5` where the pair of curly brackets were in the format
|
||||
string.
|
||||
|
||||
In function signatures, you *must* declare the type of each parameter. This is
|
||||
@ -122,6 +124,7 @@ project’s *src/main.rs* file with the preceding example, and run it using
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
The value of y is: 6
|
||||
@ -226,19 +229,21 @@ This expression:
|
||||
```
|
||||
|
||||
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
|
||||
as part of the `let` statement. Note the line without a semicolon at the end,
|
||||
unlike most of the lines you’ve seen so far. Expressions do not include ending
|
||||
semicolons. If you add a semicolon to the end of an expression, you turn it
|
||||
into a statement, which will then not return a value. Keep this in mind as you
|
||||
explore function return values and expressions next.
|
||||
as part of the `let` statement. Note the `x + 1` line without a semicolon at
|
||||
the end, unlike most of the lines you’ve seen so far. Expressions do not
|
||||
include ending semicolons. If you add a semicolon to the end of an expression,
|
||||
you turn it into a statement, which will then not return a value. Keep this in
|
||||
mind as you explore function return values and expressions next.
|
||||
|
||||
### Functions with Return Values
|
||||
|
||||
Functions can return values to the code that calls them. We don’t name return
|
||||
values, but we do declare their type after an arrow (`->`). In Rust, the return
|
||||
value of the function is synonymous with the value of the final expression in
|
||||
the block of the body of a function. Here’s an example of a function that
|
||||
returns a value:
|
||||
the block of the body of a function. You can return early from a function by
|
||||
using the `return` keyword and specifying a value, but most functions return
|
||||
the last expression implicitly. Here’s an example of a function that returns a
|
||||
value:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -262,6 +267,7 @@ running this code; the output should look like this:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
@ -322,16 +328,12 @@ error[E0308]: mismatched types
|
||||
7 | fn plus_one(x: i32) -> i32 {
|
||||
| ____________________________^
|
||||
8 | | x + 1;
|
||||
| | - help: consider removing this semicolon
|
||||
9 | | }
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
help: consider removing this semicolon:
|
||||
--> src/main.rs:8:10
|
||||
|
|
||||
8 | x + 1;
|
||||
| ^
|
||||
```
|
||||
|
||||
The main error message, “mismatched types,” reveals the core issue with this
|
||||
|
@ -43,4 +43,5 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
That’s all there is to comments. They’re not particularly complicated.
|
||||
Rust also has another kind of comment, documentation comments, which we’ll
|
||||
discuss in Chapter 14.
|
||||
|
@ -35,7 +35,7 @@ All `if` expressions start with the keyword `if`, which is followed by a
|
||||
condition. In this case, the condition checks whether or not the variable
|
||||
`number` has a value less than 5. The block of code we want to execute if the
|
||||
condition is true is placed immediately after the condition inside curly
|
||||
braces. Blocks of code associated with the conditions in `if` expressions are
|
||||
brackets. Blocks of code associated with the conditions in `if` expressions are
|
||||
sometimes called *arms*, just like the arms in `match` expressions that we
|
||||
discussed in the “Comparing the Guess to the Secret Number” section of
|
||||
Chapter 2. Optionally, we can also include an `else` expression, which we chose
|
||||
@ -49,6 +49,7 @@ Try running this code; you should see the following output:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was true
|
||||
```
|
||||
@ -65,6 +66,7 @@ Run the program again, and look at the output:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
condition was false
|
||||
```
|
||||
@ -149,6 +151,7 @@ see the following output:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/branches`
|
||||
number is divisible by 3
|
||||
```
|
||||
@ -193,6 +196,7 @@ expression. Run this code to see what happens:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/branches`
|
||||
The value of number is: 5
|
||||
```
|
||||
@ -238,7 +242,7 @@ error[E0308]: if and else have incompatible types
|
||||
| |_____^ expected integral variable, found reference
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found type `&'static str`
|
||||
found type `&str`
|
||||
```
|
||||
|
||||
The expression in the `if` block evaluates to an integer, and the expression in
|
||||
@ -285,6 +289,7 @@ continual loop. Give it a try:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
|
||||
Running `target/debug/loops`
|
||||
again!
|
||||
again!
|
||||
@ -339,7 +344,7 @@ true, the code runs; otherwise, it exits the loop.
|
||||
#### Looping Through a Collection with `for`
|
||||
|
||||
You could use the `while` construct to loop over the elements of a collection,
|
||||
such as an array. For example:
|
||||
such as an array. For example, let’s look at Listing 3-5:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -367,6 +372,7 @@ element in the array:
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
|
||||
Running `target/debug/loops`
|
||||
the value is: 10
|
||||
the value is: 20
|
||||
@ -385,7 +391,7 @@ code to perform the conditional check on every element on every iteration
|
||||
through the loop.
|
||||
|
||||
As a more efficient alternative, you can use a `for` loop and execute some code
|
||||
for each item in a collection. A `for` loop looks like this:
|
||||
for each item in a collection. A `for` loop looks like this code in Listing 3-6:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -5,7 +5,7 @@ together multiple related values that make up a meaningful group. If you’re
|
||||
familiar with an object-oriented language, a *struct* is like an object’s data
|
||||
attributes. In this chapter, we’ll compare and contrast tuples with structs,
|
||||
demonstrate how to use structs, and discuss how to define methods and
|
||||
associated functions on structs to specify behavior associated with a struct’s
|
||||
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the
|
||||
building blocks for creating new types in your program’s domain to take full
|
||||
advantage of Rust’s compile time type checking.
|
||||
associated functions to specify behavior associated with a struct’s data. The
|
||||
struct and *enum* (which is discussed in Chapter 6) concepts are the building
|
||||
blocks for creating new types in your program’s domain to take full advantage
|
||||
of Rust’s compile time type checking.
|
||||
|
@ -8,7 +8,7 @@ the data to specify or access the values of an instance.
|
||||
|
||||
To define a struct, we enter the keyword `struct` and name the entire struct. A
|
||||
struct’s name should describe the significance of the pieces of data being
|
||||
grouped together. Then, inside curly braces, we define the names and types of
|
||||
grouped together. Then, inside curly brackets, we define the names and types of
|
||||
the pieces of data, which we call *fields*. For example, Listing 5-1 shows a
|
||||
struct to store information about a user account:
|
||||
|
||||
@ -25,7 +25,7 @@ struct User {
|
||||
|
||||
To use a struct after we’ve defined it, we create an *instance* of that struct
|
||||
by specifying concrete values for each of the fields. We create an instance by
|
||||
stating the name of the struct, and then add curly braces containing `key:
|
||||
stating the name of the struct, and then add curly brackets containing `key:
|
||||
value` pairs where the keys are the names of the fields and the values are the
|
||||
data we want to store in those fields. We don’t have to specify the fields in
|
||||
the same order in which we declared them in the struct. In other words, the
|
||||
@ -54,9 +54,9 @@ struct</span>
|
||||
|
||||
To get a specific value from a struct, we can use dot notation. If we wanted
|
||||
just this user’s email address, we can use `user1.email` wherever we want to
|
||||
use this value. To change a value in a struct, if the instance is mutable, we
|
||||
can use the dot notation and assign into a particular field. Listing 5-3 shows
|
||||
how to change the value in the `email` field of a mutable `User` instance:
|
||||
use this value. If the instance is mutable, we can change a value by using the
|
||||
dot notation and assigning into a particular field. Listing 5-3 shows how to
|
||||
change the value in the `email` field of a mutable `User` instance:
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
@ -79,11 +79,14 @@ user1.email = String::from("anotheremail@example.com");
|
||||
<span class="caption">Listing 5-3: Changing the value in the `email` field of a
|
||||
`User` instance</span>
|
||||
|
||||
Like any expression, we can implicitly return a new instance of a struct from a
|
||||
function by constructing the new instance as the last expression in the
|
||||
function body. Listing 5-4 shows a `build_user` function that returns a `User`
|
||||
instance with the given `email` and `username`. The `active` field gets the
|
||||
value of `true`, and the `sign_in_count` gets a value of `1`.
|
||||
Note that the entire instance must be mutable; Rust doesn’t allow us to mark
|
||||
only certain fields as mutable. Also note that as with any expression, we can
|
||||
construct a new instance of the struct as the last expression in the function
|
||||
body to implicitly return that new instance.
|
||||
|
||||
Listing 5-4 shows a `build_user` function that returns a `User` instance with
|
||||
the given email and username. The `active` field gets the value of `true`, and
|
||||
the `sign_in_count` gets a value of `1`.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
@ -106,24 +109,17 @@ fn build_user(email: String, username: String) -> User {
|
||||
<span class="caption">Listing 5-4: A `build_user` function that takes an email
|
||||
and username and returns a `User` instance</span>
|
||||
|
||||
Repeating the `email` field name and `email` variable, and the same for
|
||||
`username`, is a bit tedious, though. It makes sense to name the function
|
||||
arguments with the same name as the struct fields, but if the struct had more
|
||||
fields, repeating each name would get even more annoying. Luckily, there’s a
|
||||
convenient shorthand!
|
||||
It makes sense to name the function arguments with the same name as the struct
|
||||
fields, but having to repeat the `email` and `username` field names and
|
||||
variables is a bit tedious. If the struct had more fields, repeating each name
|
||||
would get even more annoying. Luckily, there's a convenient shorthand!
|
||||
|
||||
### Field Init Shorthand when Variables Have the Same Name as Fields
|
||||
### Using the Field Init Shorthand when Variables and Fields Have the Same Name
|
||||
|
||||
If you have variables with the same names as struct fields, you can use *field
|
||||
init shorthand*. This can make functions that create new instances of structs
|
||||
more concise.
|
||||
|
||||
In Listing 5-4, the parameter names `email` and `username` are the same as the
|
||||
`User` struct’s field names `email` and `username`. Because the names are
|
||||
exactly the same, we can write `build_user` without the repetition of `email`
|
||||
and `username` as shown in Listing 5-5. This version of `build_user` behaves
|
||||
the same way as the one in Listing 5-4. The field init syntax can make cases
|
||||
like this shorter to write, especially when structs have many fields.
|
||||
Because the parameter names and the struct field names are exactly the same in
|
||||
Listing 5-4, we can use the *field init shorthand* syntax to rewrite
|
||||
`build_user` so that it behaves exactly the same but doesn’t have the
|
||||
repetition of `email` and `username` in the way shown in Listing 5-5.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
@ -144,16 +140,23 @@ fn build_user(email: String, username: String) -> User {
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-5: A `build_user` function that uses field init
|
||||
syntax since the `email` and `username` parameters have the same name as struct
|
||||
fields</span>
|
||||
shorthand since the `email` and `username` parameters have the same name as
|
||||
struct fields</span>
|
||||
|
||||
Here, we’re creating a new instance of the `User` struct, which has a field
|
||||
named `email`. We want to set the `email` field’s value to the value in the
|
||||
`email` parameter of the `build_user` function. Because the `email` field and
|
||||
the `email` parameter have the same name, we only need to write `email` rather
|
||||
than `email: email`.
|
||||
|
||||
### Creating Instances From Other Instances With Struct Update Syntax
|
||||
|
||||
It’s often useful to create a new instance from an old instance, using most of
|
||||
the old instance’s values but changing some. Listing 5-6 shows an example of
|
||||
creating a new `User` instance in `user2` by setting the values of `email` and
|
||||
`username` but using the same values for the rest of the fields from the
|
||||
`user1` instance we created in Listing 5-2:
|
||||
It’s often useful to create a new instance of a struct that uses most of an old
|
||||
instance’s values, but changes some. We do this using *struct update syntax*.
|
||||
|
||||
First, Listing 5-6 shows how we create a new `User` instance in `user2` without
|
||||
the update syntax. We set new values for `email` and `username`, but otherwise
|
||||
use the same values from `user1` that we created in Listing 5-2:
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
@ -178,15 +181,12 @@ let user2 = User {
|
||||
};
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-6: Creating a new `User` instance, `user2`, and
|
||||
setting some fields to the values of the same fields from `user1`</span>
|
||||
<span class="caption">Listing 5-6: Creating a new `User` instance using some of
|
||||
the values from `user1`</span>
|
||||
|
||||
The *struct update syntax* achieves the same effect as the code in Listing 5-6
|
||||
using less code. The struct update syntax uses `..` to specify that the
|
||||
remaining fields not set explicitly should have the same value as the fields in
|
||||
the given instance. The code in Listing 5-7 also creates an instance in `user2`
|
||||
that has a different value for `email` and `username` but has the same values
|
||||
for the `active` and `sign_in_count` fields that `user1` has:
|
||||
Using struct update syntax, we can achieve the same effect with less code,
|
||||
shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
|
||||
explicitly set should have the same value as the fields in the given instance.
|
||||
|
||||
```rust
|
||||
# struct User {
|
||||
@ -214,14 +214,22 @@ let user2 = User {
|
||||
`email` and `username` values for a `User` instance but use the rest of the
|
||||
values from the fields of the instance in the `user1` variable</span>
|
||||
|
||||
The code in Listing 5-7 also creates an instance in `user2` that has a
|
||||
different value for `email` and `username` but has the same values for the
|
||||
`active` and `sign_in_count` fields from `user1`.
|
||||
|
||||
### Tuple Structs without Named Fields to Create Different Types
|
||||
|
||||
We can also define structs that look similar to tuples, called *tuple structs*,
|
||||
that have the added meaning the struct name provides, but don’t have names
|
||||
associated with their fields, just the types of the fields. The definition of a
|
||||
tuple struct still starts with the `struct` keyword and the struct name, which
|
||||
are followed by the types in the tuple. For example, here are definitions and
|
||||
usages of tuple structs named `Color` and `Point`:
|
||||
associated with their fields, just the types of the fields. Tuple structs are
|
||||
useful when you want to give the whole tuple a name and make the tuple be a
|
||||
different type than other tuples, but naming each field as in a regular struct
|
||||
would be verbose or redundant.
|
||||
|
||||
To define a tuple struct you start with the `struct` keyword and the struct
|
||||
name followed by the types in the tuple. For example, here are definitions and
|
||||
usages of two tuple structs named `Color` and `Point`:
|
||||
|
||||
```rust
|
||||
struct Color(i32, i32, i32);
|
||||
@ -233,8 +241,12 @@ let origin = Point(0, 0, 0);
|
||||
|
||||
Note that the `black` and `origin` values are different types, since they’re
|
||||
instances of different tuple structs. Each struct we define is its own type,
|
||||
even though the fields within the struct have the same types. Otherwise, tuple
|
||||
struct instances behave like tuples, which we covered in Chapter 3.
|
||||
even though the fields within the struct have the same types. For example, a
|
||||
function that takes a parameter of type `Color` cannot take a `Point` as an
|
||||
argument, even though both types are made up of three `i32` values. Otherwise,
|
||||
tuple struct instances behave like tuples, which we covered in Chapter 3: you
|
||||
can destructure them into their individual pieces, you can use a `.` followed
|
||||
by the index to access an individual value, and so on.
|
||||
|
||||
### Unit-Like Structs without Any Fields
|
||||
|
||||
|
@ -5,7 +5,7 @@ calculates the area of a rectangle. We’ll start with single variables, and the
|
||||
refactor the program until we’re using structs instead.
|
||||
|
||||
Let’s make a new binary project with Cargo called *rectangles* that will take
|
||||
the length and width of a rectangle specified in pixels and will calculate the
|
||||
the width and height of a rectangle specified in pixels and will calculate the
|
||||
area of the rectangle. Listing 5-8 shows a short program with one way of doing
|
||||
just that in our project’s *src/main.rs*:
|
||||
|
||||
@ -13,22 +13,22 @@ just that in our project’s *src/main.rs*:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let length1 = 50;
|
||||
let width1 = 30;
|
||||
let height1 = 50;
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(length1, width1)
|
||||
area(width1, height1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(length: u32, width: u32) -> u32 {
|
||||
length * width
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
width * height
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-8: Calculating the area of a rectangle
|
||||
specified by its length and width in separate variables</span>
|
||||
specified by its width and height in separate variables</span>
|
||||
|
||||
Now, run this program using `cargo run`:
|
||||
|
||||
@ -39,20 +39,20 @@ The area of the rectangle is 1500 square pixels.
|
||||
### Refactoring with Tuples
|
||||
|
||||
Even though Listing 5-8 works and figures out the area of the rectangle by
|
||||
calling the `area` function with each dimension, we can do better. The length
|
||||
and the width are related to each other because together they describe one
|
||||
calling the `area` function with each dimension, we can do better. The width
|
||||
and the height are related to each other because together they describe one
|
||||
rectangle.
|
||||
|
||||
The issue with this method is evident in the signature of `area`:
|
||||
|
||||
```rust,ignore
|
||||
fn area(length: u32, width: u32) -> u32 {
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
```
|
||||
|
||||
The `area` function is supposed to calculate the area of one rectangle, but the
|
||||
function we wrote has two parameters. The parameters are related, but that’s
|
||||
not expressed anywhere in our program. It would be more readable and more
|
||||
manageable to group length and width together. We’ve already discussed one way
|
||||
manageable to group width and height together. We’ve already discussed one way
|
||||
we might do that in the Grouping Values into Tuples section of Chapter 3 on
|
||||
page XX: by using tuples. Listing 5-9 shows another version of our program that
|
||||
uses tuples:
|
||||
@ -61,7 +61,7 @@ uses tuples:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let rect1 = (50, 30);
|
||||
let rect1 = (30, 50);
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -74,7 +74,7 @@ fn area(dimensions: (u32, u32)) -> u32 {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-8: Specifying the length and width of the
|
||||
<span class="caption">Listing 5-8: Specifying the width and height of the
|
||||
rectangle with a tuple</span>
|
||||
|
||||
In one way, this program is better. Tuples let us add a bit of structure, and
|
||||
@ -82,9 +82,9 @@ we’re now passing just one argument. But in another way this version is less
|
||||
clear: tuples don’t name their elements, so our calculation has become more
|
||||
confusing because we have to index into the parts of the tuple.
|
||||
|
||||
It doesn’t matter if we mix up length and width for the area calculation, but
|
||||
It doesn’t matter if we mix up width and height for the area calculation, but
|
||||
if we want to draw the rectangle on the screen, it would matter! We would have
|
||||
to keep in mind that `length` is the tuple index `0` and `width` is the tuple
|
||||
to keep in mind that `width` is the tuple index `0` and `height` is the tuple
|
||||
index `1`. If someone else worked on this code, they would have to figure this
|
||||
out and keep it in mind as well. It would be easy to forget or mix up these
|
||||
values and cause errors, because we haven’t conveyed the meaning of our data in
|
||||
@ -100,12 +100,12 @@ parts, as shown in Listing 5-10:
|
||||
|
||||
```rust
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -114,16 +114,16 @@ fn main() {
|
||||
}
|
||||
|
||||
fn area(rectangle: &Rectangle) -> u32 {
|
||||
rectangle.length * rectangle.width
|
||||
rectangle.width * rectangle.height
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 5-10: Defining a `Rectangle` struct</span>
|
||||
|
||||
Here we’ve defined a struct and named it `Rectangle`. Inside the `{}` we
|
||||
defined the fields as `length` and `width`, both of which have type `u32`. Then
|
||||
in `main` we create a particular instance of a `Rectangle` that has a length of
|
||||
50 and a width of 30.
|
||||
defined the fields as `width` and `height`, both of which have type `u32`. Then
|
||||
in `main` we create a particular instance of a `Rectangle` that has a width of
|
||||
30 and a height of 50.
|
||||
|
||||
Our `area` function is now defined with one parameter, which we’ve named
|
||||
`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
|
||||
@ -132,10 +132,10 @@ take ownership of it. This way, `main` retains its ownership and can continue
|
||||
using `rect1`, which is the reason we use the `&` in the function signature and
|
||||
where we call the function.
|
||||
|
||||
The `area` function accesses the `length` and `width` fields of the `Rectangle`
|
||||
The `area` function accesses the `width` and `height` fields of the `Rectangle`
|
||||
instance. Our function signature for `area` now indicates exactly what we mean:
|
||||
calculate the area of a `Rectangle` using its `length` and `width` fields. This
|
||||
conveys that the length and width are related to each other, and gives
|
||||
calculate the area of a `Rectangle` using its `width` and `height` fields. This
|
||||
conveys that the width and height are related to each other, and gives
|
||||
descriptive names to the values rather than using the tuple index values of `0`
|
||||
and `1`—a win for clarity.
|
||||
|
||||
@ -150,12 +150,12 @@ chapters:
|
||||
|
||||
```rust,ignore
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {}", rect1);
|
||||
}
|
||||
@ -176,7 +176,7 @@ direct end user consumption. The primitive types we’ve seen so far implement
|
||||
`Display` by default, because there’s only one way you’d want to show a `1` or
|
||||
any other primitive type to a user. But with structs, the way `println!` should
|
||||
format the output is less clear because there are more display possibilities:
|
||||
do you want commas or not? Do you want to print the curly braces? Should all
|
||||
do you want commas or not? Do you want to print the curly brackets? Should all
|
||||
the fields be shown? Due to this ambiguity, Rust doesn’t try to guess what we
|
||||
want and structs don’t have a provided implementation of `Display`.
|
||||
|
||||
@ -216,12 +216,12 @@ definition, as shown in Listing 5-12:
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!("rect1 is {:?}", rect1);
|
||||
}
|
||||
@ -234,7 +234,7 @@ Now when we run the program, we won’t get any errors and we’ll see the
|
||||
following output:
|
||||
|
||||
```text
|
||||
rect1 is Rectangle { length: 50, width: 30 }
|
||||
rect1 is Rectangle { width: 30, height: 50 }
|
||||
```
|
||||
|
||||
Nice! It’s not the prettiest output, but it shows the values of all the fields
|
||||
@ -245,8 +245,8 @@ When we use the `{:#?}` style in the example, the output will look like this:
|
||||
|
||||
```text
|
||||
rect1 is Rectangle {
|
||||
length: 50,
|
||||
width: 30
|
||||
width: 30,
|
||||
height: 50
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -19,18 +19,18 @@ in Listing 5-13:
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
@ -44,7 +44,7 @@ fn main() {
|
||||
|
||||
To define the function within the context of `Rectangle`, we start an `impl`
|
||||
(*implementation*) block. Then we move the `area` function within the `impl`
|
||||
curly braces and change the first (and in this case, only) parameter to be
|
||||
curly brackets and change the first (and in this case, only) parameter to be
|
||||
`self` in the signature and everywhere within the body. In `main` where we
|
||||
called the `area` function and passed `rect1` as an argument, we can instead
|
||||
use *method syntax* to call the `area` method on our `Rectangle` instance.
|
||||
@ -131,9 +131,9 @@ method:
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let rect1 = Rectangle { length: 50, width: 30 };
|
||||
let rect2 = Rectangle { length: 40, width: 10 };
|
||||
let rect3 = Rectangle { length: 45, width: 60 };
|
||||
let rect1 = Rectangle { width: 30, height: 50 };
|
||||
let rect2 = Rectangle { width: 10, height: 40 };
|
||||
let rect3 = Rectangle { width: 60, height: 45 };
|
||||
|
||||
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
|
||||
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
|
||||
@ -161,8 +161,8 @@ parameter will be by looking at the code that calls the method:
|
||||
read `rect2` (rather than write, which would mean we’d need a mutable borrow),
|
||||
and we want `main` to retain ownership of `rect2` so we can use it again after
|
||||
calling the `can_hold` method. The return value of `can_hold` will be a
|
||||
boolean, and the implementation will check whether the length and width of
|
||||
`self` are both greater than the length and width of the other `Rectangle`,
|
||||
boolean, and the implementation will check whether the width and height of
|
||||
`self` are both greater than the width and height of the other `Rectangle`,
|
||||
respectively. Let’s add the new `can_hold` method to the `impl` block from
|
||||
Listing 5-13, shown in Listing 5-15:
|
||||
|
||||
@ -171,17 +171,17 @@ Listing 5-13, shown in Listing 5-15:
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# length: u32,
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.length > other.length && self.width > other.width
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -205,7 +205,7 @@ function.
|
||||
|
||||
Associated functions are often used for constructors that will return a new
|
||||
instance of the struct. For example, we could provide an associated function
|
||||
that would have one dimension parameter and use that as both length and width,
|
||||
that would have one dimension parameter and use that as both width and height,
|
||||
thus making it easier to create a square `Rectangle` rather than having to
|
||||
specify the same value twice:
|
||||
|
||||
@ -214,13 +214,13 @@ specify the same value twice:
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# length: u32,
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn square(size: u32) -> Rectangle {
|
||||
Rectangle { length: size, width: size }
|
||||
Rectangle { width: size, height: size }
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -239,19 +239,19 @@ in its own `impl` block:
|
||||
```rust
|
||||
# #[derive(Debug)]
|
||||
# struct Rectangle {
|
||||
# length: u32,
|
||||
# width: u32,
|
||||
# height: u32,
|
||||
# }
|
||||
#
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.length * self.width
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.length > other.length && self.width > other.width
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -249,7 +249,7 @@ m.call();
|
||||
|
||||
The body of the method would use `self` to get the value that we called the
|
||||
method on. In this example, we’ve created a variable `m` that has the value
|
||||
`Message::Write("hello")`, and that is what `self` will be in the body of the
|
||||
`Message::Write(String::from("hello"))`, and that is what `self` will be in the body of the
|
||||
`call` method when `m.call()` runs.
|
||||
|
||||
Let’s look at another enum in the standard library that is very common and
|
||||
|
@ -62,9 +62,9 @@ The code associated with each arm is an expression, and the resulting value of
|
||||
the expression in the matching arm is the value that gets returned for the
|
||||
entire `match` expression.
|
||||
|
||||
Curly braces typically aren’t used if the match arm code is short, as it is in
|
||||
Listing 6-3 where each arm just returns a value. If you want to run multiple
|
||||
lines of code in a match arm, you can use curly braces. For example, the
|
||||
Curly brackets typically aren’t used if the match arm code is short, as it is
|
||||
in Listing 6-3 where each arm just returns a value. If you want to run multiple
|
||||
lines of code in a match arm, you can use curly brackets. For example, the
|
||||
following code would print out “Lucky penny!” every time the method was called
|
||||
with a `Coin::Penny` but would still return the last value of the block, `1`:
|
||||
|
||||
@ -99,7 +99,7 @@ As an example, let’s change one of our enum variants to hold data inside it.
|
||||
From 1999 through 2008, the United States minted quarters with different
|
||||
designs for each of the 50 states on one side. No other coins got state
|
||||
designs, so only quarters have this extra value. We can add this information to
|
||||
our `enum` by changing the `Quarter` variant to include a `State` value stored
|
||||
our `enum` by changing the `Quarter` variant to include a `UsState` value stored
|
||||
inside it, which we’ve done here in Listing 6-4:
|
||||
|
||||
```rust
|
||||
|
@ -14,7 +14,7 @@ you can choose whether those definitions are visible outside their module
|
||||
(public) or not (private). Here’s an overview of how modules work:
|
||||
|
||||
* The `mod` keyword declares a new module. Code within the module appears
|
||||
either immediately following this declaration within curly braces or in
|
||||
either immediately following this declaration within curly brackets or in
|
||||
another file.
|
||||
* By default, functions, types, constants, and modules are private. The `pub`
|
||||
keyword makes an item public and therefore visible outside its namespace.
|
||||
|
@ -63,7 +63,7 @@ mod network {
|
||||
```
|
||||
|
||||
After the `mod` keyword, we put the name of the module, `network`, and then a
|
||||
block of code in curly braces. Everything inside this block is inside the
|
||||
block of code in curly brackets. Everything inside this block is inside the
|
||||
namespace `network`. In this case, we have a single function, `connect`. If we
|
||||
wanted to call this function from a script outside the `network` module, we
|
||||
would need to specify the module and use the namespace syntax `::`, like so:
|
||||
@ -383,7 +383,7 @@ previously, we can do what the note suggests:
|
||||
|
||||
1. Make a new *directory* named *network*, the parent module’s name.
|
||||
2. Move the *src/network.rs* file into the new *network* directory, and
|
||||
rename *src/network/mod.rs*.
|
||||
rename it to *src/network/mod.rs*.
|
||||
3. Move the submodule file *src/server.rs* into the *network* directory.
|
||||
|
||||
Here are commands to carry out these steps:
|
||||
|
@ -83,7 +83,7 @@ directly.
|
||||
Because enums also form a sort of namespace like modules, we can import an
|
||||
enum’s variants with `use` as well. For any kind of `use` statement, if you’re
|
||||
importing multiple items from one namespace, you can list them using curly
|
||||
braces and commas in the last position, like so:
|
||||
brackets and commas in the last position, like so:
|
||||
|
||||
```rust
|
||||
enum TrafficLight {
|
||||
|
@ -1,18 +1,18 @@
|
||||
# Common Collections
|
||||
|
||||
Rust’s standard library includes a number of really useful data structures
|
||||
called *collections*. Most other data types represent one specific value, but
|
||||
Rust’s standard library includes a number of very useful data structures called
|
||||
*collections*. Most other data types represent one specific value, but
|
||||
collections can contain multiple values. Unlike the built-in array and tuple
|
||||
types, the data these collections point to is stored on the heap, which means
|
||||
the amount of data does not need to be known at compile time and can grow or
|
||||
shrink as the program runs. Each kind of collection has different capabilities
|
||||
and costs, and choosing an appropriate one for the situation you’re in is a
|
||||
skill you’ll develop over time. In this chapter, we’ll go over three
|
||||
collections which are used very often in Rust programs:
|
||||
and costs, and choosing an appropriate one for your current situation is a
|
||||
skill you’ll develop over time. In this chapter, we’ll discuss three
|
||||
collections that are used very often in Rust programs:
|
||||
|
||||
* A *vector* allows us to store a variable number of values next to each other.
|
||||
* A *string* is a collection of characters. We’ve seen the `String` type
|
||||
before, but we’ll talk about it in depth now.
|
||||
* A *string* is a collection of characters. We’ve discussed the `String` type
|
||||
previously, but in this chapter we’ll talk about it in depth.
|
||||
* A *hash map* allows us to associate a value with a particular key. It’s a
|
||||
particular implementation of the more general data structure called a *map*.
|
||||
|
||||
@ -21,5 +21,5 @@ see [the documentation][collections].
|
||||
|
||||
[collections]: ../../std/collections/index.html
|
||||
|
||||
We’re going to discuss how to create and update vectors, strings, and hash
|
||||
maps, as well as what makes each special.
|
||||
We’ll discuss how to create and update vectors, strings, and hash maps, as well
|
||||
as what makes each special.
|
||||
|
@ -1,46 +1,55 @@
|
||||
## Vectors
|
||||
|
||||
The first type we’ll look at is `Vec<T>`, also known as a *vector*. Vectors
|
||||
allow us to store more than one value in a single data structure that puts all
|
||||
the values next to each other in memory. Vectors can only store values of the
|
||||
same type. They are useful in situations where you have a list of items, such
|
||||
as the lines of text in a file or the prices of items in a shopping cart.
|
||||
The first collection type we’ll look at is `Vec<T>`, also known as a *vector*.
|
||||
Vectors allow us to store more than one value in a single data structure that
|
||||
puts all the values next to each other in memory. Vectors can only store values
|
||||
of the same type. They are useful in situations in which you have a list of
|
||||
items, such as the lines of text in a file or the prices of items in a shopping
|
||||
cart.
|
||||
|
||||
### Creating a New Vector
|
||||
|
||||
To create a new, empty vector, we can call the `Vec::new` function:
|
||||
To create a new, empty vector, we can call the `Vec::new` function as shown in
|
||||
Listing 8-1:
|
||||
|
||||
```rust
|
||||
let v: Vec<i32> = Vec::new();
|
||||
```
|
||||
|
||||
Note that we added a type annotation here. Since we aren’t inserting any values
|
||||
into this vector, Rust doesn’t know what kind of elements we intend to store.
|
||||
This is an important point. Vectors are homogeneous: they may store many
|
||||
values, but those values must all be the same type. Vectors are implemented
|
||||
using generics, which Chapter 10 will cover how to use in your own types. For
|
||||
now, all you need to know is that the `Vec` type provided by the standard
|
||||
library can hold any type, and when a specific `Vec` holds a specific type, the
|
||||
type goes within angle brackets. We’ve told Rust that the `Vec` in `v` will
|
||||
<span class="caption">Listing 8-1: Creating a new, empty vector to hold values
|
||||
of type `i32`</span>
|
||||
|
||||
Note that we added a type annotation here. Because we aren’t inserting any
|
||||
values into this vector, Rust doesn’t know what kind of elements we intend to
|
||||
store. This is an important point. Vectors are implemented using generics;
|
||||
we’ll cover how to use generics with your own types in Chapter 10. For now,
|
||||
know that the `Vec<T>` type provided by the standard library can hold any type,
|
||||
and when a specific vector holds a specific type, the type is specified within
|
||||
angle brackets. In Listing 8-1, we’ve told Rust that the `Vec<T>` in `v` will
|
||||
hold elements of the `i32` type.
|
||||
|
||||
In real code, Rust can infer the type of value we want to store once we insert
|
||||
values, so you rarely need to do this type annotation. It’s more common to
|
||||
create a `Vec` that has initial values, and Rust provides the `vec!` macro for
|
||||
convenience. The macro will create a new `Vec` that holds the values we give
|
||||
it. This will create a new `Vec<i32>` that holds the values `1`, `2`, and `3`:
|
||||
In more realistic code, Rust can often infer the type of value we want to store
|
||||
once we insert values, so you rarely need to do this type annotation. It’s more
|
||||
common to create a `Vec<T>` that has initial values, and Rust provides the
|
||||
`vec!` macro for convenience. The macro will create a new vector that holds the
|
||||
values we give it. Listing 8-2 creates a new `Vec<i32>` that holds the values
|
||||
`1`, `2`, and `3`:
|
||||
|
||||
```rust
|
||||
let v = vec![1, 2, 3];
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-2: Creating a new vector containing
|
||||
values</span>
|
||||
|
||||
Because we’ve given initial `i32` values, Rust can infer that the type of `v`
|
||||
is `Vec<i32>`, and the type annotation isn’t necessary. Let’s look at how to
|
||||
modify a vector next.
|
||||
is `Vec<i32>`, and the type annotation isn’t necessary. Next, we’ll look at how
|
||||
to modify a vector.
|
||||
|
||||
### Updating a Vector
|
||||
|
||||
To create a vector then add elements to it, we can use the `push` method:
|
||||
To create a vector and then add elements to it, we can use the `push` method as
|
||||
shown in Listing 8-3:
|
||||
|
||||
```rust
|
||||
let mut v = Vec::new();
|
||||
@ -51,14 +60,18 @@ v.push(7);
|
||||
v.push(8);
|
||||
```
|
||||
|
||||
As with any variable as we discussed in Chapter 3, if we want to be able to
|
||||
change its value, we need to make it mutable with the `mut` keyword. The
|
||||
<span class="caption">Listing 8-3: Using the `push` method to add values to a
|
||||
vector</span>
|
||||
|
||||
As with any variable, as discussed in Chapter 3, if we want to be able to
|
||||
change its value, we need to make it mutable using the `mut` keyword. The
|
||||
numbers we place inside are all of type `i32`, and Rust infers this from the
|
||||
data, so we don’t need the `Vec<i32>` annotation.
|
||||
|
||||
### Dropping a Vector Drops its Elements
|
||||
### Dropping a Vector Drops Its Elements
|
||||
|
||||
Like any other `struct`, a vector will be freed when it goes out of scope:
|
||||
Like any other `struct`, a vector will be freed when it goes out of scope, as
|
||||
annotated in Listing 8-4:
|
||||
|
||||
```rust
|
||||
{
|
||||
@ -69,9 +82,12 @@ Like any other `struct`, a vector will be freed when it goes out of scope:
|
||||
} // <- v goes out of scope and is freed here
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-4: Showing where the vector and its elements
|
||||
are dropped</span>
|
||||
|
||||
When the vector gets dropped, all of its contents will also be dropped, meaning
|
||||
those integers it holds will be cleaned up. This may seem like a
|
||||
straightforward point, but can get a little more complicated once we start to
|
||||
straightforward point but can get a bit more complicated when we start to
|
||||
introduce references to the elements of the vector. Let’s tackle that next!
|
||||
|
||||
### Reading Elements of Vectors
|
||||
@ -81,7 +97,7 @@ read their contents is a good next step. There are two ways to reference a
|
||||
value stored in a vector. In the examples, we’ve annotated the types of the
|
||||
values that are returned from these functions for extra clarity.
|
||||
|
||||
This example shows both methods of accessing a value in a vector either with
|
||||
Listing 8-5 shows both methods of accessing a value in a vector either with
|
||||
indexing syntax or the `get` method:
|
||||
|
||||
```rust
|
||||
@ -91,17 +107,20 @@ let third: &i32 = &v[2];
|
||||
let third: Option<&i32> = v.get(2);
|
||||
```
|
||||
|
||||
There are a few things to note here. First, that we use the index value of `2`
|
||||
to get the third element: vectors are indexed by number, starting at zero.
|
||||
Second, the two different ways to get the third element are: using `&` and
|
||||
`[]`, which gives us a reference, or using the `get` method with the index
|
||||
passed as an argument, which gives us an `Option<&T>`.
|
||||
<span class="caption">Listing 8-5: Using indexing syntax or the `get` method to
|
||||
access an item in a vector</span>
|
||||
|
||||
The reason Rust has two ways to reference an element is so that you can choose
|
||||
how the program behaves when you try to use an index value that the vector
|
||||
doesn’t have an element for. As an example, what should a program do if it has
|
||||
a vector that holds five elements then tries to access an element at index 100
|
||||
like this:
|
||||
Note two details here. First, we use the index value of `2` to get the third
|
||||
element: vectors are indexed by number, starting at zero. Second, the two
|
||||
different ways to get the third element are by using `&` and `[]`, which gives
|
||||
us a reference, or by using the `get` method with the index passed as an
|
||||
argument, which gives us an `Option<&T>`.
|
||||
|
||||
The reason Rust has two ways to reference an element is so you can choose how
|
||||
the program behaves when you try to use an index value that the vector doesn’t
|
||||
have an element for. As an example, what should a program do if it has a vector
|
||||
that holds five elements and then tries to access an element at index 100, as
|
||||
shown in Listing 8-6:
|
||||
|
||||
```rust,should_panic
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
@ -110,31 +129,32 @@ let does_not_exist = &v[100];
|
||||
let does_not_exist = v.get(100);
|
||||
```
|
||||
|
||||
When you run this, you will find that with the first `[]` method, Rust will
|
||||
cause a `panic!` when a non-existent element is referenced. This method would
|
||||
be preferable if you want your program to consider an attempt to access an
|
||||
element past the end of the vector to be a fatal error that should crash the
|
||||
program.
|
||||
<span class="caption">Listing 8-6: Attempting to access the element at index
|
||||
100 in a vector containing 5 elements</span>
|
||||
|
||||
When the `get` method is passed an index that is outside the array, it will
|
||||
return `None` without panicking. You would use this if accessing an element
|
||||
beyond the range of the vector will happen occasionally under normal
|
||||
circumstances. Your code can then have logic to handle having either
|
||||
`Some(&element)` or `None`, as we discussed in Chapter 6. For example, the
|
||||
index could be coming from a person entering a number. If they accidentally
|
||||
enter a number that’s too large and your program gets a `None` value, you could
|
||||
tell the user how many items are in the current `Vec` and give them another
|
||||
chance to enter a valid value. That would be more user-friendly than crashing
|
||||
the program for a typo!
|
||||
When you run this code, the first `[]` method will cause a `panic!` because it
|
||||
references a nonexistent element. This method is best used when you want your
|
||||
program to consider an attempt to access an element past the end of the vector
|
||||
to be a fatal error that crashes the program.
|
||||
|
||||
When the `get` method is passed an index that is outside the vector, it returns
|
||||
`None` without panicking. You would use this method if accessing an element
|
||||
beyond the range of the vector happens occasionally under normal circumstances.
|
||||
Your code will then have logic to handle having either `Some(&element)` or
|
||||
`None`, as discussed in Chapter 6. For example, the index could be coming from
|
||||
a person entering a number. If they accidentally enter a number that’s too
|
||||
large and the program gets a `None` value, you could tell the user how many
|
||||
items are in the current `Vec` and give them another chance to enter a valid
|
||||
value. That would be more user-friendly than crashing the program due to a typo!
|
||||
|
||||
#### Invalid References
|
||||
|
||||
Once the program has a valid reference, the borrow checker will enforce the
|
||||
ownership and borrowing rules covered in Chapter 4 to ensure this reference and
|
||||
any other references to the contents of the vector stay valid. Recall the rule
|
||||
that says we can’t have mutable and immutable references in the same scope.
|
||||
That rule applies in this example, where we hold an immutable reference to the
|
||||
first element in a vector and try to add an element to the end:
|
||||
When the program has a valid reference, the borrow checker enforces the
|
||||
ownership and borrowing rules (covered in Chapter 4) to ensure this reference
|
||||
and any other references to the contents of the vector remain valid. Recall the
|
||||
rule that states we can’t have mutable and immutable references in the same
|
||||
scope. That rule applies in Listing 8-7 where we hold an immutable reference to
|
||||
the first element in a vector and try to add an element to the end:
|
||||
|
||||
```rust,ignore
|
||||
let mut v = vec![1, 2, 3, 4, 5];
|
||||
@ -144,7 +164,10 @@ let first = &v[0];
|
||||
v.push(6);
|
||||
```
|
||||
|
||||
Compiling this will give us this error:
|
||||
<span class="caption">Listing 8-7: Attempting to add an element to a vector
|
||||
while holding a reference to an item</span>
|
||||
|
||||
Compiling this code will result in this error:
|
||||
|
||||
```text
|
||||
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
|
||||
@ -159,33 +182,67 @@ immutable
|
||||
| - immutable borrow ends here
|
||||
```
|
||||
|
||||
This code might look like it should work: why should a reference to the first
|
||||
element care about what changes about the end of the vector? The reason why
|
||||
this code isn’t allowed is due to the way vectors work. Adding a new element
|
||||
The code in Listing 8-7 might look like it should work: why should a reference
|
||||
to the first element care about what changes at the end of the vector? The
|
||||
reason behind this error is due to the way vectors work: adding a new element
|
||||
onto the end of the vector might require allocating new memory and copying the
|
||||
old elements over to the new space, in the circumstance that there isn’t enough
|
||||
room to put all the elements next to each other where the vector was. In that
|
||||
case, the reference to the first element would be pointing to deallocated
|
||||
memory. The borrowing rules prevent programs from ending up in that situation.
|
||||
old elements to the new space if there isn’t enough room to put all the
|
||||
elements next to each other where the vector was. In that case, the reference
|
||||
to the first element would be pointing to deallocated memory. The borrowing
|
||||
rules prevent programs from ending up in that situation.
|
||||
|
||||
> Note: For more on this, see The Nomicon at
|
||||
*https://doc.rust-lang.org/stable/nomicon/vec.html*.
|
||||
> Note: For more on the implementation details of the `Vec<T>` type, see “The
|
||||
> Nomicon” at https://doc.rust-lang.org/stable/nomicon/vec.html.
|
||||
|
||||
### Iterating Over the Values in a Vector
|
||||
|
||||
If we want to access each element in a vector in turn, rather than using
|
||||
indexing to access one element, we can iterate through all of the elements.
|
||||
Listing 8-8 shows how to use a `for` loop to get immutable references to each
|
||||
element in a vector of `i32` values and print them out:
|
||||
|
||||
```rust
|
||||
let v = vec![100, 32, 57];
|
||||
for i in &v {
|
||||
println!("{}", i);
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-8: Printing each element in a vector by
|
||||
iterating over the elements using a `for` loop</span>
|
||||
|
||||
We can also iterate over mutable references to each element in a mutable vector
|
||||
if we want to make changes to all the elements. The `for` loop in Listing 8-9
|
||||
will add `50` to each element:
|
||||
|
||||
```rust
|
||||
let mut v = vec![100, 32, 57];
|
||||
for i in &mut v {
|
||||
*i += 50;
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-9: Iterating over mutable references to
|
||||
elements in a vector</span>
|
||||
|
||||
In order to change the value that the mutable reference refers to, before we
|
||||
can use the `+=` operator with `i`, we have to use the dereference operator
|
||||
(`*`) to get to the value.
|
||||
|
||||
### Using an Enum to Store Multiple Types
|
||||
|
||||
At the beginning of this chapter, we said that vectors can only store values
|
||||
that are all the same type. This can be inconvenient; there are definitely use
|
||||
cases for needing to store a list of things of different types. Luckily, the
|
||||
variants of an enum are all defined under the same enum type, so when we need
|
||||
to store elements of a different type in a vector, we can define and use an
|
||||
enum!
|
||||
that are the same type. This can be inconvenient; there are definitely use
|
||||
cases for needing to store a list of items of different types. Fortunately, the
|
||||
variants of an enum are defined under the same enum type, so when we need to
|
||||
store elements of a different type in a vector, we can define and use an enum!
|
||||
|
||||
For example, let’s say we want to get values from a row in a spreadsheet, where
|
||||
some of the columns in the row contain integers, some floating point numbers,
|
||||
For example, let’s say we want to get values from a row in a spreadsheet where
|
||||
some of the columns in the row contain integers, some floating-point numbers,
|
||||
and some strings. We can define an enum whose variants will hold the different
|
||||
value types, and then all of the enum variants will be considered the same
|
||||
type, that of the enum. Then we can create a vector that holds that enum and
|
||||
so, ultimately, holds different types:
|
||||
value types, and then all the enum variants will be considered the same type,
|
||||
that of the enum. Then we can create a vector that holds that enum and so,
|
||||
ultimately, holds different types. We’ve demonstrated this in Listing 8-8:
|
||||
|
||||
```rust
|
||||
enum SpreadsheetCell {
|
||||
@ -201,25 +258,24 @@ let row = vec![
|
||||
];
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-1: Defining an enum to be able to hold
|
||||
different types of data in a vector</span>
|
||||
<span class="caption">Listing 8-8: Defining an `enum` to store values of
|
||||
different types in one vector</span>
|
||||
|
||||
The reason Rust needs to know exactly what types will be in the vector at
|
||||
compile time is so that it knows exactly how much memory on the heap will be
|
||||
needed to store each element. A secondary advantage to this is that we can be
|
||||
explicit about what types are allowed in this vector. If Rust allowed a vector
|
||||
to hold any type, there would be a chance that one or more of the types would
|
||||
cause errors with the operations performed on the elements of the vector. Using
|
||||
an enum plus a `match` means that Rust will ensure at compile time that we
|
||||
always handle every possible case, as we discussed in Chapter 6.
|
||||
The reason Rust needs to know what types will be in the vector at compile time
|
||||
is so it knows exactly how much memory on the heap will be needed to store each
|
||||
element. A secondary advantage is that we can be explicit about what types are
|
||||
allowed in this vector. If Rust allowed a vector to hold any type, there would
|
||||
be a chance that one or more of the types would cause errors with the
|
||||
operations performed on the elements of the vector. Using an enum plus a
|
||||
`match` expression means that Rust will ensure at compile time that we always
|
||||
handle every possible case, as discussed in Chapter 6.
|
||||
|
||||
If you don’t know at the time that you’re writing a program the exhaustive set
|
||||
of types the program will get at runtime to store in a vector, the enum
|
||||
technique won’t work. Instead, you can use a trait object, which we’ll cover in
|
||||
Chapter 17.
|
||||
If you don’t know when you’re writing a program the exhaustive set of types the
|
||||
program will get at runtime to store in a vector, the enum technique won’t
|
||||
work. Instead, you can use a trait object, which we’ll cover in Chapter 17.
|
||||
|
||||
Now that we’ve gone over some of the most common ways to use vectors, be sure
|
||||
to take a look at the API documentation for all of the many useful methods
|
||||
defined on `Vec` by the standard library. For example, in addition to `push`
|
||||
there’s a `pop` method that will remove and return the last element. Let’s move
|
||||
on to the next collection type: `String`!
|
||||
Now that we’ve discussed some of the most common ways to use vectors, be sure
|
||||
to review the API documentation for all the many useful methods defined on
|
||||
`Vec` by the standard library. For example, in addition to `push`, a `pop`
|
||||
method removes and returns the last element. Let’s move on to the next
|
||||
collection type: `String`!
|
||||
|
@ -1,61 +1,62 @@
|
||||
## Strings
|
||||
|
||||
We’ve already talked about strings a bunch in Chapter 4, but let’s take a more
|
||||
in-depth look at them now. Strings are an area that new Rustaceans commonly get
|
||||
stuck on. This is due to a combination of three things: Rust’s propensity for
|
||||
making sure to expose possible errors, strings being a more complicated data
|
||||
structure than many programmers give them credit for, and UTF-8. These things
|
||||
combine in a way that can seem difficult when coming from other languages.
|
||||
We talked about strings in Chapter 4, but we’ll look at them in more depth now.
|
||||
New Rustaceans commonly get stuck on strings due to a combination of three
|
||||
concepts: Rust’s propensity for exposing possible errors, strings being a more
|
||||
complicated data structure than many programmers give them credit for, and
|
||||
UTF-8. These concepts combine in a way that can seem difficult when you’re
|
||||
coming from other programming languages.
|
||||
|
||||
The reason strings are in the collections chapter is that strings are
|
||||
This discussion of strings is in the collections chapter because strings are
|
||||
implemented as a collection of bytes plus some methods to provide useful
|
||||
functionality when those bytes are interpreted as text. In this section, we’ll
|
||||
talk about the operations on `String` that every collection type has, like
|
||||
talk about the operations on `String` that every collection type has, such as
|
||||
creating, updating, and reading. We’ll also discuss the ways in which `String`
|
||||
is different than the other collections, namely how indexing into a `String` is
|
||||
complicated by the differences between how people and computers interpret
|
||||
`String` data.
|
||||
|
||||
### What is a String?
|
||||
### What Is a String?
|
||||
|
||||
Before we can dig into those aspects, we need to talk about what exactly we
|
||||
mean by the term *string*. Rust actually only has one string type in the core
|
||||
language itself: `str`, the string slice, which is usually seen in its borrowed
|
||||
form, `&str`. We talked about *string slices* in Chapter 4: these are a
|
||||
reference to some UTF-8 encoded string data stored elsewhere. String literals,
|
||||
for example, are stored in the binary output of the program, and are therefore
|
||||
string slices.
|
||||
We’ll first define what we mean by the term *string*. Rust has only one string
|
||||
type in the core language, which is the string slice `str` that is usually seen
|
||||
in its borrowed form `&str`. In Chapter 4, we talked about *string slices*,
|
||||
which are references to some UTF-8 encoded string data stored elsewhere. String
|
||||
literals, for example, are stored in the binary output of the program and are
|
||||
therefore string slices.
|
||||
|
||||
The type called `String` is provided in Rust’s standard library rather than
|
||||
coded into the core language, and is a growable, mutable, owned, UTF-8 encoded
|
||||
string type. When Rustaceans talk about “strings” in Rust, they usually mean
|
||||
both the `String` and the string slice `&str` types, not just one of those.
|
||||
This section is largely about `String`, but both these types are used heavily
|
||||
in Rust’s standard library. Both `String` and string slices are UTF-8 encoded.
|
||||
The `String` type is provided in Rust’s standard library rather than coded into
|
||||
the core language and is a growable, mutable, owned, UTF-8 encoded string type.
|
||||
When Rustaceans refer to “strings” in Rust, they usually mean the `String` and
|
||||
the string slice `&str` types, not just one of those types. Although this
|
||||
section is largely about `String`, both types are used heavily in Rust’s
|
||||
standard library and both `String` and string slices are UTF-8 encoded.
|
||||
|
||||
Rust’s standard library also includes a number of other string types, such as
|
||||
`OsString`, `OsStr`, `CString`, and `CStr`. Library crates may provide even
|
||||
`OsString`, `OsStr`, `CString`, and `CStr`. Library crates can provide even
|
||||
more options for storing string data. Similar to the `*String`/`*Str` naming,
|
||||
they often provide an owned and borrowed variant, just like `String`/`&str`.
|
||||
These string types may store different encodings or be represented in memory in
|
||||
a different way, for example. We won’t be talking about these other string
|
||||
These string types can store text in different encodings or be represented in
|
||||
memory in a different way, for example. We won’t discuss these other string
|
||||
types in this chapter; see their API documentation for more about how to use
|
||||
them and when each is appropriate.
|
||||
|
||||
### Creating a New String
|
||||
|
||||
Many of the same operations available with `Vec` are available with `String` as
|
||||
well, starting with the `new` function to create a string, like so:
|
||||
well, starting with the `new` function to create a string, shown in Listing 8-9:
|
||||
|
||||
```rust
|
||||
let mut s = String::new();
|
||||
```
|
||||
|
||||
This creates a new empty string called `s` that we can then load data into.
|
||||
<span class="caption">Listing 8-9: Creating a new, empty `String`</span>
|
||||
|
||||
Often, we’ll have some initial data that we’d like to start the string off
|
||||
This line creates a new empty string called `s` that we can then load data
|
||||
into. Often, we’ll have some initial data that we want to start the string
|
||||
with. For that, we use the `to_string` method, which is available on any type
|
||||
that implements the `Display` trait, which string literals do:
|
||||
that implements the `Display` trait, which string literals do. Listing 8-10
|
||||
shows two examples:
|
||||
|
||||
```rust
|
||||
let data = "initial contents";
|
||||
@ -66,78 +67,104 @@ let s = data.to_string();
|
||||
let s = "initial contents".to_string();
|
||||
```
|
||||
|
||||
This creates a string containing `initial contents`.
|
||||
<span class="caption">Listing 8-10: Using the `to_string` method to create a
|
||||
`String` from a string literal</span>
|
||||
|
||||
This code creates a string containing `initial contents`.
|
||||
|
||||
We can also use the function `String::from` to create a `String` from a string
|
||||
literal. This is equivalent to using `to_string`:
|
||||
literal. The code in Listing 8-11 is equivalent to the code from Listing 8-10
|
||||
that uses `to_string`:
|
||||
|
||||
```rust
|
||||
let s = String::from("initial contents");
|
||||
```
|
||||
|
||||
Because strings are used for so many things, there are many different generic
|
||||
APIs that can be used for strings, so there are a lot of options. Some of them
|
||||
can feel redundant, but they all have their place! In this case, `String::from`
|
||||
and `.to_string` end up doing the exact same thing, so which you choose is a
|
||||
matter of style.
|
||||
<span class="caption">Listing 8-11: Using the `String::from` function to create
|
||||
a `String` from a string literal</span>
|
||||
|
||||
Because strings are used for so many things, we can use many different generic
|
||||
APIs for strings, providing us with a lot of options. Some of them can seem
|
||||
redundant, but they all have their place! In this case, `String::from` and
|
||||
`to_string` do the same thing, so which you choose is a matter of style.
|
||||
|
||||
Remember that strings are UTF-8 encoded, so we can include any properly encoded
|
||||
data in them:
|
||||
data in them, as shown in Listing 8-12:
|
||||
|
||||
```rust
|
||||
let hello = "السلام عليكم";
|
||||
let hello = "Dobrý den";
|
||||
let hello = "Hello";
|
||||
let hello = "שָׁלוֹם";
|
||||
let hello = "नमस्ते";
|
||||
let hello = "こんにちは";
|
||||
let hello = "안녕하세요";
|
||||
let hello = "你好";
|
||||
let hello = "Olá";
|
||||
let hello = "Здравствуйте";
|
||||
let hello = "Hola";
|
||||
let hello = String::from("السلام عليكم");
|
||||
let hello = String::from("Dobrý den");
|
||||
let hello = String::from("Hello");
|
||||
let hello = String::from("שָׁלוֹם");
|
||||
let hello = String::from("नमस्ते");
|
||||
let hello = String::from("こんにちは");
|
||||
let hello = String::from("안녕하세요");
|
||||
let hello = String::from("你好");
|
||||
let hello = String::from("Olá");
|
||||
let hello = String::from("Здравствуйте");
|
||||
let hello = String::from("Hola");
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-12: Storing greetings in different languages in
|
||||
strings</span>
|
||||
|
||||
All of these are valid `String` values.
|
||||
|
||||
### Updating a String
|
||||
|
||||
A `String` can grow in size and its contents can change just like the contents
|
||||
of a `Vec`, by pushing more data into it. In addition, `String` has
|
||||
concatenation operations implemented with the `+` operator for convenience.
|
||||
A `String` can grow in size and its contents can change, just like the contents
|
||||
of a `Vec`, by pushing more data into it. In addition, we can conveniently use
|
||||
the `+` operator or the `format!` macro to concatenate `String` values together.
|
||||
|
||||
#### Appending to a String with Push
|
||||
#### Appending to a String with `push_str` and `push`
|
||||
|
||||
We can grow a `String` by using the `push_str` method to append a string slice:
|
||||
We can grow a `String` by using the `push_str` method to append a string slice,
|
||||
as shown in Listing 8-13:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("foo");
|
||||
s.push_str("bar");
|
||||
```
|
||||
|
||||
`s` will contain “foobar” after these two lines. The `push_str` method takes a
|
||||
<span class="caption">Listing 8-13: Appending a string slice to a `String`
|
||||
using the `push_str` method</span>
|
||||
|
||||
After these two lines, `s` will contain `foobar`. The `push_str` method takes a
|
||||
string slice because we don’t necessarily want to take ownership of the
|
||||
parameter. For example, it would be unfortunate if we weren’t able to use `s2`
|
||||
after appending its contents to `s1`:
|
||||
parameter. For example, the code in Listing 8-14 shows that it would be
|
||||
unfortunate if we weren’t able to use `s2` after appending its contents to `s1`:
|
||||
|
||||
```rust
|
||||
let mut s1 = String::from("foo");
|
||||
let s2 = String::from("bar");
|
||||
let s2 = "bar";
|
||||
s1.push_str(&s2);
|
||||
println!("s2 is {}", s2);
|
||||
```
|
||||
|
||||
The `push` method is defined to have a single character as a parameter and add
|
||||
it to the `String`:
|
||||
<span class="caption">Listing 8-14: Using a string slice after appending its
|
||||
contents to a `String`</span>
|
||||
|
||||
If the `push_str` method took ownership of `s2`, we wouldn’t be able to print
|
||||
out its value on the last line. However, this code works as we’d expect!
|
||||
|
||||
The `push` method takes a single character as a parameter and adds it to the
|
||||
`String`. Listing 8-15 shows code that adds an l to a `String` using the `push`
|
||||
method:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("lo");
|
||||
s.push('l');
|
||||
```
|
||||
|
||||
After this, `s` will contain “lol”.
|
||||
<span class="caption">Listing 8-15: Adding one character to a `String` value
|
||||
using `push`</span>
|
||||
|
||||
#### Concatenation with the + Operator or the `format!` Macro
|
||||
As a result of this code, `s` will contain `lol`.
|
||||
|
||||
Often, we’ll want to combine two existing strings together. One way is to use
|
||||
the `+` operator like this:
|
||||
#### Concatenation with the `+` Operator or the `format!` Macro
|
||||
|
||||
Often, we’ll want to combine two existing strings. One way is to use the `+`
|
||||
operator, as shown in Listing 8-16:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("Hello, ");
|
||||
@ -145,8 +172,11 @@ let s2 = String::from("world!");
|
||||
let s3 = s1 + &s2; // Note that s1 has been moved here and can no longer be used
|
||||
```
|
||||
|
||||
After this code the String `s3` will contain `Hello, world!`. The reason that
|
||||
`s1` is no longer valid after the addition and the reason that we used a
|
||||
<span class="caption">Listing 8-16: Using the `+` operator to combine two
|
||||
`String` values into a new `String` value</span>
|
||||
|
||||
As a result of this code, the string `s3` will contain `Hello, world!`. The
|
||||
reason `s1` is no longer valid after the addition and the reason we used a
|
||||
reference to `s2` has to do with the signature of the method that gets called
|
||||
when we use the `+` operator. The `+` operator uses the `add` method, whose
|
||||
signature looks something like this:
|
||||
@ -155,32 +185,32 @@ signature looks something like this:
|
||||
fn add(self, s: &str) -> String {
|
||||
```
|
||||
|
||||
This isn’t the exact signature that’s in the standard library; there `add` is
|
||||
defined using generics. Here, we’re looking at the signature of `add` with
|
||||
concrete types substituted for the generic ones, which is what happens when we
|
||||
call this method with `String` values. We’ll be discussing generics in
|
||||
Chapter 10. This signature gives us the clues we need to understand the tricky
|
||||
bits of the `+` operator.
|
||||
This isn’t the exact signature that’s in the standard library: in the standard
|
||||
library, `add` is defined using generics. Here, we’re looking at the signature
|
||||
of `add` with concrete types substituted for the generic ones, which is what
|
||||
happens when we call this method with `String` values. We’ll discuss generics
|
||||
in Chapter 10. This signature gives us the clues we need to understand the
|
||||
tricky bits of the `+` operator.
|
||||
|
||||
First of all, `s2` has an `&`, meaning that we are adding a *reference* of the
|
||||
second string to the first string. This is because of the `s` parameter in the
|
||||
`add` function: we can only add a `&str` to a `String`, we can’t add two
|
||||
`String` values together. But wait - the type of `&s2` is `&String`, not
|
||||
`&str`, as specified in the second parameter to `add`. Why does our example
|
||||
compile? We are able to use `&s2` in the call to `add` because a `&String`
|
||||
argument can be *coerced* into a `&str` - when the `add` function is called,
|
||||
Rust uses something called a *deref coercion*, which you could think of here as
|
||||
turning `&s2` into `&s2[..]` for use in the `add` function. We’ll discuss deref
|
||||
coercion in more depth in Chapter 15. Because `add` does not take ownership of
|
||||
the parameter, `s2` will still be a valid `String` after this operation.
|
||||
First, `s2` has an `&`, meaning that we’re adding a *reference* of the second
|
||||
string to the first string because of the `s` parameter in the `add` function:
|
||||
we can only add a `&str` to a `String`; we can’t add two `String` values
|
||||
together. But wait - the type of `&s2` is `&String`, not `&str`, as specified
|
||||
in the second parameter to `add`. Why does Listing 8-16 compile? We are able to
|
||||
use `&s2` in the call to `add` because the compiler can *coerce* the `&String`
|
||||
argument into a `&str`. When we call the `add` method, Rust uses something
|
||||
called a *deref coercion*, which you could think of here as turning `&s2` into
|
||||
`&s2[..]`. We’ll discuss deref coercion in more depth in Chapter 15. Because
|
||||
`add` does not take ownership of the `s` parameter, `s2` will still be a valid
|
||||
`String` after this operation.
|
||||
|
||||
Second, we can see in the signature that `add` takes ownership of `self`,
|
||||
because `self` does *not* have an `&`. This means `s1` in the above example
|
||||
will be moved into the `add` call and no longer be valid after that. So while
|
||||
`let s3 = s1 + &s2;` looks like it will copy both strings and create a new one,
|
||||
this statement actually takes ownership of `s1`, appends a copy of the contents
|
||||
of `s2`, then returns ownership of the result. In other words, it looks like
|
||||
it’s making a lot of copies, but isn’t: the implementation is more efficient
|
||||
because `self` does *not* have an `&`. This means `s1` in Listing 8-16 will be
|
||||
moved into the `add` call and no longer be valid after that. So although `let
|
||||
s3 = s1 + &s2;` looks like it will copy both strings and create a new one, this
|
||||
statement actually takes ownership of `s1`, appends a copy of the contents of
|
||||
`s2`, and then returns ownership of the result. In other words, it looks like
|
||||
it’s making a lot of copies but isn’t: the implementation is more efficient
|
||||
than copying.
|
||||
|
||||
If we need to concatenate multiple strings, the behavior of `+` gets unwieldy:
|
||||
@ -193,8 +223,8 @@ let s3 = String::from("toe");
|
||||
let s = s1 + "-" + &s2 + "-" + &s3;
|
||||
```
|
||||
|
||||
`s` will be “tic-tac-toe” at this point. With all of the `+` and `"`
|
||||
characters, it gets hard to see what’s going on. For more complicated string
|
||||
At this point, `s` will be `tic-tac-toe`. With all of the `+` and `"`
|
||||
characters, it’s difficult to see what’s going on. For more complicated string
|
||||
combining, we can use the `format!` macro:
|
||||
|
||||
```rust
|
||||
@ -205,24 +235,27 @@ let s3 = String::from("toe");
|
||||
let s = format!("{}-{}-{}", s1, s2, s3);
|
||||
```
|
||||
|
||||
This code will also set `s` to “tic-tac-toe”. The `format!` macro works in the
|
||||
same way as `println!`, but instead of printing the output to the screen, it
|
||||
returns a `String` with the contents. This version is much easier to read, and
|
||||
also does not take ownership of any of its parameters.
|
||||
This code also sets `s` to `tic-tac-toe`. The `format!` macro works in the same
|
||||
way as `println!`, but instead of printing the output to the screen, it returns
|
||||
a `String` with the contents. The version of the code using `format!` is much
|
||||
easier to read and also doesn’t take ownership of any of its parameters.
|
||||
|
||||
### Indexing into Strings
|
||||
|
||||
In many other languages, accessing individual characters in a string by
|
||||
referencing them by index is a valid and common operation. In Rust, however, if
|
||||
we try to access parts of a `String` using indexing syntax, we’ll get an error.
|
||||
That is, this code:
|
||||
In many other programming languages, accessing individual characters in a
|
||||
string by referencing them by index is a valid and common operation. However,
|
||||
if we try to access parts of a `String` using indexing syntax in Rust, we’ll
|
||||
get an error. Consider the code in Listing 8-17:
|
||||
|
||||
```rust,ignore
|
||||
let s1 = String::from("hello");
|
||||
let h = s1[0];
|
||||
```
|
||||
|
||||
will result in this error:
|
||||
<span class="caption">Listing 8-17: Attempting to use indexing syntax with a
|
||||
String</span>
|
||||
|
||||
This code will result in the following error:
|
||||
|
||||
```text
|
||||
error: the trait bound `std::string::String: std::ops::Index<_>` is not
|
||||
@ -233,33 +266,31 @@ satisfied [--explain E0277]
|
||||
note: the type `std::string::String` cannot be indexed by `_`
|
||||
```
|
||||
|
||||
The error and the note tell the story: Rust strings don’t support indexing. So
|
||||
the follow-up question is, why not? In order to answer that, we have to talk a
|
||||
bit about how Rust stores strings in memory.
|
||||
The error and the note tell the story: Rust strings don’t support indexing. But
|
||||
why not? To answer that question, we need to discuss how Rust stores strings in
|
||||
memory.
|
||||
|
||||
#### Internal Representation
|
||||
|
||||
A `String` is a wrapper over a `Vec<u8>`. Let’s take a look at some of our
|
||||
properly-encoded UTF-8 example strings from before. First, this one:
|
||||
A `String` is a wrapper over a `Vec<u8>`. Let’s look at some of our properly
|
||||
encoded UTF-8 example strings from Listing 8-12. First, this one:
|
||||
|
||||
```rust
|
||||
let len = String::from("Hola").len();
|
||||
```
|
||||
|
||||
In this case, `len` will be four, which means the `Vec` storing the string
|
||||
“Hola” is four bytes long: each of these letters takes one byte when encoded in
|
||||
UTF-8. What about this example, though?
|
||||
“Hola” is four bytes long. Each of these letters takes one byte when encoded in
|
||||
UTF-8. But what about the following line?
|
||||
|
||||
```rust
|
||||
let len = String::from("Здравствуйте").len();
|
||||
```
|
||||
|
||||
A person asked how long the string is might say 12. However, Rust’s answer
|
||||
is 24. This is the number of bytes that it takes to encode “Здравствуйте” in
|
||||
UTF-8, since each Unicode scalar value takes two bytes of storage. Therefore,
|
||||
an index into the string’s bytes will not always correlate to a valid Unicode
|
||||
scalar value.
|
||||
|
||||
Asked how long the string is, you might say 12. However, Rust’s answer is 24:
|
||||
that’s the number of bytes it takes to encode “Здравствуйте” in UTF-8, because
|
||||
each Unicode scalar value takes two bytes of storage. Therefore, an index into
|
||||
the string’s bytes will not always correlate to a valid Unicode scalar value.
|
||||
To demonstrate, consider this invalid Rust code:
|
||||
|
||||
```rust,ignore
|
||||
@ -270,19 +301,20 @@ let answer = &hello[0];
|
||||
What should the value of `answer` be? Should it be `З`, the first letter? When
|
||||
encoded in UTF-8, the first byte of `З` is `208`, and the second is `151`, so
|
||||
`answer` should in fact be `208`, but `208` is not a valid character on its
|
||||
own. Returning `208` is likely not what a person would want if they asked for
|
||||
the first letter of this string, but that’s the only data that Rust has at byte
|
||||
index 0. Returning the byte value is probably not what people want, even with
|
||||
only Latin letters: `&"hello"[0]` would return `104`, not `h`. To avoid
|
||||
returning an unexpected value and causing bugs that might not be discovered
|
||||
immediately, Rust chooses to not compile this code at all and prevent
|
||||
misunderstandings earlier.
|
||||
own. Returning `208` is likely not what a user would want if they asked for the
|
||||
first letter of this string; however, that’s the only data that Rust has at
|
||||
byte index 0. Returning the byte value is probably not what users want, even if
|
||||
the string contains only Latin letters: if `&"hello"[0]` was valid code that
|
||||
returned the byte value, it would return `104`, not `h`. To avoid returning an
|
||||
unexpected value and causing bugs that might not be discovered immediately,
|
||||
Rust doesn’t compile this code at all and prevents misunderstandings earlier in
|
||||
the development process.
|
||||
|
||||
#### Bytes and Scalar Values and Grapheme Clusters! Oh my!
|
||||
#### Bytes and Scalar Values and Grapheme Clusters! Oh My!
|
||||
|
||||
This leads to another point about UTF-8: there are really three relevant ways
|
||||
to look at strings, from Rust’s perspective: as bytes, scalar values, and
|
||||
grapheme clusters (the closest thing to what people would call *letters*).
|
||||
Another point about UTF-8 is that there are actually three relevant ways to
|
||||
look at strings from Rust’s perspective: as bytes, scalar values, and grapheme
|
||||
clusters (the closest thing to what we would call *letters*).
|
||||
|
||||
If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is
|
||||
ultimately stored as a `Vec` of `u8` values that looks like this:
|
||||
@ -292,7 +324,7 @@ ultimately stored as a `Vec` of `u8` values that looks like this:
|
||||
224, 165, 135]
|
||||
```
|
||||
|
||||
That’s 18 bytes, and is how computers ultimately store this data. If we look at
|
||||
That’s 18 bytes and is how computers ultimately store this data. If we look at
|
||||
them as Unicode scalar values, which are what Rust’s `char` type is, those
|
||||
bytes look like this:
|
||||
|
||||
@ -300,10 +332,10 @@ bytes look like this:
|
||||
['न', 'म', 'स', '्', 'त', 'े']
|
||||
```
|
||||
|
||||
There are six `char` values here, but the fourth and sixth are not letters,
|
||||
There are six `char` values here, but the fourth and sixth are not letters:
|
||||
they’re diacritics that don’t make sense on their own. Finally, if we look at
|
||||
them as grapheme clusters, we’d get what a person would call the four letters
|
||||
that make up this word:
|
||||
that make up the Hindi word:
|
||||
|
||||
```text
|
||||
["न", "म", "स्", "ते"]
|
||||
@ -313,19 +345,21 @@ Rust provides different ways of interpreting the raw string data that computers
|
||||
store so that each program can choose the interpretation it needs, no matter
|
||||
what human language the data is in.
|
||||
|
||||
A final reason Rust does not allow you to index into a `String` to get a
|
||||
A final reason Rust doesn’t allow us to index into a `String` to get a
|
||||
character is that indexing operations are expected to always take constant time
|
||||
(O(1)). It isn’t possible to guarantee that performance with a `String`,
|
||||
though, since Rust would have to walk through the contents from the beginning
|
||||
to the index to determine how many valid characters there were.
|
||||
(O(1)). But it isn’t possible to guarantee that performance with a `String`,
|
||||
because Rust would have to walk through the contents from the beginning to the
|
||||
index to determine how many valid characters there were.
|
||||
|
||||
### Slicing Strings
|
||||
|
||||
Because it’s not clear what the return type of string indexing should be, and
|
||||
it is often a bad idea to index into a string, Rust dissuades you from doing so
|
||||
by asking you to be more specific if you really need it. The way you can be
|
||||
more specific than indexing using `[]` with a single number is using `[]` with
|
||||
a range to create a string slice containing particular bytes:
|
||||
Indexing into a string is often a bad idea because it’s not clear what the
|
||||
return type of the string indexing operation should be: a byte value, a
|
||||
character, a grapheme cluster, or a string slice. Therefore, Rust asks you to
|
||||
be more specific if you really need to use indices to create string slices. To
|
||||
be more specific in your indexing and indicate that you want a string slice,
|
||||
rather than indexing using `[]` with a single number, you can use `[]` with a
|
||||
range to create a string slice containing particular bytes:
|
||||
|
||||
```rust
|
||||
let hello = "Здравствуйте";
|
||||
@ -334,27 +368,28 @@ let s = &hello[0..4];
|
||||
```
|
||||
|
||||
Here, `s` will be a `&str` that contains the first four bytes of the string.
|
||||
Earlier, we mentioned that each of these characters was two bytes, so that
|
||||
means that `s` will be “Зд”.
|
||||
Earlier, we mentioned that each of these characters was two bytes, which means
|
||||
`s` will be `Зд`.
|
||||
|
||||
What would happen if we did `&hello[0..1]`? The answer: it will panic at
|
||||
runtime, in the same way that accessing an invalid index in a vector does:
|
||||
What would happen if we used `&hello[0..1]`? The answer: Rust will panic at
|
||||
runtime in the same way that accessing an invalid index in a vector does:
|
||||
|
||||
```text
|
||||
thread 'main' panicked at 'index 0 and/or 1 in `Здравствуйте` do not lie on
|
||||
character boundary', ../src/libcore/str/mod.rs:1694
|
||||
```
|
||||
|
||||
You should use this with caution, since it can cause your program to crash.
|
||||
You should use ranges to create string slices with caution, because it can
|
||||
crash your program.
|
||||
|
||||
### Methods for Iterating Over Strings
|
||||
|
||||
Luckily, there are other ways we can access elements in a String.
|
||||
Fortunately, we can access elements in a string in other ways.
|
||||
|
||||
If we need to perform operations on individual Unicode scalar values, the best
|
||||
way to do so is to use the `chars` method. Calling `chars` on “नमस्ते”
|
||||
separates out and returns six values of type `char`, and you can iterate over
|
||||
the result in order to access each element:
|
||||
way to do so is to use the `chars` method. Calling `chars` on “नमस्ते” separates
|
||||
out and returns six values of type `char`, and you can iterate over the result
|
||||
in order to access each element:
|
||||
|
||||
```rust
|
||||
for c in "नमस्ते".chars() {
|
||||
@ -362,7 +397,7 @@ for c in "नमस्ते".chars() {
|
||||
}
|
||||
```
|
||||
|
||||
This code will print:
|
||||
This code will print the following:
|
||||
|
||||
```text
|
||||
न
|
||||
@ -392,22 +427,22 @@ This code will print the 18 bytes that make up this `String`, starting with:
|
||||
// ... etc
|
||||
```
|
||||
|
||||
But make sure to remember that valid Unicode scalar values may be made up of
|
||||
more than one byte.
|
||||
But be sure to remember that valid Unicode scalar values may be made up of more
|
||||
than one byte.
|
||||
|
||||
Getting grapheme clusters from strings is complex, so this functionality is not
|
||||
provided by the standard library. There are crates available on crates.io if
|
||||
this is the functionality you need.
|
||||
provided by the standard library. Crates are available on
|
||||
[crates.io](https://crates.io) if this is the functionality you need.
|
||||
|
||||
### Strings are Not so Simple
|
||||
### Strings Are Not So Simple
|
||||
|
||||
To summarize, strings are complicated. Different programming languages make
|
||||
different choices about how to present this complexity to the programmer. Rust
|
||||
has chosen to make the correct handling of `String` data the default behavior
|
||||
for all Rust programs, which does mean programmers have to put more thought
|
||||
into handling UTF-8 data upfront. This tradeoff exposes more of the complexity
|
||||
of strings than other programming languages do, but this will prevent you from
|
||||
having to handle errors involving non-ASCII characters later in your
|
||||
development lifecycle.
|
||||
for all Rust programs, which means programmers have to put more thought into
|
||||
handling UTF-8 data upfront. This trade-off exposes more of the complexity of
|
||||
strings than other programming languages do but prevents you from having to
|
||||
handle errors involving non-ASCII characters later in your development life
|
||||
cycle.
|
||||
|
||||
Let’s switch to something a bit less complex: hash maps!
|
||||
|
@ -4,26 +4,25 @@ The last of our common collections is the *hash map*. The type `HashMap<K, V>`
|
||||
stores a mapping of keys of type `K` to values of type `V`. It does this via a
|
||||
*hashing function*, which determines how it places these keys and values into
|
||||
memory. Many different programming languages support this kind of data
|
||||
structure, but often with a different name: hash, map, object, hash table, or
|
||||
associative array, just to name a few.
|
||||
structure, but often use a different name, such as hash, map, object, hash
|
||||
table, or associative array, just to name a few.
|
||||
|
||||
Hash maps are useful for when you want to be able to look up data not by an
|
||||
index, as you can with vectors, but by using a key that can be of any type. For
|
||||
example, in a game, you could keep track of each team’s score in a hash map
|
||||
where each key is a team’s name and the values are each team’s score. Given a
|
||||
team name, you can retrieve their score.
|
||||
Hash maps are useful for when you want to look up data not by an index, as you
|
||||
can with vectors, but by using a key that can be of any type. For example, in a
|
||||
game, you could keep track of each team’s score in a hash map where each key is
|
||||
a team’s name and the values are each team’s score. Given a team name, you can
|
||||
retrieve its score.
|
||||
|
||||
We’ll go over the basic API of hash maps in this chapter, but there are many
|
||||
more goodies hiding in the functions defined on `HashMap` by the standard
|
||||
library. As always, check the standard library documentation for more
|
||||
information.
|
||||
We’ll go over the basic API of hash maps in this section, but many more goodies
|
||||
are hiding in the functions defined on `HashMap<K, V>` by the standard library.
|
||||
As always, check the standard library documentation for more information.
|
||||
|
||||
### Creating a New Hash Map
|
||||
|
||||
We can create an empty `HashMap` with `new`, and add elements with `insert`.
|
||||
Here we’re keeping track of the scores of two teams whose names are Blue and
|
||||
Yellow. The Blue team will start with 10 points and the Yellow team starts with
|
||||
50:
|
||||
We can create an empty hash map with `new` and add elements with `insert`. In
|
||||
Listing 8-18, we’re keeping track of the scores of two teams whose names are
|
||||
Blue and Yellow. The Blue team will start with 10 points, and the Yellow team
|
||||
starts with 50:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -34,6 +33,9 @@ scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Yellow"), 50);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-18: Creating a new hash map and inserting some
|
||||
keys and values</span>
|
||||
|
||||
Note that we need to first `use` the `HashMap` from the collections portion of
|
||||
the standard library. Of our three common collections, this one is the least
|
||||
often used, so it’s not included in the features imported automatically in the
|
||||
@ -47,11 +49,11 @@ must have the same type.
|
||||
|
||||
Another way of constructing a hash map is by using the `collect` method on a
|
||||
vector of tuples, where each tuple consists of a key and its value. The
|
||||
`collect` method gathers up data into a number of collection types, including
|
||||
`collect` method gathers data into a number of collection types, including
|
||||
`HashMap`. For example, if we had the team names and initial scores in two
|
||||
separate vectors, we can use the `zip` method to create a vector of tuples
|
||||
where “Blue” is paired with 10, and so forth. Then we can use the `collect`
|
||||
method to turn that vector of tuples into a `HashMap`:
|
||||
method to turn that vector of tuples into a `HashMap` as shown in Listing 8-19:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -62,17 +64,20 @@ let initial_scores = vec![10, 50];
|
||||
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-19: Creating a hash map from a list of teams
|
||||
and a list of scores</span>
|
||||
|
||||
The type annotation `HashMap<_, _>` is needed here because it’s possible to
|
||||
`collect` into many different data structures, and Rust doesn’t know which you
|
||||
want unless you specify. For the type parameters for the key and value types,
|
||||
however, we use underscores and Rust can infer the types that the hash map
|
||||
contains based on the types of the data in the vector.
|
||||
however, we use underscores, and Rust can infer the types that the hash map
|
||||
contains based on the types of the data in the vectors.
|
||||
|
||||
### Hash Maps and Ownership
|
||||
|
||||
For types that implement the `Copy` trait, like `i32`, the values are copied
|
||||
into the hash map. For owned values like `String`, the values will be moved and
|
||||
the hash map will be the owner of those values:
|
||||
the hash map will be the owner of those values as demonstrated in Listing 8-20:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -82,20 +87,25 @@ let field_value = String::from("Blue");
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(field_name, field_value);
|
||||
// field_name and field_value are invalid at this point
|
||||
// field_name and field_value are invalid at this point, try using them and
|
||||
// see what compiler error you get!
|
||||
```
|
||||
|
||||
We would not be able to use the bindings `field_name` and `field_value` after
|
||||
they have been moved into the hash map with the call to `insert`.
|
||||
<span class="caption">Listing 8-20: Showing that keys and values are owned by
|
||||
the hash map once they’re inserted</span>
|
||||
|
||||
If we insert references to values into the hash map, the values themselves will
|
||||
not be moved into the hash map. The values that the references point to must be
|
||||
valid for at least as long as the hash map is valid, though. We will talk more
|
||||
about these issues in the Lifetimes section of Chapter 10.
|
||||
We aren’t able to use the variables `field_name` and `field_value` after
|
||||
they’ve been moved into the hash map with the call to `insert`.
|
||||
|
||||
If we insert references to values into the hash map, the values won’t be moved
|
||||
into the hash map. The values that the references point to must be valid for at
|
||||
least as long as the hash map is valid. We’ll talk more about these issues in
|
||||
the “Validating References with Lifetimes” section in Chapter 10.
|
||||
|
||||
### Accessing Values in a Hash Map
|
||||
|
||||
We can get a value out of the hash map by providing its key to the `get` method:
|
||||
We can get a value out of the hash map by providing its key to the `get` method
|
||||
as shown in Listing 8-21:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -109,11 +119,14 @@ let team_name = String::from("Blue");
|
||||
let score = scores.get(&team_name);
|
||||
```
|
||||
|
||||
<span class="caption">Listing 8-21: Accessing the score for the Blue team
|
||||
stored in the hash map</span>
|
||||
|
||||
Here, `score` will have the value that’s associated with the Blue team, and the
|
||||
result will be `Some(&10)`. The result is wrapped in `Some` because `get`
|
||||
returns an `Option<&V>`; if there’s no value for that key in the hash map, `get`
|
||||
will return `None`. The program will need to handle the `Option` in one of the
|
||||
ways that we covered in Chapter 6.
|
||||
returns an `Option<&V>`; if there’s no value for that key in the hash map,
|
||||
`get` will return `None`. The program will need to handle the `Option` in one
|
||||
of the ways that we covered in Chapter 6.
|
||||
|
||||
We can iterate over each key/value pair in a hash map in a similar manner as we
|
||||
do with vectors, using a `for` loop:
|
||||
@ -131,7 +144,7 @@ for (key, value) in &scores {
|
||||
}
|
||||
```
|
||||
|
||||
This will print each pair, in an arbitrary order:
|
||||
This code will print each pair in an arbitrary order:
|
||||
|
||||
```text
|
||||
Yellow: 50
|
||||
@ -140,22 +153,22 @@ Blue: 10
|
||||
|
||||
### Updating a Hash Map
|
||||
|
||||
While the number of keys and values is growable, each individual key can only
|
||||
have one value associated with it at a time. When we want to change the data in
|
||||
a hash map, we have to decide how to handle the case when a key already has a
|
||||
value assigned. We could choose to replace the old value with the new value,
|
||||
completely disregarding the old value. We could choose to keep the old value
|
||||
and ignore the new value, and only add the new value if the key *doesn’t*
|
||||
already have a value. Or we could combine the old value and the new value.
|
||||
Let’s look at how to do each of these!
|
||||
Although the number of keys and values is growable, each key can only have one
|
||||
value associated with it at a time. When we want to change the data in a hash
|
||||
map, we have to decide how to handle the case when a key already has a value
|
||||
assigned. We could replace the old value with the new value, completely
|
||||
disregarding the old value. We could keep the old value and ignore the new
|
||||
value, and only add the new value if the key *doesn’t* already have a value. Or
|
||||
we could combine the old value and the new value. Let’s look at how to do each
|
||||
of these!
|
||||
|
||||
#### Overwriting a Value
|
||||
|
||||
If we insert a key and a value into a hash map, then insert that same key with
|
||||
a different value, the value associated with that key will be replaced. Even
|
||||
though this following code calls `insert` twice, the hash map will only contain
|
||||
one key/value pair because we’re inserting the value for the Blue team’s key
|
||||
both times:
|
||||
If we insert a key and a value into a hash map, and then insert that same key
|
||||
with a different value, the value associated with that key will be replaced.
|
||||
Even though the code in Listing 8-22 calls `insert` twice, the hash map will
|
||||
only contain one key/value pair because we’re inserting the value for the Blue
|
||||
team’s key both times:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -168,18 +181,22 @@ scores.insert(String::from("Blue"), 25);
|
||||
println!("{:?}", scores);
|
||||
```
|
||||
|
||||
This will print `{"Blue": 25}`. The original value of 10 has been overwritten.
|
||||
<span class="caption">Listing 8-22: Replacing a value stored with a particular
|
||||
key</span>
|
||||
|
||||
This code will print `{"Blue": 25}`. The original value of `10` has been
|
||||
overwritten.
|
||||
|
||||
#### Only Insert If the Key Has No Value
|
||||
|
||||
It’s common to want to check if a particular key has a value and, if it does
|
||||
not, insert a value for it. Hash maps have a special API for this, called
|
||||
`entry`, that takes the key we want to check as an argument. The return value
|
||||
of the `entry` function is an enum, `Entry`, that represents a value that might
|
||||
or might not exist. Let’s say that we want to check if the key for the Yellow
|
||||
It’s common to check whether a particular key has a value, and if it doesn’t,
|
||||
insert a value for it. Hash maps have a special API for this called `entry`
|
||||
that takes the key we want to check as a parameter. The return value of the
|
||||
`entry` function is an enum called `Entry` that represents a value that might
|
||||
or might not exist. Let’s say we want to check whether the key for the Yellow
|
||||
team has a value associated with it. If it doesn’t, we want to insert the value
|
||||
50, and the same for the Blue team. With the entry API, the code for this looks
|
||||
like:
|
||||
50, and the same for the Blue team. Using the `entry` API, the code looks like
|
||||
Listing 8-23:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -193,23 +210,29 @@ scores.entry(String::from("Blue")).or_insert(50);
|
||||
println!("{:?}", scores);
|
||||
```
|
||||
|
||||
The `or_insert` method on `Entry` returns the value for the corresponding
|
||||
`Entry` key if it exists, and if not, inserts its argument as the new value for
|
||||
this key and returns the modified `Entry`. This is much cleaner than writing
|
||||
the logic ourselves, and in addition, plays more nicely with the borrow checker.
|
||||
<span class="caption">Listing 8-23: Using the `entry` method to only insert if
|
||||
the key does not already have a value</span>
|
||||
|
||||
This code will print `{"Yellow": 50, "Blue": 10}`. The first call to `entry`
|
||||
will insert the key for the Yellow team with the value 50, since the Yellow
|
||||
team doesn’t have a value already. The second call to `entry` will not change
|
||||
the hash map since the Blue team already has the value 10.
|
||||
The `or_insert` method on `Entry` is defined to return the value for the
|
||||
corresponding `Entry` key if that key exists, and if not, inserts the parameter
|
||||
as the new value for this key and returns the modified `Entry`. This technique
|
||||
is much cleaner than writing the logic ourselves, and in addition, plays more
|
||||
nicely with the borrow checker.
|
||||
|
||||
#### Update a Value Based on the Old Value
|
||||
Running the code in Listing 8-23 will print `{"Yellow": 50, "Blue": 10}`. The
|
||||
first call to `entry` will insert the key for the Yellow team with the value
|
||||
`50` because the Yellow team doesn’t have a value already. The second call to
|
||||
`entry` will not change the hash map because the Blue team already has the
|
||||
value `10`.
|
||||
|
||||
Another common use case for hash maps is to look up a key’s value then update
|
||||
it, based on the old value. For instance, if we wanted to count how many times
|
||||
each word appeared in some text, we could use a hash map with the words as keys
|
||||
and increment the value to keep track of how many times we’ve seen that word.
|
||||
If this is the first time we’ve seen a word, we’ll first insert the value `0`.
|
||||
#### Updating a Value Based on the Old Value
|
||||
|
||||
Another common use case for hash maps is to look up a key’s value and then
|
||||
update it based on the old value. For instance, Listing 8-24 shows code that
|
||||
counts how many times each word appears in some text. We use a hash map with
|
||||
the words as keys and increment the value to keep track of how many times we’ve
|
||||
seen that word. If it’s the first time we’ve seen a word, we’ll first insert
|
||||
the value `0`:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
@ -226,47 +249,51 @@ for word in text.split_whitespace() {
|
||||
println!("{:?}", map);
|
||||
```
|
||||
|
||||
This will print `{"world": 2, "hello": 1, "wonderful": 1}`. The `or_insert`
|
||||
method actually returns a mutable reference (`&mut V`) to the value for this
|
||||
key. Here we store that mutable reference in the `count` variable, so in order
|
||||
to assign to that value we must first dereference `count` using the asterisk
|
||||
(`*`). The mutable reference goes out of scope at the end of the `for` loop, so
|
||||
all of these changes are safe and allowed by the borrowing rules.
|
||||
<span class="caption">Listing 8-24: Counting occurrences of words using a hash
|
||||
map that stores words and counts</span>
|
||||
|
||||
This code will print `{"world": 2, "hello": 1, "wonderful": 1}`. The
|
||||
`or_insert` method actually returns a mutable reference (`&mut V`) to the value
|
||||
for this key. Here we store that mutable reference in the `count` variable, so
|
||||
in order to assign to that value we must first dereference `count` using the
|
||||
asterisk (`*`). The mutable reference goes out of scope at the end of the `for`
|
||||
loop, so all of these changes are safe and allowed by the borrowing rules.
|
||||
|
||||
### Hashing Function
|
||||
|
||||
By default, `HashMap` uses a cryptographically secure hashing function that can
|
||||
provide resistance to Denial of Service (DoS) attacks. This is not the fastest
|
||||
hashing algorithm out there, but the tradeoff for better security that comes
|
||||
hashing algorithm available, but the trade-off for better security that comes
|
||||
with the drop in performance is worth it. If you profile your code and find
|
||||
that the default hash function is too slow for your purposes, you can switch to
|
||||
another function by specifying a different *hasher*. A hasher is a type that
|
||||
implements the `BuildHasher` trait. We’ll be talking about traits and how to
|
||||
implements the `BuildHasher` trait. We’ll talk about traits and how to
|
||||
implement them in Chapter 10. You don’t necessarily have to implement your own
|
||||
hasher from scratch; crates.io has libraries that others have shared that
|
||||
provide hashers implementing many common hashing algorithms.
|
||||
hasher from scratch; [crates.io](https://crates.io) has libraries shared by
|
||||
other Rust users that provide hashers implementing many common hashing
|
||||
algorithms.
|
||||
|
||||
## Summary
|
||||
|
||||
Vectors, strings, and hash maps will take you far in programs where you need to
|
||||
store, access, and modify data. Here are some exercises you should now be
|
||||
equipped to solve:
|
||||
Vectors, strings, and hash maps will provide a large amount of functionality
|
||||
that you need in programs where you need to store, access, and modify data.
|
||||
Here are some exercises you should now be equipped to solve:
|
||||
|
||||
* Given a list of integers, use a vector and return the mean (average), median
|
||||
(when sorted, the value in the middle position), and mode (the value that
|
||||
occurs most often; a hash map will be helpful here) of the list.
|
||||
* Convert strings to Pig Latin, where the first consonant of each word is moved
|
||||
to the end of the word with an added “ay”, so “first” becomes “irst-fay”.
|
||||
Words that start with a vowel get “hay” added to the end instead (“apple”
|
||||
becomes “apple-hay”). Remember about UTF-8 encoding!
|
||||
* Convert strings to pig latin. The first consonant of each word is moved to
|
||||
the end of the word and “ay” is added, so “first” becomes “irst-fay.” Words
|
||||
that start with a vowel have “hay” added to the end instead (“apple” becomes
|
||||
“apple-hay”). Keep in mind the details about UTF-8 encoding!
|
||||
* Using a hash map and vectors, create a text interface to allow a user to add
|
||||
employee names to a department in the company. For example, “Add Sally to
|
||||
Engineering” or “Add Amir to Sales”. Then let the user retrieve a list of all
|
||||
employee names to a department in a company. For example, “Add Sally to
|
||||
Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all
|
||||
people in a department or all people in the company by department, sorted
|
||||
alphabetically.
|
||||
|
||||
The standard library API documentation describes methods these types have that
|
||||
will be helpful for these exercises!
|
||||
The standard library API documentation describes methods that vectors, strings,
|
||||
and hash maps have that will be helpful for these exercises!
|
||||
|
||||
We’re getting into more complex programs where operations can fail, which means
|
||||
it’s a perfect time to go over error handling next!
|
||||
We’re getting into more complex programs in which operations can fail; so, it’s
|
||||
a perfect time to discuss error handling next!
|
||||
|
@ -2,22 +2,23 @@
|
||||
|
||||
Rust’s commitment to reliability extends to error handling. Errors are a fact
|
||||
of life in software, so Rust has a number of features for handling situations
|
||||
in which something goes wrong. In many cases, Rust will require you to
|
||||
acknowledge the possibility of an error occurring and take some action before
|
||||
your code will compile. This makes your program more robust by ensuring that
|
||||
you won’t only discover errors after you’ve deployed your code to production.
|
||||
in which something goes wrong. In many cases, Rust requires you to acknowledge
|
||||
the possibility of an error occurring and take some action before your code
|
||||
will compile. This requirement makes your program more robust by ensuring that
|
||||
you’ll discover errors and handle them appropriately before you’ve deployed
|
||||
your code to production!
|
||||
|
||||
Rust groups errors into two major categories: *recoverable* and *unrecoverable*
|
||||
errors. Recoverable errors are situations when it’s usually reasonable to
|
||||
report the problem to the user and retry the operation, like a file not being
|
||||
found. Unrecoverable errors are always symptoms of bugs, like trying to access
|
||||
a location beyond the end of an array.
|
||||
errors. Recoverable errors are situations in which it’s reasonable to report
|
||||
the problem to the user and retry the operation, like a file not found error.
|
||||
Unrecoverable errors are always symptoms of bugs, like trying to access a
|
||||
location beyond the end of an array.
|
||||
|
||||
Most languages don’t distinguish between the 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
|
||||
exceptions. Instead, it has the value `Result<T, E>` for recoverable errors and
|
||||
the `panic!` macro that stops execution when it encounters unrecoverable
|
||||
errors. This chapter will cover calling `panic!` first, then talk about
|
||||
returning `Result<T, E>` values. Finally, we’ll discuss considerations to take
|
||||
into account when deciding whether to try to recover from an error or to stop
|
||||
execution.
|
||||
errors. This chapter covers calling `panic!` first and then talks about
|
||||
returning `Result<T, E>` values. Additionally, we’ll explore considerations to
|
||||
take into account when deciding whether to try to recover from an error or to
|
||||
stop execution.
|
||||
|
@ -1,30 +1,31 @@
|
||||
## Unrecoverable Errors with `panic!`
|
||||
|
||||
Sometimes, bad things happen, and there’s nothing that you can do about it. For
|
||||
these cases, Rust has the `panic!` macro. When this macro executes, your
|
||||
program will print a failure message, unwind and clean up the stack, and then
|
||||
quit. The most common situation this occurs in is when a bug of some kind has
|
||||
been detected and it’s not clear to the programmer how to handle the error.
|
||||
Sometimes, bad things happen in your code, and there’s nothing you can do about
|
||||
it. In these cases, Rust has the `panic!` macro. When the `panic!` macro
|
||||
executes, your program will print a failure message, unwind and clean up the
|
||||
stack, and then quit. The most common situation this occurs in is when a bug of
|
||||
some kind has been detected, and it’s not clear to the programmer how to handle
|
||||
the error.
|
||||
|
||||
> ### Unwinding the Stack Versus Aborting on Panic
|
||||
> ### Unwinding the Stack or Aborting in Response to a `panic!`
|
||||
>
|
||||
> By default, when a `panic!` occurs, the program starts
|
||||
> *unwinding*, which means Rust walks back up the stack and cleans up the data
|
||||
> from each function it encounters, but this walking and cleanup is a lot of
|
||||
> work. The alternative is to immediately *abort*, which ends the program
|
||||
> without cleaning up. Memory that the program was using will then need to be
|
||||
> cleaned up by the operating system. If in your project you need to make the
|
||||
> resulting binary as small as possible, you can switch from unwinding to
|
||||
> aborting on panic by adding `panic = 'abort'` to the appropriate `[profile]`
|
||||
> sections in your *Cargo.toml*. For example, if you want to abort on panic in
|
||||
> release mode:
|
||||
> By default, when a `panic!` occurs, the program starts *unwinding*, which
|
||||
> means Rust walks back up the stack and cleans up the data from each function
|
||||
> it encounters. But this walking back and cleanup is a lot of work. The
|
||||
> alternative is to immediately *abort*, which ends the program without
|
||||
> cleaning up. Memory that the program was using will then need to be cleaned
|
||||
> up by the operating system. If in your project you need to make the resulting
|
||||
> binary as small as possible, you can switch from unwinding to aborting on
|
||||
> panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in
|
||||
> your *Cargo.toml* file. For example, if you want to abort on panic in release
|
||||
> mode, add this:
|
||||
>
|
||||
> ```toml
|
||||
> [profile.release]
|
||||
> panic = 'abort'
|
||||
> ```
|
||||
|
||||
Let’s try calling `panic!` with a simple program:
|
||||
Let’s try calling `panic!` in a simple program:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -34,7 +35,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
If you run it, you’ll see something like this:
|
||||
When you run the program, you’ll see something like this:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
@ -46,23 +47,26 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
||||
```
|
||||
|
||||
The last three lines contain the error message caused by the call to `panic!`.
|
||||
The first line shows our panic message and the place in our source code where
|
||||
the panic occurred: *src/main.rs:2* indicates that it’s the second line of our
|
||||
*src/main.rs* file.
|
||||
The call to `panic!` causes the error message contained in the last three
|
||||
lines. The first line shows our panic message and the place in our source code
|
||||
where the panic occurred: *src/main.rs:2* indicates that it’s the second line
|
||||
of our *src/main.rs* file.
|
||||
|
||||
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 be in
|
||||
code that our code calls. The filename and line number reported by the error
|
||||
message will be someone else’s code where the `panic!` macro is called, not the
|
||||
line of our code that eventually led to the `panic!`. We can use the backtrace
|
||||
of the functions the `panic!` call came from to figure this out.
|
||||
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
|
||||
be in code that our code calls. The filename and line number reported by the
|
||||
error message will be someone else’s code where the `panic!` macro is called,
|
||||
not the line of our code that eventually led to the `panic!` call. We can use
|
||||
the backtrace of the functions the `panic!` call came from to figure out the
|
||||
part of our code that is causing the problem. We’ll discuss what a backtrace is
|
||||
in more detail next.
|
||||
|
||||
### Using a `panic!` Backtrace
|
||||
|
||||
Let’s look at another example to see what it’s like when a `panic!` call comes
|
||||
from a library because of a bug in our code instead of from our code calling
|
||||
the macro directly:
|
||||
the macro directly. Listing 9-1 has some code that attempts to access an
|
||||
element by index in a vector:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -74,22 +78,25 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
We’re attempting to access the hundredth element of our vector, but it only has
|
||||
three elements. In this situation, Rust will panic. Using `[]` is supposed to
|
||||
return an element, but if you pass an invalid index, there’s no element that
|
||||
Rust could return here that would be correct.
|
||||
<span class="caption">Listing 9-1: Attempting to access an element beyond the
|
||||
end of a vector, which will cause a `panic!`</span>
|
||||
|
||||
Other languages like C will attempt to give you exactly what you asked for in
|
||||
Here, we’re attempting to access the hundredth element of our vector, but it
|
||||
has only three elements. In this situation, Rust will panic. Using `[]` is
|
||||
supposed to return an element, but if you pass an invalid index, there’s no
|
||||
element that Rust could return here that would be correct.
|
||||
|
||||
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
|
||||
the location in memory that would correspond to that element in the vector,
|
||||
even though the memory doesn’t belong to the vector. This is called a *buffer
|
||||
overread*, and can lead to security vulnerabilities if an attacker can
|
||||
overread* and can lead to security vulnerabilities if an attacker is able to
|
||||
manipulate the index in such a way as to read data they shouldn’t be allowed to
|
||||
that is stored after the array.
|
||||
|
||||
In order to protect your program from this sort of vulnerability, if you try to
|
||||
read an element at an index that doesn’t exist, Rust will stop execution and
|
||||
refuse to continue. Let’s try it and see:
|
||||
To protect your program from this sort of vulnerability, if you try to read an
|
||||
element at an index that doesn’t exist, Rust will stop execution and refuse to
|
||||
continue. Let’s try it and see:
|
||||
|
||||
```text
|
||||
$ cargo run
|
||||
@ -102,14 +109,21 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
||||
```
|
||||
|
||||
This points at a file we didn’t write, *libcollections/vec.rs*. That’s the
|
||||
implementation of `Vec<T>` in the standard library. The code that gets run when
|
||||
we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is where
|
||||
the `panic!` is actually happening.
|
||||
This error points at a file we didn’t write, *libcollections/vec.rs*. That’s
|
||||
the implementation of `Vec<T>` in the standard library. The code that gets run
|
||||
when we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is
|
||||
where the `panic!` is actually happening.
|
||||
|
||||
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. Let’s
|
||||
try that. Listing 9-1 shows the output:
|
||||
variable to get a backtrace of exactly what happened to cause the error. A
|
||||
*backtrace* is a list of all the functions that have been called to get to this
|
||||
point. Backtraces in Rust work like they do in other languages: the key to
|
||||
reading the backtrace is to start from the top and read until you see files you
|
||||
wrote. That’s the spot where the problem originated. The lines above the lines
|
||||
mentioning your files are code that your code called; the lines below are code
|
||||
that called your code. These lines might include core Rust code, standard
|
||||
library code, or crates that you’re using. Let’s try getting a backtrace:
|
||||
Listing 9-2 shows output similar to what you’ll see:
|
||||
|
||||
```text
|
||||
$ RUST_BACKTRACE=1 cargo run
|
||||
@ -151,29 +165,26 @@ stack backtrace:
|
||||
17: 0x0 - <unknown>
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-1: The backtrace generated by a call to
|
||||
<span class="caption">Listing 9-2: The backtrace generated by a call to
|
||||
`panic!` displayed when the environment variable `RUST_BACKTRACE` is set</span>
|
||||
|
||||
That’s a lot of output! Line 11 of the backtrace points to the line in our
|
||||
project causing the problem: *src/main.rs*, line four. A backtrace is a list of
|
||||
all the functions that have been called to get to this point. Backtraces in
|
||||
Rust work like they do in other languages: the key to reading the backtrace is
|
||||
to start from the top and read until you see files you wrote. That’s the spot
|
||||
where the problem originated. The lines above the lines mentioning your files
|
||||
are code that your code called; the lines below are code that called your code.
|
||||
These lines might include core Rust code, standard library code, or crates that
|
||||
you’re using.
|
||||
That’s a lot of output! The exact output you see might be different depending
|
||||
on your operating system and Rust version. In order to get backtraces with this
|
||||
information, debug symbols must be enabled. Debug symbols are enabled by
|
||||
default when using cargo build or cargo run without the --release flag, as we
|
||||
have here.
|
||||
|
||||
If we don’t want our program to panic, the location pointed to by the first
|
||||
line mentioning a file we wrote is where we should start investigating in order
|
||||
to figure out how we got to this location with values that caused the panic. In
|
||||
our example where we deliberately wrote code that would panic in order to
|
||||
demonstrate how to use backtraces, the way to fix the panic is to not try to
|
||||
request an element at index 100 from a vector that only contains three items.
|
||||
When your code panics in the future, you’ll need to figure out for your
|
||||
particular case what action the code is taking with what values that causes the
|
||||
panic and what the code should do instead.
|
||||
In the output in Listing 9-2, line 11 of the backtrace points to the line in
|
||||
our project that’s causing the problem: *src/main.rs* in line 4. If we don’t
|
||||
want our program to panic, the location pointed to by the first line mentioning
|
||||
a file we wrote is where we should start investigating to figure out how we got
|
||||
to this location with values that caused the panic. In Listing 9-1 where we
|
||||
deliberately wrote code that would panic in order to demonstrate how to use
|
||||
backtraces, the way to fix the panic is to not request an element at index 100
|
||||
from a vector that only contains three items. When your code panics in the
|
||||
future, you’ll need to figure out what action the code is taking with what
|
||||
values that causes the panic and what the code should do instead.
|
||||
|
||||
We’ll come back to `panic!` and when we should and should not use these methods
|
||||
later in the chapter. Next, we’ll now look at how to recover from an error with
|
||||
`Result`.
|
||||
We’ll come back to `panic!` and when we should and should not use `panic!` to
|
||||
handle error conditions later in the chapter. Next, we’ll look at how to
|
||||
recover from an error using `Result`.
|
||||
|
@ -6,8 +6,8 @@ interpret and respond to. For example, if we try to open a file and that
|
||||
operation fails because the file doesn’t exist, we might want to create the
|
||||
file instead of terminating the process.
|
||||
|
||||
Recall from Chapter 2 the section on “[Handling Potential Failure with the
|
||||
`Result` Type][handle_failure]<!-- ignore -->” that the `Result` enum is defined
|
||||
Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result`
|
||||
Type][handle_failure]<!-- ignore -->” section that the `Result` enum is defined
|
||||
as having two variants, `Ok` and `Err`, as follows:
|
||||
|
||||
[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type
|
||||
@ -19,7 +19,7 @@ enum Result<T, E> {
|
||||
}
|
||||
```
|
||||
|
||||
The `T` and `E` are generic type parameters; we’ll go into generics in more
|
||||
The `T` and `E` are generic type parameters: we’ll discuss generics in more
|
||||
detail in Chapter 10. What you need to know right now is that `T` represents
|
||||
the type of the value that will be returned in a success case within the `Ok`
|
||||
variant, and `E` represents the type of the error that will be returned in a
|
||||
@ -29,7 +29,7 @@ library has defined on it in many different situations where the successful
|
||||
value and error value we want to return may differ.
|
||||
|
||||
Let’s call a function that returns a `Result` value because the function could
|
||||
fail: opening a file, shown in Listing 9-2.
|
||||
fail: in Listing 9-3 we try to open a file:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -41,21 +41,21 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-2: Opening a file</span>
|
||||
<span class="caption">Listing 9-3: Opening a file</span>
|
||||
|
||||
How do we know `File::open` returns a `Result`? We could look at the standard
|
||||
library API documentation, or we could ask the compiler! If we give `f` a type
|
||||
annotation of some type that we know the return type of the function is *not*,
|
||||
annotation of a type that we know the return type of the function is *not* and
|
||||
then we try to compile the code, the compiler will tell us that the types don’t
|
||||
match. The error message will then tell us what the type of `f` *is*! Let’s try
|
||||
match. The error message will then tell us what the type of `f` *is*. Let’s try
|
||||
it: we know that the return type of `File::open` isn’t of type `u32`, so let’s
|
||||
change the `let f` statement to:
|
||||
change the `let f` statement to this:
|
||||
|
||||
```rust,ignore
|
||||
let f: u32 = File::open("hello.txt");
|
||||
```
|
||||
|
||||
Attempting to compile now gives us:
|
||||
Attempting to compile now gives us the following output:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
@ -76,9 +76,9 @@ error value is `std::io::Error`.
|
||||
|
||||
This return type means the call to `File::open` might succeed and return to us
|
||||
a file handle that we can read from or write to. The function call also might
|
||||
fail: for example, the file might not exist, or we might not have permission to
|
||||
fail: for example, the file might not exist or we might not have permission to
|
||||
access the file. The `File::open` function needs to have a way to tell us
|
||||
whether it succeeded or failed, and at the same time give us either the file
|
||||
whether it succeeded or failed and at the same time give us either the file
|
||||
handle or error information. This information is exactly what the `Result` enum
|
||||
conveys.
|
||||
|
||||
@ -87,9 +87,9 @@ In the case where `File::open` succeeds, the value we will have in the variable
|
||||
it fails, the value in `f` will be an instance of `Err` that contains more
|
||||
information about the kind of error that happened.
|
||||
|
||||
We need to add to the code from Listing 9-2 to take different actions depending
|
||||
on the value `File::open` returned. Listing 9-3 shows one way to handle the
|
||||
`Result` with a basic tool: the `match` expression that we learned about in
|
||||
We need to add to the code in Listing 9-3 to take different actions depending
|
||||
on the value `File::open` returned. Listing 9-4 shows one way to handle the
|
||||
`Result` using a basic tool: the `match` expression that we discussed in
|
||||
Chapter 6.
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
@ -109,7 +109,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-3: Using a `match` expression to handle the
|
||||
<span class="caption">Listing 9-4: Using a `match` expression to handle the
|
||||
`Result` variants we might have</span>
|
||||
|
||||
Note that, like the `Option` enum, the `Result` enum and its variants have been
|
||||
@ -131,19 +131,23 @@ thread 'main' panicked at 'There was a problem opening the file: Error { repr:
|
||||
Os { code: 2, message: "No such file or directory" } }', src/main.rs:8
|
||||
```
|
||||
|
||||
As usual, this output tells us exactly what has gone wrong.
|
||||
|
||||
### Matching on Different Errors
|
||||
|
||||
The code in Listing 9-3 will `panic!` no matter the reason that `File::open`
|
||||
failed. What we’d really like to do instead is take different actions for
|
||||
different failure reasons: if `File::open` failed because the file doesn’t
|
||||
exist, we want to create the file and return the handle to the new file. If
|
||||
`File::open` failed for any other reason, for example because we didn’t have
|
||||
permission to open the file, we still want to `panic!` in the same way as we
|
||||
did in Listing 9-3. Let’s look at Listing 9-4, which adds another arm to the
|
||||
`match`:
|
||||
The code in Listing 9-4 will `panic!` no matter the reason that `File::open`
|
||||
failed. What we want to do instead is take different actions for different
|
||||
failure reasons: if `File::open` failed because the file doesn’t exist, we want
|
||||
to create the file and return the handle to the new file. If `File::open`
|
||||
failed for any other reason, for example because we didn’t have permission to
|
||||
open the file, we still want the code to `panic!` in the same way as it did in
|
||||
Listing 9-4. Look at Listing 9-5, which adds another arm to the `match`:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
<!-- ignore this test because otherwise it creates hello.txt which causes other
|
||||
tests to fail lol -->
|
||||
|
||||
```rust,ignore
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind;
|
||||
@ -174,7 +178,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-4: Handling different kinds of errors in
|
||||
<span class="caption">Listing 9-5: Handling different kinds of errors in
|
||||
different ways</span>
|
||||
|
||||
The type of the value that `File::open` returns inside the `Err` variant is
|
||||
@ -182,36 +186,38 @@ The type of the value that `File::open` returns inside the `Err` variant is
|
||||
has a method `kind` that we can call to get an `io::ErrorKind` value.
|
||||
`io::ErrorKind` is an enum provided by the standard library that has variants
|
||||
representing the different kinds of errors that might result from an `io`
|
||||
operation. The variant we’re interested in is `ErrorKind::NotFound`, which
|
||||
indicates the file we’re trying to open doesn’t exist yet.
|
||||
operation. The variant we want to use is `ErrorKind::NotFound`, which indicates
|
||||
the file we’re trying to open doesn’t exist yet.
|
||||
|
||||
The condition `if error.kind() == ErrorKind::NotFound` is called a *match
|
||||
guard*: it’s an extra condition on a `match` arm that further refines the arm’s
|
||||
pattern. This condition must be true in order for that arm’s code to get run;
|
||||
otherwise, the pattern matching will move on to consider the next arm in the
|
||||
`match`. The `ref` in the pattern is needed so that `error` is not moved into
|
||||
the guard condition but is merely referenced by it. The reason `ref` is used to
|
||||
take a reference in a pattern instead of `&` will be covered in detail in
|
||||
Chapter 18. In short, in the context of a pattern, `&` matches a reference and
|
||||
gives us its value, but `ref` matches a value and gives us a reference to it.
|
||||
pattern. This condition must be true for that arm’s code to be run; otherwise,
|
||||
the pattern matching will move on to consider the next arm in the `match`. The
|
||||
`ref` in the pattern is needed so `error` is not moved into the guard condition
|
||||
but is merely referenced by it. The reason `ref` is used to take a reference in
|
||||
a pattern instead of `&` will be covered in detail in Chapter 18. In short, in
|
||||
the context of a pattern, `&` matches a reference and gives us its value, but
|
||||
`ref` matches a value and gives us a reference to it.
|
||||
|
||||
The condition we want to check in the match guard is whether the value returned
|
||||
by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is,
|
||||
we try to create the file with `File::create`. However, since `File::create`
|
||||
could also fail, we need to add an inner `match` statement as well! When the
|
||||
we try to create the file with `File::create`. However, because `File::create`
|
||||
could also fail, we need to add an inner `match` statement as well. When the
|
||||
file can’t be opened, a different error message will be printed. The last arm
|
||||
of the outer `match` stays the same so that the program panics on any error
|
||||
besides the missing file error.
|
||||
of the outer `match` stays the same so the program panics on any error besides
|
||||
the missing file error.
|
||||
|
||||
### Shortcuts for Panic on Error: `unwrap` and `expect`
|
||||
|
||||
Using `match` works well enough, but it can be a bit verbose and doesn’t always
|
||||
communicate intent well. The `Result<T, E>` type has many helper methods
|
||||
defined on it to do various things. One of those methods, called `unwrap`, is a
|
||||
defined on it to do various tasks. One of those methods, called `unwrap`, is a
|
||||
shortcut method that is implemented just like the `match` statement we wrote in
|
||||
Listing 9-3. If the `Result` value is the `Ok` variant, `unwrap` will return
|
||||
Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return
|
||||
the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will
|
||||
call the `panic!` macro for us.
|
||||
call the `panic!` macro for us. Here is an example of `unwrap` in action:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,should_panic
|
||||
use std::fs::File;
|
||||
@ -230,10 +236,12 @@ repr: Os { code: 2, message: "No such file or directory" } }',
|
||||
/stable-dist-rustc/build/src/libcore/result.rs:868
|
||||
```
|
||||
|
||||
There’s another method similar to `unwrap` that lets us also choose the
|
||||
`panic!` error message: `expect`. Using `expect` instead of `unwrap` and
|
||||
providing good error messages can convey your intent and make tracking down the
|
||||
source of a panic easier. The syntax of `expect` looks like this:
|
||||
Another method, `expect`, which is similar to `unwrap`, lets us also choose the
|
||||
`panic!` error message. Using `expect` instead of `unwrap` and providing good
|
||||
error messages can convey your intent and make tracking down the source of a
|
||||
panic easier. The syntax of `expect` looks like this:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust,should_panic
|
||||
use std::fs::File;
|
||||
@ -244,8 +252,8 @@ fn main() {
|
||||
```
|
||||
|
||||
We use `expect` in the same way as `unwrap`: to return the file handle or call
|
||||
the `panic!` macro. The error message that `expect` uses in its call to
|
||||
`panic!` will be the parameter that we pass to `expect` instead of the default
|
||||
the `panic!` macro. The error message used by `expect` in its call to `panic!`
|
||||
will be the parameter that we pass to `expect`, rather than the default
|
||||
`panic!` message that `unwrap` uses. Here’s what it looks like:
|
||||
|
||||
```text
|
||||
@ -254,19 +262,27 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
/stable-dist-rustc/build/src/libcore/result.rs:868
|
||||
```
|
||||
|
||||
Because this error message starts with the text we specified, `Failed to open
|
||||
hello.txt`, it will be easier to find where in the code this error message is
|
||||
coming from. If we use `unwrap` in multiple places, it can take more time to
|
||||
figure out exactly which `unwrap` is causing the panic because all `unwrap`
|
||||
calls that panic print the same message.
|
||||
|
||||
### Propagating Errors
|
||||
|
||||
When writing a function whose implementation calls something that might fail,
|
||||
instead of handling the error within this function, you can choose to let your
|
||||
caller know about the error so they can decide what to do. This is known as
|
||||
*propagating* the error, and gives more control to the calling code where there
|
||||
When you’re writing a function whose implementation calls something that might
|
||||
fail, instead of handling the error within this function, you can return the
|
||||
error to the calling code so that it can decide what to do. This is known as
|
||||
*propagating* the error and gives more control to the calling code where there
|
||||
might be more information or logic that dictates how the error should be
|
||||
handled than what you have available in the context of your code.
|
||||
|
||||
For example, Listing 9-5 shows a function that reads a username from a file. If
|
||||
For example, Listing 9-6 shows a function that reads a username from a file. If
|
||||
the file doesn’t exist or can’t be read, this function will return those errors
|
||||
to the code that called this function:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
@ -289,59 +305,61 @@ fn read_username_from_file() -> Result<String, io::Error> {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-5: A function that returns errors to the
|
||||
<span class="caption">Listing 9-6: A function that returns errors to the
|
||||
calling code using `match`</span>
|
||||
|
||||
Let’s look at the return type of the function first: `Result<String,
|
||||
io::Error>`. This means that the function is returning a value of the type
|
||||
io::Error>`. This means the function is returning a value of the type
|
||||
`Result<T, E>` where the generic parameter `T` has been filled in with the
|
||||
concrete type `String`, and the generic type `E` has been filled in with the
|
||||
concrete type `io::Error`. If this function succeeds without any problems, the
|
||||
caller of this function will receive an `Ok` value that holds a `String` — the
|
||||
username that this function read from the file. If this function encounters any
|
||||
problems, the caller of this function will receive an `Err` value that holds an
|
||||
instance of `io::Error` that contains more information about what the problems
|
||||
were. We chose `io::Error` as the return type of this function because that
|
||||
happens to be the type of the error value returned from both of the operations
|
||||
we’re calling in this function’s body that might fail: the `File::open`
|
||||
function and the `read_to_string` method.
|
||||
code that calls this function will receive an `Ok` value that holds a
|
||||
`String`—the username that this function read from the file. If this function
|
||||
encounters any problems, the code that calls this function will receive an
|
||||
`Err` value that holds an instance of `io::Error` that contains more
|
||||
information about what the problems were. We chose `io::Error` as the return
|
||||
type of this function because that happens to be the type of the error value
|
||||
returned from both of the operations we’re calling in this function’s body that
|
||||
might fail: the `File::open` function and the `read_to_string` method.
|
||||
|
||||
The body of the function starts by calling the `File::open` function. Then we
|
||||
handle the `Result` value returned with a `match` similar to the `match` in
|
||||
Listing 9-3, only instead of calling `panic!` in the `Err` case, we return
|
||||
Listing 9-4, only instead of calling `panic!` in the `Err` case, we return
|
||||
early from this function and pass the error value from `File::open` back to the
|
||||
caller as this function’s error value. If `File::open` succeeds, we store the
|
||||
file handle in the variable `f` and continue.
|
||||
calling code as this function’s error value. If `File::open` succeeds, we store
|
||||
the file handle in the variable `f` and continue.
|
||||
|
||||
Then we create a new `String` in variable `s` and call the `read_to_string`
|
||||
method on the file handle in `f` in order to read the contents of the file into
|
||||
`s`. The `read_to_string` method also returns a `Result` because it might fail,
|
||||
even though `File::open` succeeded. So we need another `match` to handle that
|
||||
method on the file handle in `f` to read the contents of the file into `s`. The
|
||||
`read_to_string` method also returns a `Result` because it might fail, even
|
||||
though `File::open` succeeded. So we need another `match` to handle that
|
||||
`Result`: if `read_to_string` succeeds, then our function has succeeded, and we
|
||||
return the username from the file that’s now in `s` wrapped in an `Ok`. If
|
||||
`read_to_string` fails, we return the error value in the same way that we
|
||||
returned the error value in the `match` that handled the return value of
|
||||
`File::open`. We don’t need to explicitly say `return`, however, since this is
|
||||
the last expression in the function.
|
||||
`File::open`. However, we don’t need to explicitly say `return`, because this
|
||||
is the last expression in the function.
|
||||
|
||||
The code that calls this code will then handle getting either an `Ok` value
|
||||
that contains a username or an `Err` value that contains an `io::Error`. We
|
||||
don’t know what the caller will do with those values. If they get an `Err`
|
||||
value, they could choose to call `panic!` and crash their program, use a
|
||||
don’t know what the calling code will do with those values. If the calling code
|
||||
gets an `Err` value, it could call `panic!` and crash the program, use a
|
||||
default username, or look up the username from somewhere other than a file, for
|
||||
example. We don’t have enough information on what the caller is actually trying
|
||||
to do, so we propagate all the success or error information upwards for them to
|
||||
handle as they see fit.
|
||||
example. We don’t have enough information on what the calling code is actually
|
||||
trying to do, so we propagate all the success or error information upwards for
|
||||
it to handle appropriately.
|
||||
|
||||
This pattern of propagating errors is so common in Rust that there is dedicated
|
||||
syntax to make this easier: `?`.
|
||||
This pattern of propagating errors is so common in Rust that Rust provides the
|
||||
question mark operator `?` to make this easier.
|
||||
|
||||
### A Shortcut for Propagating Errors: `?`
|
||||
#### A Shortcut for Propagating Errors: `?`
|
||||
|
||||
Listing 9-6 shows an implementation of `read_username_from_file` that has the
|
||||
same functionality as it had in Listing 9-5, but this implementation uses the
|
||||
Listing 9-7 shows an implementation of `read_username_from_file` that has the
|
||||
same functionality as it had in Listing 9-6, but this implementation uses the
|
||||
question mark operator:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
@ -355,26 +373,42 @@ fn read_username_from_file() -> Result<String, io::Error> {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-6: A function that returns errors to the
|
||||
<span class="caption">Listing 9-7: A function that returns errors to the
|
||||
calling code using `?`</span>
|
||||
|
||||
The `?` placed after a `Result` value is defined to work the exact same way as
|
||||
the `match` expressions we defined to handle the `Result` values in Listing
|
||||
9-5. If the value of the `Result` is an `Ok`, the value inside the `Ok` will
|
||||
The `?` placed after a `Result` value is defined to work in almost the same way
|
||||
as the `match` expressions we defined to handle the `Result` values in Listing
|
||||
9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will
|
||||
get returned from this expression and the program will continue. If the value
|
||||
is an `Err`, the value inside the `Err` will be returned from the whole
|
||||
function as if we had used the `return` keyword so that the error value gets
|
||||
propagated to the caller.
|
||||
function as if we had used the `return` keyword so the error value gets
|
||||
propagated to the calling code.
|
||||
|
||||
In the context of Listing 9-6, the `?` at the end of the `File::open` call will
|
||||
The one difference between the `match` expression from Listing 9-6 and what the
|
||||
question mark operator does is that when using the question mark operator,
|
||||
error values go through the `from` function defined in the `From` trait in the
|
||||
standard library. Many error types implement the `from` function to convert an
|
||||
error of one type into an error of another type. When used by the question mark
|
||||
operator, the call to the `from` function converts the error type that the
|
||||
question mark operator gets into the error type defined in the return type of
|
||||
the current function that we’re using `?` in. This is useful when parts of a
|
||||
function might fail for many different reasons, but the function returns one
|
||||
error type that represents all the ways the function might fail. As long as
|
||||
each error type implements the `from` function to define how to convert itself
|
||||
to the returned error type, the question mark operator takes care of the
|
||||
conversion automatically.
|
||||
|
||||
In the context of Listing 9-7, the `?` at the end of the `File::open` call will
|
||||
return the value inside an `Ok` to the variable `f`. If an error occurs, `?`
|
||||
will return early out of the whole function and give any `Err` value to our
|
||||
caller. The same thing applies to the `?` at the end of the `read_to_string`
|
||||
call.
|
||||
will return early out of the whole function and give any `Err` value to the
|
||||
calling code. The same thing applies to the `?` at the end of the
|
||||
`read_to_string` call.
|
||||
|
||||
The `?` eliminates a lot of boilerplate and makes this function’s
|
||||
implementation simpler. We could even shorten this code further by chaining
|
||||
method calls immediately after the `?`:
|
||||
method calls immediately after the `?` as shown in Listing 9-8:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
@ -390,21 +424,24 @@ fn read_username_from_file() -> Result<String, io::Error> {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-8: Chaining method calls after the question
|
||||
mark operator</span>
|
||||
|
||||
We’ve moved the creation of the new `String` in `s` to the beginning of the
|
||||
function; that part hasn’t changed. Instead of creating a variable `f`, we’ve
|
||||
chained the call to `read_to_string` directly onto the result of
|
||||
`File::open("hello.txt")?`. We still have a `?` at the end of the
|
||||
`read_to_string` call, and we still return an `Ok` value containing the
|
||||
username in `s` when both `File::open` and `read_to_string` succeed rather than
|
||||
returning errors. The functionality is again the same as in Listing 9-5 and
|
||||
Listing 9-6, this is just a different, more ergonomic way to write it.
|
||||
returning errors. The functionality is again the same as in Listing 9-6 and
|
||||
Listing 9-7; this is just a different, more ergonomic way to write it.
|
||||
|
||||
### `?` Can Only Be Used in Functions That Return `Result`
|
||||
#### `?` Can Only Be Used in Functions That Return Result
|
||||
|
||||
The `?` can only be used in functions that have a return type of `Result`,
|
||||
since it is defined to work in exactly the same way as the `match` expression
|
||||
we defined in Listing 9-5. The part of the `match` that requires a return type
|
||||
of `Result` is `return Err(e)`, so the return type of the function must be a
|
||||
because it is defined to work in the same way as the `match` expression we
|
||||
defined in Listing 9-6. The part of the `match` that requires a return type of
|
||||
`Result` is `return Err(e)`, so the return type of the function must be a
|
||||
`Result` to be compatible with this `return`.
|
||||
|
||||
Let’s look at what happens if we use `?` in the `main` function, which you’ll
|
||||
@ -418,34 +455,28 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<!-- NOTE: as of 2016-12-21, the error message when calling `?` in a function
|
||||
that doesn't return a result is STILL confusing. Since we want to only explain
|
||||
`?` now, I've changed the example, but if you try running this code you WON'T
|
||||
get the error message below.
|
||||
I'm bugging people to try and get
|
||||
https://github.com/rust-lang/rust/issues/35946 fixed soon, hopefully before this
|
||||
chapter gets through copy editing-- at that point I'll make sure to update this
|
||||
error message. /Carol -->
|
||||
|
||||
When we compile this, we get the following error message:
|
||||
When we compile this code, we get the following error message:
|
||||
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
-->
|
||||
error[E0277]: the `?` operator can only be used in a function that returns
|
||||
`Result` (or another type that implements `std::ops::Try`)
|
||||
--> src/main.rs:4:13
|
||||
|
|
||||
3 | let f = File::open("hello.txt")?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum
|
||||
`std::result::Result`
|
||||
4 | let f = File::open("hello.txt")?;
|
||||
| ------------------------
|
||||
| |
|
||||
| cannot use the `?` operator in a function that returns `()`
|
||||
| in this macro invocation
|
||||
|
|
||||
= note: expected type `()`
|
||||
= note: found type `std::result::Result<_, _>`
|
||||
= help: the trait `std::ops::Try` is not implemented for `()`
|
||||
= note: required by `std::ops::Try::from_error`
|
||||
```
|
||||
|
||||
This error is pointing out that we have mismatched types: the `main` function
|
||||
has a return type of `()`, but the `?` might return a `Result`. In functions
|
||||
that don’t return `Result`, when you call other functions that return `Result`,
|
||||
you’ll need to use a `match` or one of the `Result` methods to handle it,
|
||||
instead of using `?` to potentially propagate the error to the caller.
|
||||
This error points out that we’re only allowed to use the question mark operator
|
||||
in a function that returns `Result`. In functions that don’t return `Result`,
|
||||
when you call other functions that return `Result`, you’ll need to use a
|
||||
`match` or one of the `Result` methods to handle it instead of using `?` to
|
||||
potentially propagate the error to the calling code.
|
||||
|
||||
Now that we’ve discussed the details of calling `panic!` or returning `Result`,
|
||||
let’s return to the topic of how to decide which is appropriate to use in which
|
||||
|
@ -1,32 +1,31 @@
|
||||
## To `panic!` or Not To `panic!`
|
||||
## To `panic!` or Not to `panic!`
|
||||
|
||||
So how do you decide when you should `panic!` and when you should return
|
||||
`Result`? When code panics, there’s no way to recover. You could choose to call
|
||||
`panic!` for any error situation, whether there’s a possible way to recover or
|
||||
not, but then you’re making the decision for your callers that a situation is
|
||||
unrecoverable. When you choose to return a `Result` value, you give your caller
|
||||
options, rather than making the decision for them. They could choose to attempt
|
||||
to recover in a way that’s appropriate for their situation, or they could
|
||||
decide that actually, an `Err` value in this case is unrecoverable, so they can
|
||||
call `panic!` and turn your recoverable error into an unrecoverable one.
|
||||
Therefore, returning `Result` is a good default choice when you’re defining a
|
||||
function that might fail.
|
||||
`Result`? When code panics, there’s no way to recover. You could call `panic!`
|
||||
for any error situation, whether there’s a possible way to recover or not, but
|
||||
then you’re making the decision on behalf of the code calling your code that a
|
||||
situation is unrecoverable. When you choose to return a `Result` value, you
|
||||
give the calling code options rather than making the decision for it. The
|
||||
calling code could choose to attempt to recover in a way that’s appropriate for
|
||||
its situation, or it could decide that an `Err` value in this case is
|
||||
unrecoverable, so it can call `panic!` and turn your recoverable error into an
|
||||
unrecoverable one. Therefore, returning `Result` is a good default choice when
|
||||
you’re defining a function that might fail.
|
||||
|
||||
There are a few situations in which it’s more appropriate to write code that
|
||||
panics instead of returning a `Result`, but they are less common. Let’s discuss
|
||||
why it’s appropriate to panic in examples, prototype code, and tests, then
|
||||
situations where you as a human can know a method won’t fail that the compiler
|
||||
can’t reason about, and conclude with some general guidelines on how to decide
|
||||
In a few situations it’s more appropriate to write code that panics instead of
|
||||
returning a `Result`, but they are less common. Let’s explore why it’s
|
||||
appropriate to panic in examples, prototype code, and tests; then in situations
|
||||
where you as a human can know a method won’t fail that the compiler can’t
|
||||
reason about; and conclude with some general guidelines on how to decide
|
||||
whether to panic in library code.
|
||||
|
||||
### Examples, Prototype Code, and Tests: Perfectly Fine to Panic
|
||||
### Examples, Prototype Code, and Tests Are All Places it’s Perfectly Fine to Panic
|
||||
|
||||
When you’re writing an example to illustrate some concept, having robust error
|
||||
handling code in the example as well can make the example less clear. In
|
||||
examples, it’s understood that a call to a method like `unwrap` that could
|
||||
`panic!` is meant as a placeholder for the way that you’d actually like your
|
||||
application to handle errors, which can differ based on what the rest of your
|
||||
code is doing.
|
||||
`panic!` is meant as a placeholder for the way that you’d want your application
|
||||
to handle errors, which can differ based on what the rest of your code is doing.
|
||||
|
||||
Similarly, the `unwrap` and `expect` methods are very handy when prototyping,
|
||||
before you’re ready to decide how to handle errors. They leave clear markers in
|
||||
@ -34,10 +33,10 @@ your code for when you’re ready to make your program more robust.
|
||||
|
||||
If a method call fails in a test, we’d want the whole test to fail, even if
|
||||
that method isn’t the functionality under test. Because `panic!` is how a test
|
||||
gets marked as a failure, calling `unwrap` or `expect` is exactly what makes
|
||||
sense to do.
|
||||
is marked as a failure, calling `unwrap` or `expect` is exactly what should
|
||||
happen.
|
||||
|
||||
### Cases When You Have More Information Than The Compiler
|
||||
### Cases When You Have More Information Than the Compiler
|
||||
|
||||
It would also be appropriate to call `unwrap` when you have some other logic
|
||||
that ensures the `Result` will have an `Ok` value, but the logic isn’t
|
||||
@ -45,7 +44,7 @@ something the compiler understands. You’ll still have a `Result` value that yo
|
||||
need to handle: whatever operation you’re calling still has the possibility of
|
||||
failing in general, even though it’s logically impossible in your particular
|
||||
situation. If you can ensure by manually inspecting the code that you’ll never
|
||||
have an `Err` variant, it is perfectly acceptable to call `unwrap`. Here’s an
|
||||
have an `Err` variant, it’s perfectly acceptable to call `unwrap`. Here’s an
|
||||
example:
|
||||
|
||||
```rust
|
||||
@ -59,62 +58,62 @@ that `127.0.0.1` is a valid IP address, so it’s acceptable to use `unwrap`
|
||||
here. However, having a hardcoded, valid string doesn’t change the return type
|
||||
of the `parse` method: we still get a `Result` value, and the compiler will
|
||||
still make us handle the `Result` as if the `Err` variant is still a
|
||||
possibility since the compiler isn’t smart enough to see that this string is
|
||||
always a valid IP address. If the IP address string came from a user instead of
|
||||
being hardcoded into the program, and therefore *did* have a possibility of
|
||||
failure, we’d definitely want to handle the `Result` in a more robust way
|
||||
possibility because the compiler isn’t smart enough to see that this string is
|
||||
always a valid IP address. If the IP address string came from a user rather
|
||||
than being hardcoded into the program, and therefore *did* have a possibility
|
||||
of failure, we’d definitely want to handle the `Result` in a more robust way
|
||||
instead.
|
||||
|
||||
### Guidelines for Error Handling
|
||||
|
||||
It’s advisable to have your code `panic!` when it’s possible that you could end
|
||||
up in a bad state—in this context, bad state is when some assumption,
|
||||
guarantee, contract, or invariant has been broken, such as when invalid values,
|
||||
contradictory values, or missing values are passed to your code—plus one or
|
||||
more of the following:
|
||||
It’s advisable to have your code `panic!` when it’s possible that your code
|
||||
could end up in a bad state. In this context, bad state is when some
|
||||
assumption, guarantee, contract, or invariant has been broken, such as when
|
||||
invalid values, contradictory values, or missing values are passed to your
|
||||
code—plus one or more of the following:
|
||||
|
||||
* The bad state is not something that’s *expected* to happen occasionally
|
||||
* Your code after this point needs to rely on not being in this bad state
|
||||
* There’s not a good way to encode this information in the types you use
|
||||
* The bad state is not something that’s *expected* to happen occasionally.
|
||||
* Your code after this point needs to rely on not being in this bad state.
|
||||
* There’s not a good way to encode this information in the types you use.
|
||||
|
||||
If someone calls your code and passes in values that don’t make sense, the best
|
||||
thing might be to `panic!` and alert the person using your library to the bug
|
||||
in their code so that they can fix it during development. Similarly, `panic!`
|
||||
is often appropriate if you’re calling external code that is out of your
|
||||
control, and it returns an invalid state that you have no way of fixing.
|
||||
choice might be to `panic!` and alert the person using your library to the bug
|
||||
in their code so they can fix it during development. Similarly, `panic!` is
|
||||
often appropriate if you’re calling external code that is out of your control,
|
||||
and it returns an invalid state that you have no way of fixing.
|
||||
|
||||
When a bad state is reached, but it’s expected to happen no matter how well you
|
||||
write your code, it’s still more appropriate to return a `Result` rather than
|
||||
calling `panic!`. Examples of this include a parser being given malformed data,
|
||||
or an HTTP request returning a status that indicates you have hit a rate limit.
|
||||
In these cases, you should indicate that failure is an expected possibility by
|
||||
returning a `Result` in order to propagate these bad states upwards so that the
|
||||
caller can decide how they would like to handle the problem. To `panic!`
|
||||
wouldn’t be the best way to handle these cases.
|
||||
making a `panic!` call. Examples of this include a parser being given malformed
|
||||
data or an HTTP request returning a status that indicates you have hit a rate
|
||||
limit. In these cases, you should indicate that failure is an expected
|
||||
possibility by returning a `Result` to propagate these bad states upwards so
|
||||
the calling code can decide how to handle the problem. To `panic!` wouldn’t be
|
||||
the best way to handle these cases.
|
||||
|
||||
When your code performs operations on values, your code should verify the
|
||||
values are valid first, and `panic!` if the values aren’t valid. This is mostly
|
||||
for safety reasons: attempting to operate on invalid data can expose your code
|
||||
to vulnerabilities. This is the main reason that the standard library will
|
||||
`panic!` if you attempt an out-of-bounds array access: trying to access memory
|
||||
that doesn’t belong to the current data structure is a common security problem.
|
||||
to vulnerabilities. This is the main reason the standard library will `panic!`
|
||||
if you attempt an out-of-bounds memory access: trying to access memory that
|
||||
doesn’t belong to the current data structure is a common security problem.
|
||||
Functions often have *contracts*: their behavior is only guaranteed if the
|
||||
inputs meet particular requirements. Panicking when the contract is violated
|
||||
makes sense because a contract violation always indicates a caller-side bug,
|
||||
and it is not a kind of error you want callers to have to explicitly handle. In
|
||||
fact, there’s no reasonable way for calling code to recover: the calling
|
||||
*programmers* need to fix the code. Contracts for a function, especially when a
|
||||
violation will cause a panic, should be explained in the API documentation for
|
||||
the function.
|
||||
and it’s not a kind of error you want the calling code to have to explicitly
|
||||
handle. In fact, there’s no reasonable way for calling code to recover: the
|
||||
calling *programmers* need to fix the code. Contracts for a function,
|
||||
especially when a violation will cause a panic, should be explained in the API
|
||||
documentation for the function.
|
||||
|
||||
Having lots of error checks in all of your functions would be verbose and
|
||||
annoying, though. Luckily, you can use Rust’s type system (and thus the type
|
||||
checking the compiler does) to do a lot of the checks for you. If your function
|
||||
However, having lots of error checks in all of your functions would be verbose
|
||||
and annoying. Fortunately, you can use Rust’s type system (and thus the type
|
||||
checking the compiler does) to do many of the checks for you. If your function
|
||||
has a particular type as a parameter, you can proceed with your code’s logic
|
||||
knowing that the compiler has already ensured you have a valid value. For
|
||||
example, if you have a type rather than an `Option`, your program expects to
|
||||
have *something* rather than *nothing*. Your code then doesn’t have to handle
|
||||
two cases for the `Some` and `None` variants, it will only have one case for
|
||||
two cases for the `Some` and `None` variants: it will only have one case for
|
||||
definitely having a value. Code trying to pass nothing to your function won’t
|
||||
even compile, so your function doesn’t have to check for that case at runtime.
|
||||
Another example is using an unsigned integer type like `u32`, which ensures the
|
||||
@ -123,19 +122,19 @@ parameter is never negative.
|
||||
### Creating Custom Types for Validation
|
||||
|
||||
Let’s take the idea of using Rust’s type system to ensure we have a valid value
|
||||
one step further, and look at creating a custom type for validation. Recall the
|
||||
guessing game in Chapter 2, where our code asked the user to guess a number
|
||||
between 1 and 100. We actually never validated that the user’s guess was
|
||||
between those numbers before checking it against our secret number, only that
|
||||
it was positive. In this case, the consequences were not very dire: our output
|
||||
of “Too high” or “Too low” would still be correct. It would be a useful
|
||||
enhancement to guide the user towards valid guesses, though, and have different
|
||||
behavior when a user guesses a number that’s out of range versus when a user
|
||||
types, for example, letters instead.
|
||||
one step further and look at creating a custom type for validation. Recall the
|
||||
guessing game in Chapter 2 where our code asked the user to guess a number
|
||||
between 1 and 100. We never validated that the user’s guess was between those
|
||||
numbers before checking it against our secret number; we only validated that
|
||||
the guess was positive. In this case, the consequences were not very dire: our
|
||||
output of “Too high” or “Too low” would still be correct. It would be a useful
|
||||
enhancement to guide the user toward valid guesses and have different behavior
|
||||
when a user guesses a number that’s out of range versus when a user types, for
|
||||
example, letters instead.
|
||||
|
||||
One way to do this would be to parse the guess as an `i32` instead of only a
|
||||
`u32`, to allow potentially negative numbers, then add a check for the number
|
||||
being in range:
|
||||
`u32` to allow potentially negative numbers, and then add a check for the
|
||||
number being in range, like so:
|
||||
|
||||
```rust,ignore
|
||||
loop {
|
||||
@ -156,7 +155,7 @@ loop {
|
||||
}
|
||||
```
|
||||
|
||||
The `if` expression checks to see if our value is out of range, tells the user
|
||||
The `if` expression checks whether our value is out of range, tells the user
|
||||
about the problem, and calls `continue` to start the next iteration of the loop
|
||||
and ask for another guess. After the `if` expression, we can proceed with the
|
||||
comparisons between `guess` and the secret number knowing that `guess` is
|
||||
@ -170,7 +169,7 @@ to have a check like this in every function.
|
||||
Instead, we can make a new type and put the validations in a function to create
|
||||
an instance of the type rather than repeating the validations everywhere. That
|
||||
way, it’s safe for functions to use the new type in their signatures and
|
||||
confidently use the values they receive. Listing 9-8 shows one way to define a
|
||||
confidently use the values they receive. Listing 9-9 shows one way to define a
|
||||
`Guess` type that will only create an instance of `Guess` if the `new` function
|
||||
receives a value between 1 and 100:
|
||||
|
||||
@ -196,7 +195,7 @@ impl Guess {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 9-8: A `Guess` type that will only continue with
|
||||
<span class="caption">Listing 9-9: A `Guess` type that will only continue with
|
||||
values between 1 and 100</span>
|
||||
|
||||
First, we define a struct named `Guess` that has a field named `value` that
|
||||
@ -205,35 +204,35 @@ holds a `u32`. This is where the number will be stored.
|
||||
Then we implement an associated function named `new` on `Guess` that creates
|
||||
instances of `Guess` values. The `new` function is defined to have one
|
||||
parameter named `value` of type `u32` and to return a `Guess`. The code in the
|
||||
body of the `new` function tests `value` to make sure it is between 1 and 100.
|
||||
If `value` doesn’t pass this test, we call `panic!`, which will alert the
|
||||
programmer who is calling this code that they have a bug they need to fix,
|
||||
since creating a `Guess` with a `value` outside this range would violate the
|
||||
contract that `Guess::new` is relying on. The conditions in which `Guess::new`
|
||||
might panic should be discussed in its public-facing API documentation; we’ll
|
||||
cover documentation conventions around indicating the possibility of a `panic!`
|
||||
in the API documentation that you create in Chapter 14. If `value` does pass
|
||||
the test, we create a new `Guess` with its `value` field set to the `value`
|
||||
parameter and return the `Guess`.
|
||||
body of the `new` function tests `value` to make sure it’s between 1 and 100.
|
||||
If `value` doesn’t pass this test, we make a `panic!` call, which will alert
|
||||
the programmer who is writing the calling code that they have a bug they need
|
||||
to fix, because creating a `Guess` with a `value` outside this range would
|
||||
violate the contract that `Guess::new` is relying on. The conditions in which
|
||||
`Guess::new` might panic should be discussed in its public-facing API
|
||||
documentation; we’ll cover documentation conventions indicating the possibility
|
||||
of a `panic!` in the API documentation that you create in Chapter 14. If
|
||||
`value` does pass the test, we create a new `Guess` with its `value` field set
|
||||
to the `value` parameter and return the `Guess`.
|
||||
|
||||
Next, we implement a method named `value` that borrows `self`, doesn’t have any
|
||||
other parameters, and returns a `u32`. This is a kind of method sometimes
|
||||
called a *getter*, since its purpose is to get some data from its fields and
|
||||
called a *getter*, because its purpose is to get some data from its fields and
|
||||
return it. This public method is necessary because the `value` field of the
|
||||
`Guess` struct is private. It’s important that the `value` field is private so
|
||||
that code using the `Guess` struct is not allowed to set `value` directly:
|
||||
callers outside the module *must* use the `Guess::new` function to create an
|
||||
instance of `Guess`, which ensures there’s no way for a `Guess` to have a
|
||||
`value` that hasn’t been checked by the conditions in the `Guess::new` function.
|
||||
code using the `Guess` struct is not allowed to set `value` directly: code
|
||||
outside the module *must* use the `Guess::new` function to create an instance
|
||||
of `Guess`, which ensures there’s no way for a `Guess` to have a `value` that
|
||||
hasn’t been checked by the conditions in the `Guess::new` function.
|
||||
|
||||
A function that has a parameter or returns only numbers between 1 and 100 could
|
||||
then declare in its signature that it takes or returns a `Guess` rather than a
|
||||
`u32`, and wouldn’t need to do any additional checks in its body.
|
||||
`u32` and wouldn’t need to do any additional checks in its body.
|
||||
|
||||
## Summary
|
||||
|
||||
Rust’s error handling features are designed to help you write more robust code.
|
||||
The `panic!` macro signals that your program is in a state it can’t handle, and
|
||||
The `panic!` macro signals that your program is in a state it can’t handle and
|
||||
lets you tell the process to stop instead of trying to proceed with invalid or
|
||||
incorrect values. The `Result` enum uses Rust’s type system to indicate that
|
||||
operations might fail in a way that your code could recover from. You can use
|
||||
@ -241,6 +240,7 @@ operations might fail in a way that your code could recover from. You can use
|
||||
success or failure as well. Using `panic!` and `Result` in the appropriate
|
||||
situations will make your code more reliable in the face of inevitable problems.
|
||||
|
||||
Now that we’ve seen useful ways that the standard library uses generics with
|
||||
the `Option` and `Result` enums, let’s talk about how generics work and how you
|
||||
can make use of them in your code.
|
||||
Now that you’ve seen useful ways that the standard library uses generics with
|
||||
the `Option` and `Result` enums, we’ll talk about how generics work and how you
|
||||
can use them in your code in the next chapter.
|
||||
|
||||
|
@ -42,14 +42,14 @@ pub trait Summarizable {
|
||||
consists of the behavior provided by a `summary` method</span>
|
||||
|
||||
We declare a trait with the `trait` keyword, then the trait’s name, in this
|
||||
case `Summarizable`. Inside curly braces we declare the method signatures that
|
||||
describe the behaviors that types that implement this trait will need to have,
|
||||
in this case `fn summary(&self) -> String`. After the method signature, instead
|
||||
of providing an implementation within curly braces, we put a semicolon. Each
|
||||
type that implements this trait must then provide its own custom behavior for
|
||||
the body of the method, but the compiler will enforce that any type that has
|
||||
the `Summarizable` trait will have the method `summary` defined for it with
|
||||
this signature exactly.
|
||||
case `Summarizable`. Inside curly brackets we declare the method signatures
|
||||
that describe the behaviors that types that implement this trait will need to
|
||||
have, in this case `fn summary(&self) -> String`. After the method signature,
|
||||
instead of providing an implementation within curly brackets, we put a
|
||||
semicolon. Each type that implements this trait must then provide its own
|
||||
custom behavior for the body of the method, but the compiler will enforce that
|
||||
any type that has the `Summarizable` trait will have the method `summary`
|
||||
defined for it with this signature exactly.
|
||||
|
||||
A trait can have multiple methods in its body, with the method signatures
|
||||
listed one per line and each line ending in a semicolon.
|
||||
@ -106,7 +106,7 @@ related to a trait. The difference is after `impl`, we put the trait name that
|
||||
we want to implement, then say `for` and the name of the type that we want to
|
||||
implement the trait for. Within the `impl` block, we put the method signatures
|
||||
that the trait definition has defined, but instead of putting a semicolon after
|
||||
each signature, we put curly braces and fill in the method body with the
|
||||
each signature, we put curly brackets and fill in the method body with the
|
||||
specific behavior that we want the methods of the trait to have for the
|
||||
particular type.
|
||||
|
||||
@ -187,7 +187,7 @@ behavior. When we implement the trait on a particular type, we can choose to
|
||||
keep or override each method’s default behavior.
|
||||
|
||||
Listing 10-15 shows how we could have chosen to specify a default string for
|
||||
the `summary` method of the `Summarize` trait instead of only choosing to only
|
||||
the `summary` method of the `Summarize` trait instead of choosing to only
|
||||
define the method signature like we did in Listing 10-12:
|
||||
|
||||
<span class="filename">Filename: lib.rs</span>
|
||||
@ -240,7 +240,7 @@ implementation.
|
||||
Default implementations are allowed to call the other methods in the same
|
||||
trait, even if those other methods don’t have a default implementation. In this
|
||||
way, a trait can provide a lot of useful functionality and only require
|
||||
implementers to specify a small part of it. We could choose to have the
|
||||
implementors to specify a small part of it. We could choose to have the
|
||||
`Summarizable` trait also have an `author_summary` method whose implementation
|
||||
is required, then a `summary` method that has a default implementation that
|
||||
calls the `author_summary` method:
|
||||
@ -519,7 +519,7 @@ let s = 3.to_string();
|
||||
```
|
||||
|
||||
Blanket implementations appear in the documentation for the trait in the
|
||||
“Implementers” section.
|
||||
“Implementors” section.
|
||||
|
||||
Traits and trait bounds let us write code that uses generic type parameters in
|
||||
order to reduce duplication, but still specify to the compiler exactly what
|
||||
|
@ -66,7 +66,7 @@ error: `x` does not live long enough
|
||||
```
|
||||
|
||||
The variable `x` doesn’t “live long enough.” Why not? Well, `x` is going to go
|
||||
out of scope when we hit the closing curly brace on line 7, ending the inner
|
||||
out of scope when we hit the closing curly bracket on line 7, ending the inner
|
||||
scope. But `r` is valid for the outer scope; its scope is larger and we say
|
||||
that it “lives longer.” If Rust allowed this code to work, `r` would be
|
||||
referencing memory that was deallocated when `x` went out of scope, and
|
||||
@ -101,9 +101,9 @@ Listing 10-18 with annotations showing the lifetimes of the variables:
|
||||
correct? I want to leave a note for production, make sure we can make that
|
||||
clear -->
|
||||
<!-- Yes, the inside block for the `'b` lifetime starts with the `let x = 5;`
|
||||
line and ends with the first closing curly brace on the 7th line. Do you think
|
||||
the text art comments work or should we make an SVG diagram that has nicer
|
||||
looking arrows and labels? /Carol -->
|
||||
line and ends with the first closing curly bracket on the 7th line. Do you
|
||||
think the text art comments work or should we make an SVG diagram that has
|
||||
nicer looking arrows and labels? /Carol -->
|
||||
|
||||
We’ve annotated the lifetime of `r` with `'a` and the lifetime of `x` with
|
||||
`'b`. As you can see, the inner `'b` block is much smaller than the outer `'a`
|
||||
@ -184,7 +184,7 @@ and below). If these topics are confusing you in this context, I'd be
|
||||
interested to know if rereading Chapter 4 clears up that confusion.
|
||||
/Carol -->
|
||||
|
||||
Refer back to the “String Slices as Arguments” section of Chapter 4 for more
|
||||
Refer back to the “String Slices as Parameters” section of Chapter 4 for more
|
||||
discussion about why these are the arguments we want.
|
||||
|
||||
If we try to implement the `longest` function as shown in Listing 10-22, it
|
||||
|
@ -211,8 +211,7 @@ helps us check that our code is functioning in the way we intend.
|
||||
|
||||
Remember all the way back in Chapter 5, Listing 5-9, where we had a `Rectangle`
|
||||
struct and a `can_hold` method, repeated here in Listing 11-5. Let’s put this
|
||||
code in *src/lib.rs* instead of *src/main.rs* and write some tests for it using
|
||||
the `assert!` macro.
|
||||
code in *src/lib.rs* and write some tests for it using the `assert!` macro.
|
||||
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
@ -592,7 +591,7 @@ Listing 11-8 shows how we’d write a test that checks the error conditions of
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
struct Guess {
|
||||
pub struct Guess {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
@ -638,7 +637,7 @@ Looks good! Now let’s introduce a bug in our code, by removing the condition
|
||||
that the `new` function will panic if the value is greater than 100:
|
||||
|
||||
```rust
|
||||
# struct Guess {
|
||||
# pub struct Guess {
|
||||
# value: u32,
|
||||
# }
|
||||
#
|
||||
@ -686,7 +685,7 @@ different messages depending on whether the value was too small or too large:
|
||||
<span class="filename">Filename: src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
struct Guess {
|
||||
pub struct Guess {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
@ -764,7 +763,7 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
|
||||
```
|
||||
|
||||
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 expected string `'Guess value must be
|
||||
less than or equal to 100'`. We can see the panic message that we did get,
|
||||
which in this case was `Guess value must be greater than or equal to 1, got
|
||||
200.` We could then start figuring out where our bug was!
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user