New upstream version 1.22.1+dfsg1

This commit is contained in:
Ximin Luo 2017-11-25 15:21:16 +01:00
parent 3b2f29766f
commit ea8adc8c7c
3140 changed files with 261132 additions and 54906 deletions

View File

@ -1,4 +1,5 @@
# Contributing to Rust # Contributing to Rust
[contributing-to-rust]: #contributing-to-rust
Thank you for your interest in contributing to Rust! There are many ways to 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 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]. 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 [internals]: https://internals.rust-lang.org
[coc]: https://www.rust-lang.org/conduct.html [coc]: https://www.rust-lang.org/conduct.html
## Feature Requests ## Feature Requests
[feature-requests]: #feature-requests
To request a change to the way that the Rust language works, please open an 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) 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. must go through the RFC process.
## Bug Reports ## Bug Reports
[bug-reports]: #bug-reports
While bugs are unfortunate, they're a reality in software. We can't fix what we 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 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]: #the-build-system
Rust's build system allows you to bootstrap the compiler, run tests & Rust's build system allows you to bootstrap the compiler, run tests &
benchmarks, generate documentation, install a fresh build of Rust, and more. 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/ [bootstrap]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/
### Configuration ### Configuration
[configuration]: #configuration
Before you can start building the compiler you need to configure the build for 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 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. `./configure` - you may need to delete it for `config.toml` to work.
### Building ### 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 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 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. They are still available, but `x.py` is the recommended build system.
### Useful commands ### Useful commands
[useful-commands]: #useful-commands
Some common invocations of `x.py` are: 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 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 more than 99 characters in a single line should be kept in mind when writing
code. 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
Pull requests are the primary mechanism we use to change Rust. GitHub itself 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. 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 it can be found
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md). [here](https://github.com/rust-lang/rust-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]: #writing-documentation
Documentation improvements are very welcome. The source of `doc.rust-lang.org` 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 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. verify that the HTML is right.
## Issue Triage ## Issue Triage
[issue-triage]: #issue-triage
Sometimes, an issue will stay open, even though the bug has been fixed. And 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 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**. * 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 * Green, **E**-prefixed labels explain the level of **experience** necessary
to fix the issue. 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 * Red, **I**-prefixed labels indicate the **importance** of the issue. The
[I-nominated][inom] label indicates that an issue has been nominated for [I-nominated][inom] label indicates that an issue has been nominated for
prioritizing at the next triage meeting. 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 * Orange, **P**-prefixed labels indicate a bug's **priority**. These labels
are only assigned during triage meetings, and replace the [I-nominated][inom] are only assigned during triage meetings, and replace the [I-nominated][inom]
label. 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. * 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. 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 [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 [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 [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]: #out-of-tree-contributions
There are a number of other ways to contribute to Rust that don't deal with There are a number of other ways to contribute to Rust that don't deal with
this repository. this repository.
@ -392,11 +483,13 @@ valuable!
[community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library
## Helpful Links and Information ## Helpful Links and Information
[helpful-info]: #helpful-info
For people new to Rust, and just starting to contribute, or even for For people new to Rust, and just starting to contribute, or even for
more seasoned developers, some useful places to look for information more seasoned developers, some useful places to look for information
are: 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 * The [Rust Internals forum][rif], a place to ask questions and
discuss Rust's internals discuss Rust's internals
* The [generated documentation for rust's compiler][gdfrustc] * 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 [gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here
[rif]: http://internals.rust-lang.org [rif]: http://internals.rust-lang.org
[rr]: https://doc.rust-lang.org/book/README.html [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/ [tlgba]: http://tomlee.co/2014/04/a-more-detailed-tour-of-the-rust-compiler/
[ro]: http://www.rustaceans.org/ [ro]: http://www.rustaceans.org/
[rctd]: ./src/test/COMPILER_TESTS.md [rctd]: ./src/test/COMPILER_TESTS.md

View File

@ -6,6 +6,7 @@ standard library, and documentation.
[Rust]: https://www.rust-lang.org [Rust]: https://www.rust-lang.org
## Quick Start ## Quick Start
[quick-start]: #quick-start
Read ["Installation"] from [The Book]. Read ["Installation"] from [The Book].
@ -13,6 +14,7 @@ Read ["Installation"] from [The Book].
[The Book]: https://doc.rust-lang.org/book/index.html [The Book]: https://doc.rust-lang.org/book/index.html
## Building from Source ## Building from Source
[building-from-source]: #building-from-source
1. Make sure you have installed the dependencies: 1. Make sure you have installed the dependencies:
@ -52,6 +54,7 @@ Read ["Installation"] from [The Book].
[Cargo]: https://github.com/rust-lang/cargo [Cargo]: https://github.com/rust-lang/cargo
### Building on Windows ### Building on Windows
[building-on-windows]: #building-on-windows
There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by 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 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. build.
#### MinGW #### MinGW
[windows-mingw]: #windows-mingw
[MSYS2][msys2] can be used to easily build Rust on Windows: [MSYS2][msys2] can be used to easily build Rust on Windows:
@ -101,6 +105,7 @@ build.
``` ```
#### MSVC #### MSVC
[windows-msvc]: #windows-msvc
MSVC builds of Rust additionally require an installation of Visual Studio 2013 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” (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]: #specifying-an-abi
Each specific ABI can also be used from either environment (for example, using 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 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. section.
### Configure and Make ### Configure and Make
[configure-and-make]: #configure-and-make
While it's not the recommended build system, this project also provides a 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`). 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. `config.mk` file.
## Building Documentation ## Building Documentation
[building-documentation]: #building-documentation
If youd like to build the documentation, its almost the same: If youd like to build the documentation, its 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`. `build\x86_64-pc-windows-msvc\doc`.
## Notes ## Notes
[notes]: #notes
Since the Rust compiler is written in Rust, it must be built by a 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 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 [CONTRIBUTING.md]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md
## Getting Help ## Getting Help
[getting-help]: #getting-help
The Rust community congregates in a few places: 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/ [users.rust-lang.org]: https://users.rust-lang.org/
## Contributing ## Contributing
[contributing]: #contributing
To contribute to Rust, please see [CONTRIBUTING](CONTRIBUTING.md). 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 [#rust-beginners]: irc://irc.mozilla.org/rust-beginners
## License ## License
[license]: #license
Rust is primarily distributed under the terms of both the MIT 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 and the Apache License (Version 2.0), with portions covered by various

View File

@ -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) Version 1.20.0 (2017-08-31)
=========================== ===========================
Language Language
-------- --------
- [Associated constants in traits is now stabilised.][42809] - [Associated constants are now stabilised.][42809]
- [A lot of macro bugs are now fixed.][42913] - [A lot of macro bugs are now fixed.][42913]
Compiler Compiler
@ -77,7 +175,7 @@ Stabilized APIs
- [`slice::sort_unstable_by_key`] - [`slice::sort_unstable_by_key`]
- [`slice::sort_unstable_by`] - [`slice::sort_unstable_by`]
- [`slice::sort_unstable`] - [`slice::sort_unstable`]
- [`ste::from_boxed_utf8_unchecked`] - [`str::from_boxed_utf8_unchecked`]
- [`str::as_bytes_mut`] - [`str::as_bytes_mut`]
- [`str::as_bytes_mut`] - [`str::as_bytes_mut`]
- [`str::from_utf8_mut`] - [`str::from_utf8_mut`]
@ -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_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_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 [`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::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_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 [`str::from_utf8_unchecked_mut`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked_mut.html

379
config.toml.example Normal file
View 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
View File

@ -1,779 +1,17 @@
#!/bin/sh #!/bin/sh
# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/bash is. script="$(dirname $0)"/src/bootstrap/configure.py
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
msg() { try() {
echo "configure: $*" cmd=$1
}
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
shift shift
local P T=$($cmd --version 2>/dev/null)
local T if [ $? -eq 0 ]; then
for P exec $cmd "$script" "$@"
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: $*"
fi fi
} }
validate_opt () { try python2.7 "$@"
for arg in $CFG_CONFIGURE_ARGS try python27 "$@"
do try python2 "$@"
isArgValid=0 exec python $script "$@"
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

1
git-commit-hash Normal file
View File

@ -0,0 +1 @@
05e2e1c41414e8fc73d0f267ea8dab1a3eeeaa99

910
src/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ members = [
"tools/cargo", "tools/cargo",
"tools/rustdoc", "tools/rustdoc",
"tools/rls", "tools/rls",
"tools/rustfmt",
# FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude # FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude
"tools/rls/test_data/borrow_error", "tools/rls/test_data/borrow_error",
"tools/rls/test_data/completion", "tools/rls/test_data/completion",
@ -37,6 +38,8 @@ members = [
"tools/rls/test_data/infer_custom_bin", "tools/rls/test_data/infer_custom_bin",
"tools/rls/test_data/infer_lib", "tools/rls/test_data/infer_lib",
"tools/rls/test_data/omit_init_build", "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 # Curiously, compiletest will segfault if compiled with opt-level=3 on 64-bit
@ -55,3 +58,9 @@ debug-assertions = false
[profile.test] [profile.test]
debug = false debug = false
debug-assertions = false debug-assertions = false
[patch."https://github.com/rust-lang/cargo"]
cargo = { path = "tools/cargo" }
[patch.crates-io]
rustfmt-nightly = { path = "tools/rustfmt" }

View File

@ -34,7 +34,7 @@ cmake = "0.1.23"
filetime = "0.1" filetime = "0.1"
num_cpus = "1.0" num_cpus = "1.0"
getopts = "0.2" getopts = "0.2"
gcc = "0.3.50" cc = "1.0"
libc = "0.2" libc = "0.2"
serde = "1.0.8" serde = "1.0.8"
serde_derive = "1.0.8" serde_derive = "1.0.8"

View File

@ -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. There are currently two methods for configuring the rustbuild build system.
First, rustbuild offers a TOML-based configuration system with a `config.toml` 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 file. An example of this configuration can be found at `config.toml.example`,
be found at `config.toml.example`, and the configuration file can also be passed and the configuration file can also be passed as `--config path/to/config.toml`
as `--config path/to/config.toml` if the build system is being invoked manually if the build system is being invoked manually (via the python script).
(via the python script).
Next, the `./configure` options serialized in `config.mk` will be Next, the `./configure` options serialized in `config.mk` will be
parsed and read. That is, if any `./configure` options are passed, they'll be parsed and read. That is, if any `./configure` options are passed, they'll be

View File

@ -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) // Force all crates compiled by this compiler to (a) be unstable and (b)
// allow the `rustc_private` feature to link to other unstable crates // allow the `rustc_private` feature to link to other unstable crates
// also in the sysroot. // also in the sysroot.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
extern crate gcc; extern crate cc;
use std::env; use std::env;
use std::process::{self, Command}; use std::process::{self, Command};
@ -18,12 +18,13 @@ fn main() {
// Locate the actual compiler that we're invoking // Locate the actual compiler that we're invoking
env::remove_var("CC"); env::remove_var("CC");
env::remove_var("CXX"); env::remove_var("CXX");
let mut cfg = gcc::Config::new(); let mut cfg = cc::Build::new();
cfg.cargo_metadata(false) cfg.cargo_metadata(false)
.out_dir("/") .out_dir("/")
.target(&target) .target(&target)
.host(&target) .host(&target)
.opt_level(0) .opt_level(0)
.warnings(false)
.debug(false); .debug(false);
let compiler = cfg.get_compiler(); let compiler = cfg.get_compiler();

View File

@ -167,6 +167,141 @@ def format_build_time(duration):
return str(datetime.timedelta(seconds=int(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): class RustBuild(object):
"""Provide all the methods required to build Rust""" """Provide all the methods required to build Rust"""
def __init__(self): def __init__(self):
@ -177,7 +312,6 @@ class RustBuild(object):
self.build = '' self.build = ''
self.build_dir = os.path.join(os.getcwd(), "build") self.build_dir = os.path.join(os.getcwd(), "build")
self.clean = False self.clean = False
self.config_mk = ''
self.config_toml = '' self.config_toml = ''
self.printed = False self.printed = False
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) 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 self.get_string(value) or value.strip()
return None 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): def cargo(self):
"""Return config path for cargo""" """Return config path for cargo"""
return self.program_config('cargo') return self.program_config('cargo')
@ -407,15 +521,9 @@ class RustBuild(object):
>>> rb = RustBuild() >>> rb = RustBuild()
>>> rb.config_toml = 'rustc = "rustc"\\n' >>> rb.config_toml = 'rustc = "rustc"\\n'
>>> rb.config_mk = 'CFG_LOCAL_RUST_ROOT := /tmp/rust\\n'
>>> rb.program_config('rustc') >>> rb.program_config('rustc')
'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_toml = ''
>>> rb.config_mk = ''
>>> cargo_path = rb.program_config('cargo') >>> cargo_path = rb.program_config('cargo')
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(), >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
... "bin", "cargo") ... "bin", "cargo")
@ -424,10 +532,6 @@ class RustBuild(object):
config = self.get_toml(program) config = self.get_toml(program)
if config: if config:
return 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( return os.path.join(self.bin_root(), "bin", "{}{}".format(
program, self.exe_suffix())) program, self.exe_suffix()))
@ -439,10 +543,14 @@ class RustBuild(object):
'devel' 'devel'
""" """
start = line.find('"') start = line.find('"')
if start == -1: if start != -1:
return None
end = start + 1 + line[start + 1:].find('"') end = start + 1 + line[start + 1:].find('"')
return line[start + 1:end] 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 @staticmethod
def exe_suffix(): def exe_suffix():
@ -517,162 +625,19 @@ class RustBuild(object):
def build_triple(self): def build_triple(self):
"""Build triple as in LLVM""" """Build triple as in LLVM"""
default_encoding = sys.getdefaultencoding()
config = self.get_toml('build') config = self.get_toml('build')
if config: if config:
return config return config
config = self.get_mk('CFG_BUILD') return default_build_triple()
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)
def update_submodules(self): def update_submodules(self):
"""Update submodules""" """Update submodules"""
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \ if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
self.get_toml('submodules') == "false" or \ self.get_toml('submodules') == "false":
self.get_mk('CFG_DISABLE_MANAGE_SUBMODULES') == "1":
return return
print('Updating submodules') print('Updating submodules')
default_encoding = sys.getdefaultencoding() 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( submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
["git", "config", "--file", ["git", "config", "--file",
os.path.join(self.rust_root, ".gitmodules"), os.path.join(self.rust_root, ".gitmodules"),
@ -680,11 +645,9 @@ class RustBuild(object):
).decode(default_encoding).splitlines()] ).decode(default_encoding).splitlines()]
submodules = [module for module in submodules submodules = [module for module in submodules
if not ((module.endswith("llvm") and if not ((module.endswith("llvm") and
(self.get_toml('llvm-config') or self.get_toml('llvm-config')) or
self.get_mk('CFG_LLVM_ROOT'))) or
(module.endswith("jemalloc") and (module.endswith("jemalloc") and
(self.get_toml('jemalloc') or self.get_toml('jemalloc')))]
self.get_mk('CFG_JEMALLOC_ROOT'))))]
run(["git", "submodule", "update", run(["git", "submodule", "update",
"--init", "--recursive"] + submodules, "--init", "--recursive"] + submodules,
cwd=self.rust_root, verbose=self.verbose) cwd=self.rust_root, verbose=self.verbose)
@ -719,11 +682,7 @@ def bootstrap():
try: try:
with open(args.config or 'config.toml') as config: with open(args.config or 'config.toml') as config:
build.config_toml = config.read() build.config_toml = config.read()
except: except (OSError, IOError):
pass
try:
build.config_mk = open('config.mk').read()
except:
pass pass
if '\nverbose = 2' in build.config_toml: if '\nverbose = 2' in build.config_toml:
@ -731,11 +690,9 @@ def bootstrap():
elif '\nverbose = 1' in build.config_toml: elif '\nverbose = 1' in build.config_toml:
build.verbose = 1 build.verbose = 1
build.use_vendored_sources = '\nvendor = true' in build.config_toml or \ build.use_vendored_sources = '\nvendor = true' in build.config_toml
'CFG_ENABLE_VENDOR' in build.config_mk
build.use_locked_deps = '\nlocked-deps = true' in build.config_toml or \ build.use_locked_deps = '\nlocked-deps = true' in build.config_toml
'CFG_ENABLE_LOCKED_DEPS' in build.config_mk
if 'SUDO_USER' in os.environ and not build.use_vendored_sources: if 'SUDO_USER' in os.environ and not build.use_vendored_sources:
if os.environ.get('USER') != os.environ['SUDO_USER']: if os.environ.get('USER') != os.environ['SUDO_USER']:

View File

@ -15,6 +15,7 @@ import doctest
import unittest import unittest
import tempfile import tempfile
import hashlib import hashlib
import sys
from shutil import rmtree from shutil import rmtree
@ -110,5 +111,6 @@ if __name__ == '__main__':
TEST_LOADER.loadTestsFromTestCase(VerifyTestCase), TEST_LOADER.loadTestsFromTestCase(VerifyTestCase),
TEST_LOADER.loadTestsFromTestCase(ProgramOutOfDate)]) TEST_LOADER.loadTestsFromTestCase(ProgramOutOfDate)])
RUNNER = unittest.TextTestRunner(verbosity=2) RUNNER = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
RUNNER.run(SUITE) result = RUNNER.run(SUITE)
sys.exit(0 if result.wasSuccessful() else 1)

View File

@ -8,15 +8,16 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::fmt::Debug; use std::any::Any;
use std::hash::Hash;
use std::cell::RefCell; 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::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::fs;
use std::ops::Deref;
use std::any::Any;
use std::collections::BTreeSet;
use compile; use compile;
use install; use install;
@ -248,19 +249,19 @@ impl<'a> Builder<'a> {
compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex, compile::StartupObjects, tool::BuildManifest, tool::Rustbook, tool::ErrorIndex,
tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest, tool::UnstableBookGen, tool::Tidy, tool::Linkchecker, tool::CargoTest,
tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient, tool::Compiletest, tool::RemoteTestServer, tool::RemoteTestClient,
tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::RustInstaller, tool::Cargo, tool::Rls, tool::Rustdoc, tool::Clippy,
native::Llvm), native::Llvm, tool::Rustfmt, tool::Miri),
Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest, Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest,
check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Linkcheck, check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Rustdoc,
check::Cargotest, check::Cargo, check::Rls, check::Docs, check::ErrorIndex, check::Linkcheck, check::Cargotest, check::Cargo, check::Rls, check::Docs,
check::Distcheck), check::ErrorIndex, check::Distcheck, check::Rustfmt, check::Miri, check::Clippy),
Kind::Bench => describe!(check::Crate, check::CrateLibrustc), Kind::Bench => describe!(check::Crate, check::CrateLibrustc),
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook, Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon, doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
doc::Reference, doc::Rustdoc, doc::CargoBook), doc::Reference, doc::Rustdoc, doc::CargoBook),
Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts, Kind::Dist => describe!(dist::Docs, dist::Mingw, dist::Rustc, dist::DebuggerScripts,
dist::Std, dist::Analysis, dist::Src, dist::PlainSourceTarball, dist::Cargo, 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, Kind::Install => describe!(install::Docs, install::Std, install::Cargo, install::Rls,
install::Analysis, install::Src, install::Rustc), install::Analysis, install::Src, install::Rustc),
} }
@ -305,7 +306,7 @@ impl<'a> Builder<'a> {
Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]), Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
Subcommand::Clean => panic!(), Subcommand::Clean { .. } => panic!(),
}; };
let builder = Builder { let builder = Builder {
@ -437,9 +438,14 @@ impl<'a> Builder<'a> {
let out_dir = self.stage_out(compiler, mode); let out_dir = self.stage_out(compiler, mode);
cargo.env("CARGO_TARGET_DIR", out_dir) cargo.env("CARGO_TARGET_DIR", out_dir)
.arg(cmd) .arg(cmd)
.arg("-j").arg(self.jobs().to_string())
.arg("--target").arg(target); .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 // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
// Force cargo to output binaries with disambiguating hashes in the name // Force cargo to output binaries with disambiguating hashes in the name
cargo.env("__CARGO_DEFAULT_LIB_METADATA", &self.config.channel); cargo.env("__CARGO_DEFAULT_LIB_METADATA", &self.config.channel);
@ -475,6 +481,7 @@ impl<'a> Builder<'a> {
} else { } else {
PathBuf::from("/path/to/nowhere/rustdoc/not/required") PathBuf::from("/path/to/nowhere/rustdoc/not/required")
}) })
.env("TEST_MIRI", self.config.test_miri.to_string())
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" ")); .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
if mode != Mode::Tool { 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 // 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 // library up and running, so we can use the normal compiler to compile
// build scripts in that situation. // 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) cargo.env("RUSTC_SNAPSHOT", &self.initial_rustc)
.env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir());
} else { } else {

View File

@ -23,7 +23,7 @@
//! 6. "cc" //! 6. "cc"
//! //!
//! Some of this logic is implemented here, but much of it is farmed out to the //! 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 //! Similar logic is then used to find a C++ compiler, just some s/cc/c++/ is
//! used. //! used.
//! //!
@ -35,7 +35,7 @@ use std::process::Command;
use std::iter; use std::iter;
use build_helper::{cc2ar, output}; use build_helper::{cc2ar, output};
use gcc; use cc;
use Build; use Build;
use config::Target; 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 // 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. // 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)) { for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
let mut cfg = gcc::Config::new(); let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).debug(false) cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
.target(&target).host(&build.build); .target(&target).host(&build.build);
let config = build.config.target_config.get(&target); let config = build.config.target_config.get(&target);
if let Some(cc) = config.and_then(|c| c.cc.as_ref()) { if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
cfg.compiler(cc); cfg.compiler(cc);
} else { } else {
set_compiler(&mut cfg, "gcc", target, config, build); set_compiler(&mut cfg, Language::C, target, config, build);
} }
let compiler = cfg.get_compiler(); 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 all host triples we need to find a C++ compiler as well
for host in build.hosts.iter().cloned().chain(iter::once(build.build)) { for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
let mut cfg = gcc::Config::new(); let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true) cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
.target(&host).host(&build.build); .target(&host).host(&build.build);
let config = build.config.target_config.get(&host); let config = build.config.target_config.get(&host);
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) { if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
cfg.compiler(cxx); cfg.compiler(cxx);
} else { } else {
set_compiler(&mut cfg, "g++", host, config, build); set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
} }
let compiler = cfg.get_compiler(); let compiler = cfg.get_compiler();
build.verbose(&format!("CXX_{} = {:?}", host, compiler.path())); build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
@ -82,8 +82,8 @@ pub fn find(build: &mut Build) {
} }
} }
fn set_compiler(cfg: &mut gcc::Config, fn set_compiler(cfg: &mut cc::Build,
gnu_compiler: &str, compiler: Language,
target: Interned<String>, target: Interned<String>,
config: Option<&Target>, config: Option<&Target>,
build: &Build) { build: &Build) {
@ -94,7 +94,7 @@ fn set_compiler(cfg: &mut gcc::Config,
t if t.contains("android") => { t if t.contains("android") => {
if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) { if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
let target = target.replace("armv7", "arm"); 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)); 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. // which is a gcc version from ports, if this is the case.
t if t.contains("openbsd") => { t if t.contains("openbsd") => {
let c = cfg.get_compiler(); let c = cfg.get_compiler();
let gnu_compiler = compiler.gcc();
if !c.path().ends_with(gnu_compiler) { if !c.path().ends_with(gnu_compiler) {
return 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++",
}
}
}

View File

@ -24,12 +24,12 @@ use Build;
use config::Config; use config::Config;
// The version number // 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' // 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 // Be sure to make this starts with a dot to conform to semver pre-release
// versions (section 9) // versions (section 9)
pub const CFG_PRERELEASE_VERSION: &str = ".4"; pub const CFG_PRERELEASE_VERSION: &str = ".3";
pub struct GitInfo { pub struct GitInfo {
inner: Option<Info>, inner: Option<Info>,

View File

@ -23,7 +23,7 @@ use std::path::{PathBuf, Path};
use std::process::Command; use std::process::Command;
use std::io::Read; use std::io::Read;
use build_helper::{self, output}; use build_helper::{self, output, BuildExpectation};
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step}; use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
use cache::{INTERNER, Interned}; use cache::{INTERNER, Interned};
@ -33,6 +33,7 @@ use native;
use tool::{self, Tool}; use tool::{self, Tool};
use util::{self, dylib_path, dylib_path_var}; use util::{self, dylib_path, dylib_path_var};
use {Build, Mode}; use {Build, Mode};
use toolstate::ToolState;
const ADB_TEST_DIR: &str = "/data/tmp/work"; 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.fail_fast {
if !build.try_run(cmd) { if !build.try_run(cmd, expect) {
let failures = build.delayed_failures.get(); let mut failures = build.delayed_failures.borrow_mut();
build.delayed_failures.set(failures + 1); failures.push(format!("{:?}", cmd));
} }
} else { } 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) { fn try_run_quiet(build: &Build, cmd: &mut Command) {
if !build.fail_fast { if !build.fail_fast {
if !build.try_run_quiet(cmd) { if !build.try_run_quiet(cmd) {
let failures = build.delayed_failures.get(); let mut failures = build.delayed_failures.borrow_mut();
build.delayed_failures.set(failures + 1); failures.push(format!("{:?}", cmd));
} }
} else { } else {
build.run_quiet(cmd); build.run_quiet(cmd);
@ -241,15 +246,162 @@ impl Step for Rls {
let compiler = builder.compiler(stage, host); let compiler = builder.compiler(stage, host);
builder.ensure(tool::Rls { compiler, target: self.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"); 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 // Don't build tests dynamically, just a pain to work with
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1"); cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
builder.add_rustc_lib_path(compiler, &mut cargo); 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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Crate { pub struct Crate {
compiler: Compiler, 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 { fn envify(s: &str) -> String {
s.chars().map(|c| { s.chars().map(|c| {
match c { match c {

View File

@ -13,7 +13,7 @@
//! Responsible for cleaning out a build directory of all old and stale //! Responsible for cleaning out a build directory of all old and stale
//! artifacts to prepare for a fresh build. Currently doesn't remove the //! artifacts to prepare for a fresh build. Currently doesn't remove the
//! `build/cache` directory (download cache) or the `build/$target/llvm` //! `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::fs;
use std::io::{self, ErrorKind}; use std::io::{self, ErrorKind};
@ -21,8 +21,12 @@ use std::path::Path;
use Build; use Build;
pub fn clean(build: &Build) { pub fn clean(build: &Build, all: bool) {
rm_rf("tmp".as_ref()); rm_rf("tmp".as_ref());
if all {
rm_rf(&build.out);
} else {
rm_rf(&build.out.join("tmp")); rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist")); rm_rf(&build.out.join("dist"));
@ -42,6 +46,7 @@ pub fn clean(build: &Build) {
} }
} }
} }
}
fn rm_rf(path: &Path) { fn rm_rf(path: &Path) {
match path.symlink_metadata() { match path.symlink_metadata() {

View File

@ -10,12 +10,12 @@
//! Serialized configuration of a build. //! Serialized configuration of a build.
//! //!
//! This module implements parsing `config.mk` and `config.toml` configuration //! This module implements parsing `config.toml` configuration files to tweak
//! files to tweak how the build runs. //! how the build runs.
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::fs::{self, File}; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
@ -23,10 +23,11 @@ use std::cmp;
use num_cpus; use num_cpus;
use toml; use toml;
use util::{exe, push_exe_path}; use util::exe;
use cache::{INTERNER, Interned}; use cache::{INTERNER, Interned};
use flags::Flags; use flags::Flags;
pub use flags::Subcommand; pub use flags::Subcommand;
use toolstate::ToolStates;
/// Global configuration for the entire build and/or bootstrap. /// Global configuration for the entire build and/or bootstrap.
/// ///
@ -111,6 +112,7 @@ pub struct Config {
pub low_priority: bool, pub low_priority: bool,
pub channel: String, pub channel: String,
pub quiet_tests: bool, pub quiet_tests: bool,
pub test_miri: bool,
// Fallback musl-root for all targets // Fallback musl-root for all targets
pub musl_root: Option<PathBuf>, pub musl_root: Option<PathBuf>,
pub prefix: Option<PathBuf>, pub prefix: Option<PathBuf>,
@ -124,14 +126,14 @@ pub struct Config {
pub nodejs: Option<PathBuf>, pub nodejs: Option<PathBuf>,
pub gdb: Option<PathBuf>, pub gdb: Option<PathBuf>,
pub python: Option<PathBuf>, pub python: Option<PathBuf>,
pub configure_args: Vec<String>,
pub openssl_static: bool, pub openssl_static: bool,
pub configure_args: Vec<String>,
// These are either the stage0 downloaded binaries or the locally installed ones. // These are either the stage0 downloaded binaries or the locally installed ones.
pub initial_cargo: PathBuf, pub initial_cargo: PathBuf,
pub initial_rustc: PathBuf, pub initial_rustc: PathBuf,
pub toolstate: ToolStates,
} }
/// Per-target configuration stored in the global configuration structure. /// Per-target configuration stored in the global configuration structure.
@ -190,6 +192,8 @@ struct Build {
sanitizers: Option<bool>, sanitizers: Option<bool>,
profiler: Option<bool>, profiler: Option<bool>,
openssl_static: Option<bool>, openssl_static: Option<bool>,
configure_args: Option<Vec<String>>,
local_rebuild: Option<bool>,
} }
/// TOML representation of various global install decisions. /// TOML representation of various global install decisions.
@ -219,6 +223,7 @@ struct Llvm {
targets: Option<String>, targets: Option<String>,
experimental_targets: Option<String>, experimental_targets: Option<String>,
link_jobs: Option<u32>, link_jobs: Option<u32>,
link_shared: Option<bool>,
} }
#[derive(Deserialize, Default, Clone)] #[derive(Deserialize, Default, Clone)]
@ -265,6 +270,10 @@ struct Rust {
debuginfo_tests: Option<bool>, debuginfo_tests: Option<bool>,
codegen_tests: Option<bool>, codegen_tests: Option<bool>,
ignore_git: 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. /// TOML representation of how each build target is configured.
@ -300,6 +309,7 @@ impl Config {
config.codegen_tests = true; config.codegen_tests = true;
config.ignore_git = false; config.ignore_git = false;
config.rust_dist_src = true; config.rust_dist_src = true;
config.test_miri = false;
config.on_fail = flags.on_fail; config.on_fail = flags.on_fail;
config.stage = flags.stage; config.stage = flags.stage;
@ -326,6 +336,18 @@ impl Config {
} }
}).unwrap_or_else(|| TomlConfig::default()); }).unwrap_or_else(|| TomlConfig::default());
let toolstate_toml_path = config.src.join("src/tools/toolstate.toml");
let parse_toolstate = || -> Result<_, Box<::std::error::Error>> {
let mut f = File::open(toolstate_toml_path)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
Ok(toml::from_str(&contents)?)
};
config.toolstate = parse_toolstate().unwrap_or_else(|err| {
println!("failed to parse TOML configuration 'toolstate.toml': {}", err);
process::exit(2);
});
let build = toml.build.clone().unwrap_or(Build::default()); let build = toml.build.clone().unwrap_or(Build::default());
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x))); set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
set(&mut config.build, flags.build); set(&mut config.build, flags.build);
@ -374,6 +396,8 @@ impl Config {
set(&mut config.sanitizers, build.sanitizers); set(&mut config.sanitizers, build.sanitizers);
set(&mut config.profiler, build.profiler); set(&mut config.profiler, build.profiler);
set(&mut config.openssl_static, build.openssl_static); 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); config.verbose = cmp::max(config.verbose, flags.verbose);
if let Some(ref install) = toml.install { if let Some(ref install) = toml.install {
@ -385,6 +409,18 @@ impl Config {
config.mandir = install.mandir.clone().map(PathBuf::from); 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 { if let Some(ref llvm) = toml.llvm {
match llvm.ccache { match llvm.ccache {
Some(StringOrBool::String(ref s)) => { Some(StringOrBool::String(ref s)) => {
@ -397,31 +433,36 @@ impl Config {
} }
set(&mut config.ninja, llvm.ninja); set(&mut config.ninja, llvm.ninja);
set(&mut config.llvm_enabled, llvm.enabled); 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_optimize, llvm.optimize);
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo); set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
set(&mut config.llvm_version_check, llvm.version_check); set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp); set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
set(&mut config.llvm_link_shared, llvm.link_shared);
config.llvm_targets = llvm.targets.clone(); config.llvm_targets = llvm.targets.clone();
config.llvm_experimental_targets = llvm.experimental_targets.clone(); config.llvm_experimental_targets = llvm.experimental_targets.clone();
config.llvm_link_jobs = llvm.link_jobs; config.llvm_link_jobs = llvm.link_jobs;
} }
if let Some(ref rust) = toml.rust { if let Some(ref rust) = toml.rust {
set(&mut config.rust_debug_assertions, rust.debug_assertions); debug = rust.debug;
set(&mut config.rust_debuginfo, rust.debuginfo); debug_assertions = rust.debug_assertions;
set(&mut config.rust_debuginfo_lines, rust.debuginfo_lines); debuginfo = rust.debuginfo;
set(&mut config.rust_debuginfo_only_std, rust.debuginfo_only_std); debuginfo_lines = rust.debuginfo_lines;
set(&mut config.rust_optimize, rust.optimize); 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_optimize_tests, rust.optimize_tests);
set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests); set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
set(&mut config.codegen_tests, rust.codegen_tests); set(&mut config.codegen_tests, rust.codegen_tests);
set(&mut config.rust_rpath, rust.rpath); 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.use_jemalloc, rust.use_jemalloc);
set(&mut config.backtrace, rust.backtrace); set(&mut config.backtrace, rust.backtrace);
set(&mut config.channel, rust.channel.clone()); 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_linker = rust.default_linker.clone();
config.rustc_default_ar = rust.default_ar.clone(); config.rustc_default_ar = rust.default_ar.clone();
config.musl_root = rust.musl_root.clone().map(PathBuf::from); config.musl_root = rust.musl_root.clone().map(PathBuf::from);
@ -476,226 +517,31 @@ impl Config {
None => stage0_root.join(exe("cargo", &config.build)), None => stage0_root.join(exe("cargo", &config.build)),
}; };
// compat with `./configure` while we're still using that // Now that we've reached the end of our configuration, infer the
if fs::metadata("config.mk").is_ok() { // default values for all options that we haven't otherwise stored yet.
config.update_with_config_mk();
} 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 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 { pub fn verbose(&self) -> bool {
self.verbose > 0 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>) { fn set<T>(field: &mut T, val: Option<T>) {
if let Some(v) = val { if let Some(v) = val {
*field = v; *field = v;

417
src/bootstrap/configure.py Executable file
View 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("")

View File

@ -20,7 +20,7 @@
use std::env; use std::env;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Read, Write}; use std::io::{self, Read, Write};
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -365,6 +365,9 @@ impl Step for Rustc {
// tiny morsel of metadata is used by rust-packaging // tiny morsel of metadata is used by rust-packaging
let version = build.rust_version(); let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); 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 // 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 // include. The first argument to this script is where to put these DLLs
@ -429,7 +432,7 @@ impl Step for Rustc {
// Man pages // Man pages
t!(fs::create_dir_all(image.join("share/man/man1"))); t!(fs::create_dir_all(image.join("share/man/man1")));
cp_r(&build.src.join("man"), &image.join("share/man/man1")); cp_r(&build.src.join("src/doc/man"), &image.join("share/man/man1"));
// Debugger scripts // Debugger scripts
builder.ensure(DebuggerScripts { builder.ensure(DebuggerScripts {
@ -724,6 +727,9 @@ impl Step for Src {
let dst_src = dst.join("rust"); let dst_src = dst.join("rust");
t!(fs::create_dir_all(&dst_src)); 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 // This is the reduced set of paths which will become the rust-src component
// (essentially libstd and all of its path dependencies) // (essentially libstd and all of its path dependencies)
let std_src_dirs = [ let std_src_dirs = [
@ -754,11 +760,14 @@ impl Step for Src {
"src/libprofiler_builtins", "src/libprofiler_builtins",
]; ];
let std_src_dirs_exclude = [ let std_src_dirs_exclude = [
"src/compiler-rt/test", "src/libcompiler_builtins/compiler-rt/test",
"src/jemalloc/test/unit", "src/jemalloc/test/unit",
]; ];
copy_src_dirs(build, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src); 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 // Create source tarball in rust-installer format
let mut cmd = rust_installer(builder); let mut cmd = rust_installer(builder);
@ -822,9 +831,9 @@ impl Step for PlainSourceTarball {
"RELEASES.md", "RELEASES.md",
"configure", "configure",
"x.py", "x.py",
"config.toml.example",
]; ];
let src_dirs = [ let src_dirs = [
"man",
"src", "src",
]; ];
@ -837,6 +846,9 @@ impl Step for PlainSourceTarball {
// Create the version file // Create the version file
write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes()); 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 we're building from git sources, we need to vendor a complete distribution.
if build.rust_info.is_git() { if build.rust_info.is_git() {
@ -887,7 +899,12 @@ impl Step for PlainSourceTarball {
fn install(src: &Path, dstdir: &Path, perms: u32) { fn install(src: &Path, dstdir: &Path, perms: u32) {
let dst = dstdir.join(src.file_name().unwrap()); let dst = dstdir.join(src.file_name().unwrap());
t!(fs::create_dir_all(dstdir)); 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); chmod(&dst, perms);
} }
@ -1081,19 +1098,39 @@ impl Step for Rls {
.arg("--output-dir").arg(&distdir(build)) .arg("--output-dir").arg(&distdir(build))
.arg("--non-installed-overlay").arg(&overlay) .arg("--non-installed-overlay").arg(&overlay)
.arg(format!("--package-name={}-{}", name, target)) .arg(format!("--package-name={}-{}", name, target))
.arg("--legacy-manifest-dirs=rustlib,cargo"); .arg("--legacy-manifest-dirs=rustlib,cargo")
.arg("--component-name=rls-preview");
if build.config.channel == "nightly" {
cmd.arg("--component-name=rls");
} else {
cmd.arg("--component-name=rls-preview");
}
build.run(&mut cmd); build.run(&mut cmd);
distdir(build).join(format!("{}-{}.tar.gz", name, target)) 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)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Extended { pub struct Extended {
stage: u32, stage: u32,
@ -1156,6 +1193,9 @@ impl Step for Extended {
install(&build.src.join("LICENSE-MIT"), &overlay, 0o644); install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
let version = build.rust_version(); let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes())); 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); install(&etc.join("README.md"), &overlay, 0o644);
// When rust-std package split from rustc, we needed to ensure that during // 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 // the std files during uninstall. To do this ensure that rustc comes
// before rust-std in the list below. // before rust-std in the list below.
let mut tarballs = vec![rustc_installer, cargo_installer, rls_installer, 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") { if target.contains("pc-windows-gnu") {
tarballs.push(mingw_installer.unwrap()); tarballs.push(mingw_installer.unwrap());
} }
@ -1285,12 +1328,8 @@ impl Step for Extended {
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target)) cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target))
.join(format!("rust-std-{}", target)), .join(format!("rust-std-{}", target)),
&exe.join("rust-std")); &exe.join("rust-std"));
let rls_path = if build.config.channel == "nightly" { cp_r(&work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview"),
work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls") &exe.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, "rust-analysis"), target)) cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-analysis"), target))
.join(format!("rust-analysis-{}", target)), .join(format!("rust-analysis-{}", target)),
&exe.join("rust-analysis")); &exe.join("rust-analysis"));

View File

@ -669,11 +669,6 @@ impl Step for ErrorIndex {
let build = builder.build; let build = builder.build;
let target = self.target; let target = self.target;
builder.ensure(compile::Rustc {
compiler: builder.compiler(0, build.build),
target,
});
println!("Documenting error index ({})", target); println!("Documenting error index ({})", target);
let out = build.doc_out(target); let out = build.doc_out(target);
t!(fs::create_dir_all(&out)); t!(fs::create_dir_all(&out));

View File

@ -60,7 +60,9 @@ pub enum Subcommand {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
test_args: Vec<String>, test_args: Vec<String>,
}, },
Clean, Clean {
all: bool,
},
Dist { Dist {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
}, },
@ -136,7 +138,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
None => { None => {
// No subcommand -- show the general usage and subcommand help // No subcommand -- show the general usage and subcommand help
println!("{}\n", 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"); opts.optmulti("", "test-args", "extra arguments", "ARGS");
}, },
"bench" => { 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") { if matches.opt_present("verbose") {
let config = Config::parse(&["build".to_string()]); let config = Config::parse(&["build".to_string()]);
let mut build = Build::new(config); let mut build = Build::new(config);
@ -258,8 +261,9 @@ Arguments:
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
} else { } else if subcommand.as_str() != "clean" {
extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.", extra_help.push_str(format!(
"Run `./x.py {} -h -v` to see a list of available paths.",
subcommand).as_str()); subcommand).as_str());
} }
@ -290,10 +294,13 @@ Arguments:
} }
"clean" => { "clean" => {
if paths.len() > 0 { 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); usage(1, &opts, &subcommand_help, &extra_help);
} }
Subcommand::Clean
Subcommand::Clean {
all: matches.opt_present("all"),
}
} }
"dist" => { "dist" => {
Subcommand::Dist { Subcommand::Dist {

View File

@ -126,7 +126,7 @@ extern crate lazy_static;
extern crate serde_json; extern crate serde_json;
extern crate cmake; extern crate cmake;
extern crate filetime; extern crate filetime;
extern crate gcc; extern crate cc;
extern crate getopts; extern crate getopts;
extern crate num_cpus; extern crate num_cpus;
extern crate toml; extern crate toml;
@ -134,20 +134,21 @@ extern crate toml;
#[cfg(unix)] #[cfg(unix)]
extern crate libc; extern crate libc;
use std::cell::Cell; use std::cell::RefCell;
use std::collections::{HashSet, HashMap}; use std::collections::{HashSet, HashMap};
use std::env; use std::env;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::Read; use std::io::Read;
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use std::process::Command; use std::process::{self, Command};
use std::slice; use std::slice;
use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime}; use build_helper::{run_silent, run_suppressed, try_run_silent, try_run_suppressed, output, mtime,
BuildExpectation};
use util::{exe, libdir, OutputFolder, CiEnv}; use util::{exe, libdir, OutputFolder, CiEnv};
mod cc; mod cc_detect;
mod channel; mod channel;
mod check; mod check;
mod clean; mod clean;
@ -164,6 +165,7 @@ pub mod util;
mod builder; mod builder;
mod cache; mod cache;
mod tool; mod tool;
mod toolstate;
#[cfg(windows)] #[cfg(windows)]
mod job; mod job;
@ -239,13 +241,13 @@ pub struct Build {
// Runtime state filled in later on // Runtime state filled in later on
// target -> (cc, ar) // target -> (cc, ar)
cc: HashMap<Interned<String>, (gcc::Tool, Option<PathBuf>)>, cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>,
// host -> (cc, ar) // host -> (cc, ar)
cxx: HashMap<Interned<String>, gcc::Tool>, cxx: HashMap<Interned<String>, cc::Tool>,
crates: HashMap<Interned<String>, Crate>, crates: HashMap<Interned<String>, Crate>,
is_sudo: bool, is_sudo: bool,
ci_env: CiEnv, ci_env: CiEnv,
delayed_failures: Cell<usize>, delayed_failures: RefCell<Vec<String>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -327,7 +329,7 @@ impl Build {
lldb_python_dir: None, lldb_python_dir: None,
is_sudo, is_sudo,
ci_env: CiEnv::current(), ci_env: CiEnv::current(),
delayed_failures: Cell::new(0), delayed_failures: RefCell::new(Vec::new()),
} }
} }
@ -343,12 +345,12 @@ impl Build {
job::setup(self); job::setup(self);
} }
if let Subcommand::Clean = self.config.cmd { if let Subcommand::Clean { all } = self.config.cmd {
return clean::clean(self); return clean::clean(self, all);
} }
self.verbose("finding compilers"); self.verbose("finding compilers");
cc::find(self); cc_detect::find(self);
self.verbose("running sanity check"); self.verbose("running sanity check");
sanity::check(self); sanity::check(self);
// If local-rust is the same major.minor as the current version, then force a local-rebuild // 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); metadata::build(self);
builder::Builder::run(&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. /// Clear out `dir` if `input` is newer.
@ -542,24 +554,31 @@ impl Build {
.join(libdir(&self.config.build)) .join(libdir(&self.config.build))
} }
/// Runs a command, printing out nice contextual information if its build
/// status is not the expected one
fn run_expecting(&self, cmd: &mut Command, expect: BuildExpectation) {
self.verbose(&format!("running: {:?}", cmd));
run_silent(cmd, expect)
}
/// Runs a command, printing out nice contextual information if it fails. /// Runs a command, printing out nice contextual information if it fails.
fn run(&self, cmd: &mut Command) { fn run(&self, cmd: &mut Command) {
self.verbose(&format!("running: {:?}", cmd)); self.run_expecting(cmd, BuildExpectation::None)
run_silent(cmd)
} }
/// Runs a command, printing out nice contextual information if it fails. /// Runs a command, printing out nice contextual information if it fails.
fn run_quiet(&self, cmd: &mut Command) { fn run_quiet(&self, cmd: &mut Command) {
self.verbose(&format!("running: {:?}", cmd)); self.verbose(&format!("running: {:?}", cmd));
run_suppressed(cmd) run_suppressed(cmd, BuildExpectation::None)
} }
/// Runs a command, printing out nice contextual information if it fails. /// Runs a command, printing out nice contextual information if its build
/// Exits if the command failed to execute at all, otherwise returns its /// status is not the expected one.
/// `status.success()`. /// Exits if the command failed to execute at all, otherwise returns whether
fn try_run(&self, cmd: &mut Command) -> bool { /// the expectation was met
fn try_run(&self, cmd: &mut Command, expect: BuildExpectation) -> bool {
self.verbose(&format!("running: {:?}", cmd)); 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. /// Runs a command, printing out nice contextual information if it fails.
@ -567,7 +586,7 @@ impl Build {
/// `status.success()`. /// `status.success()`.
fn try_run_quiet(&self, cmd: &mut Command) -> bool { fn try_run_quiet(&self, cmd: &mut Command) -> bool {
self.verbose(&format!("running: {:?}", cmd)); self.verbose(&format!("running: {:?}", cmd));
try_run_suppressed(cmd) try_run_suppressed(cmd, BuildExpectation::None)
} }
pub fn is_verbose(&self) -> bool { pub fn is_verbose(&self) -> bool {
@ -600,7 +619,7 @@ impl Build {
/// specified. /// specified.
fn cflags(&self, target: Interned<String>) -> Vec<String> { fn cflags(&self, target: Interned<String>) -> Vec<String> {
// Filter out -O and /O (the optimization flags) that we picked up from // 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() let mut base = self.cc[&target].0.args().iter()
.map(|s| s.to_string_lossy().into_owned()) .map(|s| s.to_string_lossy().into_owned())
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) .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 { fn force_use_stage1(&self, compiler: Compiler, target: Interned<String>) -> bool {
!self.config.full_bootstrap && !self.config.full_bootstrap &&
compiler.stage >= 2 && 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 /// 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) 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. /// Returns the `a.b.c` version that the given package is at.
fn release_num(&self, package: &str) -> String { fn release_num(&self, package: &str) -> String {
let mut toml = String::new(); let mut toml = String::new();

View File

@ -8,8 +8,6 @@
# option. This file may not be copied, modified, or distributed # option. This file may not be copied, modified, or distributed
# except according to those terms. # except according to those terms.
include config.mk
ifdef VERBOSE ifdef VERBOSE
Q := Q :=
BOOTSTRAP_ARGS := -v BOOTSTRAP_ARGS := -v
@ -57,6 +55,8 @@ check-aux:
src/tools/cargotest \ src/tools/cargotest \
src/tools/cargo \ src/tools/cargo \
src/tools/rls \ src/tools/rls \
src/tools/rustfmt \
src/tools/miri \
src/test/pretty \ src/test/pretty \
src/test/run-pass/pretty \ src/test/run-pass/pretty \
src/test/run-fail/pretty \ src/test/run-fail/pretty \

View File

@ -27,7 +27,7 @@ use std::process::Command;
use build_helper::output; use build_helper::output;
use cmake; use cmake;
use gcc; use cc;
use Build; use Build;
use util; use util;
@ -289,7 +289,7 @@ impl Step for TestHelpers {
let _folder = build.fold_output(|| "build_test_helpers"); let _folder = build.fold_output(|| "build_test_helpers");
println!("Building test helpers"); println!("Building test helpers");
t!(fs::create_dir_all(&dst)); 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 // We may have found various cross-compilers a little differently due to our
// extra configuration, so inform gcc of these compilers. Note, though, that // extra configuration, so inform gcc of these compilers. Note, though, that
@ -306,6 +306,7 @@ impl Step for TestHelpers {
.target(&target) .target(&target)
.host(&build.build) .host(&build.build)
.opt_level(0) .opt_level(0)
.warnings(false)
.debug(false) .debug(false)
.file(build.src.join("src/rt/rust_test_helpers.c")) .file(build.src.join("src/rt/rust_test_helpers.c"))
.compile("librust_test_helpers.a"); .compile("librust_test_helpers.a");
@ -366,7 +367,7 @@ impl Step for Openssl {
if !ok { if !ok {
panic!("failed to download openssl source") 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"); let mut cmd = Command::new("shasum");
cmd.arg("-a").arg("256"); cmd.arg("-a").arg("256");
cmd cmd
@ -386,9 +387,10 @@ impl Step for Openssl {
let dst = build.openssl_install_dir(target).unwrap(); let dst = build.openssl_install_dir(target).unwrap();
drop(fs::remove_dir_all(&obj)); drop(fs::remove_dir_all(&obj));
drop(fs::remove_dir_all(&dst)); 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(format!("--prefix={}", dst.display()));
configure.arg("no-dso"); configure.arg("no-dso");
configure.arg("no-ssl2"); configure.arg("no-ssl2");
@ -397,6 +399,7 @@ impl Step for Openssl {
let os = match &*target { let os = match &*target {
"aarch64-linux-android" => "linux-aarch64", "aarch64-linux-android" => "linux-aarch64",
"aarch64-unknown-linux-gnu" => "linux-aarch64", "aarch64-unknown-linux-gnu" => "linux-aarch64",
"aarch64-unknown-linux-musl" => "linux-aarch64",
"arm-linux-androideabi" => "android", "arm-linux-androideabi" => "android",
"arm-unknown-linux-gnueabi" => "linux-armv4", "arm-unknown-linux-gnueabi" => "linux-armv4",
"arm-unknown-linux-gnueabihf" => "linux-armv4", "arm-unknown-linux-gnueabihf" => "linux-armv4",
@ -407,6 +410,7 @@ impl Step for Openssl {
"i686-unknown-freebsd" => "BSD-x86-elf", "i686-unknown-freebsd" => "BSD-x86-elf",
"i686-unknown-linux-gnu" => "linux-elf", "i686-unknown-linux-gnu" => "linux-elf",
"i686-unknown-linux-musl" => "linux-elf", "i686-unknown-linux-musl" => "linux-elf",
"i686-unknown-netbsd" => "BSD-x86-elf",
"mips-unknown-linux-gnu" => "linux-mips32", "mips-unknown-linux-gnu" => "linux-mips32",
"mips64-unknown-linux-gnuabi64" => "linux64-mips64", "mips64-unknown-linux-gnuabi64" => "linux64-mips64",
"mips64el-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", "powerpc64-unknown-linux-gnu" => "linux-ppc64",
"powerpc64le-unknown-linux-gnu" => "linux-ppc64le", "powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
"s390x-unknown-linux-gnu" => "linux64-s390x", "s390x-unknown-linux-gnu" => "linux64-s390x",
"sparc64-unknown-netbsd" => "BSD-sparc64",
"x86_64-apple-darwin" => "darwin64-x86_64-cc", "x86_64-apple-darwin" => "darwin64-x86_64-cc",
"x86_64-linux-android" => "linux-x86_64", "x86_64-linux-android" => "linux-x86_64",
"x86_64-unknown-freebsd" => "BSD-x86_64", "x86_64-unknown-freebsd" => "BSD-x86_64",
@ -434,6 +439,15 @@ impl Step for Openssl {
configure.arg("-mandroid"); configure.arg("-mandroid");
configure.arg("-fomit-frame-pointer"); 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 // Make PIE binaries
// Non-PIE linker support was removed in Lollipop // Non-PIE linker support was removed in Lollipop
// https://source.android.com/security/enhancements/enhancements50 // https://source.android.com/security/enhancements/enhancements50

View File

@ -221,8 +221,9 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
let run = |cmd: &mut Command| { let run = |cmd: &mut Command| {
cmd.output().map(|output| { cmd.output().map(|output| {
String::from_utf8_lossy(&output.stdout) String::from_utf8_lossy(&output.stdout)
.lines().next().unwrap() .lines().next().unwrap_or_else(|| {
.to_string() panic!("{:?} failed {:?}", cmd, output)
}).to_string()
}) })
}; };
build.lldb_version = run(Command::new("lldb").arg("--version")).ok(); build.lldb_version = run(Command::new("lldb").arg("--version")).ok();

View File

@ -21,6 +21,8 @@ use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
use native; use native;
use channel::GitInfo; use channel::GitInfo;
use cache::Interned; use cache::Interned;
use toolstate::ToolState;
use build_helper::BuildExpectation;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct CleanTools { pub struct CleanTools {
@ -62,7 +64,9 @@ struct ToolBuild {
compiler: Compiler, compiler: Compiler,
target: Interned<String>, target: Interned<String>,
tool: &'static str, tool: &'static str,
path: &'static str,
mode: Mode, mode: Mode,
expectation: BuildExpectation,
} }
impl Step for ToolBuild { impl Step for ToolBuild {
@ -81,6 +85,8 @@ impl Step for ToolBuild {
let compiler = self.compiler; let compiler = self.compiler;
let target = self.target; let target = self.target;
let tool = self.tool; let tool = self.tool;
let path = self.path;
let expectation = self.expectation;
match self.mode { match self.mode {
Mode::Libstd => builder.ensure(compile::Std { compiler, target }), 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)); let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
println!("Building stage{} tool {} ({})", compiler.stage, tool, target); println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
let mut cargo = prepare_tool_cargo(builder, compiler, target, tool); let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
build.run(&mut cargo); build.run_expecting(&mut cargo, expectation);
build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host)) build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host))
} }
} }
fn prepare_tool_cargo( pub fn prepare_tool_cargo(
builder: &Builder, builder: &Builder,
compiler: Compiler, compiler: Compiler,
target: Interned<String>, target: Interned<String>,
tool: &'static str, command: &'static str,
path: &'static str,
) -> Command { ) -> Command {
let build = builder.build; let build = builder.build;
let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build"); let mut cargo = builder.cargo(compiler, Mode::Tool, target, command);
let dir = build.src.join("src/tools").join(tool); let dir = build.src.join(path);
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml")); cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
// We don't want to build tools dynamically as they'll be running across // 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"); 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_RELEASE_CHANNEL", &build.config.channel);
cargo.env("CFG_VERSION", build.rust_version());
let info = GitInfo::new(&build.config, &dir); let info = GitInfo::new(&build.config, &dir);
if let Some(sha) = info.sha() { if let Some(sha) = info.sha() {
@ -145,15 +157,27 @@ macro_rules! tool {
impl<'a> Builder<'a> { impl<'a> Builder<'a> {
pub fn tool_exe(&self, tool: Tool) -> PathBuf { pub fn tool_exe(&self, tool: Tool) -> PathBuf {
let stage = self.tool_default_stage(tool);
match tool { match tool {
$(Tool::$name => $(Tool::$name =>
self.ensure($name { self.ensure($name {
compiler: self.compiler(0, self.build.build), compiler: self.compiler(stage, self.build.build),
target: 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, target: self.target,
tool: $tool_name, tool: $tool_name,
mode: $mode, mode: $mode,
path: $path,
expectation: BuildExpectation::None,
}) })
} }
} }
@ -200,7 +226,7 @@ tool!(
Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest; Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest;
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd; BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd;
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", 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)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
@ -229,6 +255,8 @@ impl Step for RemoteTestServer {
target: self.target, target: self.target,
tool: "remote-test-server", tool: "remote-test-server",
mode: Mode::Libstd, 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)); let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host); 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); build.run(&mut cargo);
// Cargo adds a number of paths to the dylib search path on windows, which results in // 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" // 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, target: self.target,
tool: "cargo", tool: "cargo",
mode: Mode::Librustc, 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, target: self.target,
tool: "rls", tool: "rls",
mode: Mode::Librustc, 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`. /// `host`.
pub fn tool_cmd(&self, tool: Tool) -> Command { pub fn tool_cmd(&self, tool: Tool) -> Command {
let mut cmd = Command::new(self.tool_exe(tool)); 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); self.prepare_tool_cmd(compiler, &mut cmd);
cmd cmd
} }

View 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,
}

View File

@ -34,8 +34,12 @@ pub fn staticlib(name: &str, target: &str) -> String {
/// Copies a file from `src` to `dst` /// Copies a file from `src` to `dst`
pub fn copy(src: &Path, dst: &Path) { pub fn copy(src: &Path, dst: &Path) {
let _ = fs::remove_file(&dst); let _ = fs::remove_file(&dst);
let res = fs::copy(src, dst); // Attempt to "easy copy" by creating a hard link (symlinks don't work on
if let Err(e) = res { // 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(), panic!("failed to copy `{}` to `{}`: {}", src.display(),
dst.display(), e) dst.display(), e)
} }
@ -44,7 +48,6 @@ pub fn copy(src: &Path, dst: &Path) {
let atime = FileTime::from_last_access_time(&metadata); let atime = FileTime::from_last_access_time(&metadata);
let mtime = FileTime::from_last_modification_time(&metadata); let mtime = FileTime::from_last_modification_time(&metadata);
t!(filetime::set_file_times(dst, atime, mtime)); t!(filetime::set_file_times(dst, atime, mtime));
} }
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist /// 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()); ptr::null_mut());
let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; 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; as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
let buf = &mut (*db).ReparseTarget as *mut _; let buf = &mut (*db).ReparseTarget as *mut _;
let mut i = 0; let mut i = 0;

View File

@ -35,55 +35,97 @@ macro_rules! t {
}) })
} }
pub fn run(cmd: &mut Command) { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
println!("running: {:?}", cmd); pub enum BuildExpectation {
run_silent(cmd); Succeeding,
Failing,
None,
} }
pub fn run_silent(cmd: &mut Command) { pub fn run(cmd: &mut Command, expect: BuildExpectation) {
if !try_run_silent(cmd) { 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); 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() { let status = match cmd.status() {
Ok(status) => status, Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
cmd, e)), cmd, e)),
}; };
if !status.success() { process_status(
println!("\n\ncommand did not execute successfully: {:?}\n\ cmd,
status.success(),
expect,
|| println!("\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n", expected success, got: {}\n\n",
cmd, cmd,
status); status))
}
status.success()
} }
pub fn run_suppressed(cmd: &mut Command) { fn process_status<F: FnOnce()>(
if !try_run_suppressed(cmd) { 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); 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() { let output = match cmd.output() {
Ok(status) => status, Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
cmd, e)), cmd, e)),
}; };
if !output.status.success() { process_status(
println!("\n\ncommand did not execute successfully: {:?}\n\ cmd,
output.status.success(),
expect,
|| println!("\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n\ expected success, got: {}\n\n\
stdout ----\n{}\n\ stdout ----\n{}\n\
stderr ----\n{}\n\n", stderr ----\n{}\n\n",
cmd, cmd,
output.status, output.status,
String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)); String::from_utf8_lossy(&output.stderr)))
}
output.status.success()
} }
pub fn gnu_target(target: &str) -> String { pub fn gnu_target(target: &str) -> String {

View File

@ -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 - Each directory, excluding `scripts` and `disabled`, corresponds to a docker image
- `scripts` contains files shared by docker images - `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 ## Cross toolchains

View File

@ -5,21 +5,27 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ 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 && \ RUN dpkg --add-architecture i386 && \
apt-get update && \ apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libgl1-mesa-glx \ libgl1-mesa-glx \
libpulse0 \ libpulse0 \
libstdc++6:i386 \ libstdc++6:i386 \
openjdk-9-jre-headless \ openjdk-8-jre-headless \
tzdata tzdata
COPY scripts/android-sdk.sh /scripts/ COPY scripts/android-sdk.sh /scripts/
RUN . /scripts/android-sdk.sh && \ 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/tools
ENV PATH=$PATH:/android/sdk/platform-tools ENV PATH=$PATH:/android/sdk/platform-tools
@ -27,7 +33,7 @@ ENV TARGETS=arm-linux-androideabi
ENV RUST_CONFIGURE_ARGS \ ENV RUST_CONFIGURE_ARGS \
--target=$TARGETS \ --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 ENV SCRIPT python2.7 ../x.py test --target $TARGETS

View File

@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
zlib1g-dev \ zlib1g-dev \
g++-arm-linux-gnueabi \ g++-arm-linux-gnueabi \
g++-arm-linux-gnueabihf \ g++-arm-linux-gnueabihf \
g++-aarch64-linux-gnu \
gcc-sparc64-linux-gnu \ gcc-sparc64-linux-gnu \
libc6-dev-sparc64-cross \ libc6-dev-sparc64-cross \
bzip2 \ 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-musleabi
ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf
ENV TARGETS=$TARGETS,armv7-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,sparc64-unknown-linux-gnu
ENV TARGETS=$TARGETS,x86_64-unknown-redox ENV TARGETS=$TARGETS,x86_64-unknown-redox
@ -62,7 +64,8 @@ ENV RUST_CONFIGURE_ARGS \
--target=$TARGETS \ --target=$TARGETS \
--musl-root-arm=/usr/local/arm-linux-musleabi \ --musl-root-arm=/usr/local/arm-linux-musleabi \
--musl-root-armhf=/usr/local/arm-linux-musleabihf \ --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 ENV SCRIPT python2.7 ../x.py dist --target $TARGETS
# sccache # sccache

View File

@ -65,11 +65,24 @@ CFLAGS="-march=armv7-a" \
hide_output make -j$(nproc) hide_output make -j$(nproc)
hide_output make install hide_output make install
cd .. 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* rm -rf musl-$MUSL*
ln -nsf ../arm-linux-musleabi/bin/musl-gcc /usr/local/bin/arm-linux-musleabi-gcc 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 ../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 ../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/llvm/archive/release_39.tar.gz | tar xzf -
curl -L https://github.com/llvm-mirror/libunwind/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 .. cd ..
rm -rf libunwind-build 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 libunwind-release_39
rm -rf llvm-release_39 rm -rf llvm-release_39

View 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

View 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

View 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

View File

@ -31,7 +31,7 @@ WORKDIR /build
# The `config` config file was a previously generated config file for # The `config` config file was a previously generated config file for
# the kernel. This file was generated by running `make defconfig` # the kernel. This file was generated by running `make defconfig`
# followed by `make menuconfig` and then enabling the IPv6 protocol page. # 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 | \ RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.42.tar.xz | \
tar xJf - && \ tar xJf - && \
cd /build/linux-4.4.42 && \ cd /build/linux-4.4.42 && \

View File

@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ 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 ENV PATH=$PATH:/android/ndk/arm64-21/bin

View File

@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ RUN . /scripts/android-ndk.sh && \
download_ndk android-ndk-r13b-linux-x86_64.zip && \ download_ndk android-ndk-r15c-linux-x86_64.zip && \
make_standalone_toolchain arm 9 && \ make_standalone_toolchain arm 14 && \
make_standalone_toolchain arm 21 && \ make_standalone_toolchain arm 21 && \
remove_ndk remove_ndk
RUN chmod 777 /android/ndk && \ RUN chmod 777 /android/ndk && \
ln -s /android/ndk/arm-21 /android/ndk/arm 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 ENV HOSTS=armv7-linux-androideabi
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
--enable-extended \ --enable-extended \
--enable-cargo-openssl-static --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 # 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 # 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 # build to finish we use --warn-unresolved-symbols. Note that the missing
# symbols does not affect std, only the compiler (llvm) and cargo (openssl). # symbols does not affect std, only the compiler (llvm) and cargo (openssl).
ENV SCRIPT \ ENV SCRIPT \
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
rm /android/ndk/arm && \ 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) python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
COPY scripts/sccache.sh /scripts/ COPY scripts/sccache.sh /scripts/

View File

@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ RUN . /scripts/android-ndk.sh && \
download_ndk android-ndk-r13b-linux-x86_64.zip && \ download_ndk android-ndk-r15c-linux-x86_64.zip && \
make_standalone_toolchain x86 9 && \ make_standalone_toolchain x86 14 && \
make_standalone_toolchain x86 21 && \ make_standalone_toolchain x86 21 && \
remove_ndk remove_ndk
RUN chmod 777 /android/ndk && \ RUN chmod 777 /android/ndk && \
ln -s /android/ndk/x86-21 /android/ndk/x86 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 ENV HOSTS=i686-linux-android
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
--enable-extended \ --enable-extended \
--enable-cargo-openssl-static --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 # 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 # 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 # build to finish we use --warn-unresolved-symbols. Note that the missing
# symbols does not affect std, only the compiler (llvm) and cargo (openssl). # symbols does not affect std, only the compiler (llvm) and cargo (openssl).
ENV SCRIPT \ ENV SCRIPT \
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
rm /android/ndk/x86 && \ 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) python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
COPY scripts/sccache.sh /scripts/ COPY scripts/sccache.sh /scripts/

View File

@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ 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 ENV PATH=$PATH:/android/ndk/x86_64-21/bin

View 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

View 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

View 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

View 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

View File

@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# emscripten # emscripten
COPY scripts/emscripten-wasm.sh /scripts/ 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 RUN bash /scripts/emscripten-wasm.sh
# cache # cache

View File

@ -6,9 +6,9 @@ RUN sh /scripts/android-base-apt-get.sh
# ndk # ndk
COPY scripts/android-ndk.sh /scripts/ COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \ RUN . /scripts/android-ndk.sh && \
download_ndk android-ndk-r13b-linux-x86_64.zip && \ download_ndk android-ndk-r15c-linux-x86_64.zip && \
make_standalone_toolchain arm 9 && \ make_standalone_toolchain arm 14 && \
make_standalone_toolchain x86 9 && \ make_standalone_toolchain x86 14 && \
make_standalone_toolchain arm64 21 && \ make_standalone_toolchain arm64 21 && \
make_standalone_toolchain x86_64 21 && \ make_standalone_toolchain x86_64 21 && \
remove_ndk remove_ndk
@ -23,9 +23,9 @@ ENV TARGETS=$TARGETS,x86_64-linux-android
ENV RUST_CONFIGURE_ARGS \ ENV RUST_CONFIGURE_ARGS \
--target=$TARGETS \ --target=$TARGETS \
--enable-extended \ --enable-extended \
--arm-linux-androideabi-ndk=/android/ndk/arm-9 \ --arm-linux-androideabi-ndk=/android/ndk/arm-14 \
--armv7-linux-androideabi-ndk=/android/ndk/arm-9 \ --armv7-linux-androideabi-ndk=/android/ndk/arm-14 \
--i686-linux-android-ndk=/android/ndk/x86-9 \ --i686-linux-android-ndk=/android/ndk/x86-14 \
--aarch64-linux-android-ndk=/android/ndk/arm64-21 \ --aarch64-linux-android-ndk=/android/ndk/arm64-21 \
--x86_64-linux-android-ndk=/android/ndk/x86_64-21 --x86_64-linux-android-ndk=/android/ndk/x86_64-21

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -23,7 +23,7 @@ SYSROOT=/usr/local/$TARGET/sysroot
mkdir -p $SYSROOT mkdir -p $SYSROOT
pushd $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 glibc_v=2.17-157.el7
kernel_v=3.10.0-514.el7 kernel_v=3.10.0-514.el7
for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do

View File

@ -36,12 +36,14 @@ elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
echo Cannot run disabled images on travis! echo Cannot run disabled images on travis!
exit 1 exit 1
fi 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 \ build \
--rm \ --rm \
-t rust-ci \ -t rust-ci \
-f "$docker_dir/disabled/$image/Dockerfile" \ -f "$image/Dockerfile" \
"$docker_dir" -
else else
echo Invalid image: $image echo Invalid image: $image
exit 1 exit 1

View File

@ -10,40 +10,40 @@
set -ex set -ex
URL=https://dl.google.com/android/repository export ANDROID_HOME=/android/sdk
PATH=$PATH:"${ANDROID_HOME}/tools/bin"
download_sdk() { download_sdk() {
mkdir -p /android/sdk mkdir -p /android
cd /android/sdk curl -fo sdk.zip "https://dl.google.com/android/repository/sdk-tools-linux-$1.zip"
curl -fO $URL/$1 unzip -q sdk.zip -d "$ANDROID_HOME"
unzip -q $1 rm -f sdk.zip
rm -rf $1
} }
download_sysimage() { download_sysimage() {
# See https://developer.android.com/studio/tools/help/android.html
abi=$1 abi=$1
api=$2 api=$2
filter="platform-tools,android-$api" # See https://developer.android.com/studio/command-line/sdkmanager.html for
filter="$filter,sys-img-$abi-android-$api" # usage of `sdkmanager`.
#
# Keep printing yes to accept the licenses # The output from sdkmanager is so noisy that it will occupy all of the 4 MB
while true; do echo yes; sleep 10; done | \ # log extremely quickly. Thus we must silence all output.
/android/sdk/tools/android update sdk -a --no-ui \ yes | sdkmanager --licenses > /dev/null
--filter "$filter" sdkmanager platform-tools emulator \
"platforms;android-$api" \
"system-images;android-$api;default;$abi" > /dev/null
} }
create_avd() { create_avd() {
# See https://developer.android.com/studio/tools/help/android.html
abi=$1 abi=$1
api=$2 api=$2
echo no | \ # See https://developer.android.com/studio/command-line/avdmanager.html for
/android/sdk/tools/android create avd \ # usage of `avdmanager`.
--name $abi-$api \ echo no | avdmanager create avd \
--target android-$api \ -n "$abi-$api" \
--abi $abi -k "system-images;android-$api;default;$abi"
} }
download_and_create_avd() { download_and_create_avd() {
@ -51,3 +51,15 @@ download_and_create_avd() {
download_sysimage $2 $3 download_sysimage $2 $3
create_avd $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)

View File

@ -17,5 +17,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY scripts/sccache.sh /scripts/ COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh RUN sh /scripts/sccache.sh
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-test-miri
ENV RUST_CHECK_TARGET check-aux ENV RUST_CHECK_TARGET check-aux

View File

@ -18,6 +18,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY scripts/sccache.sh /scripts/ COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh RUN sh /scripts/sccache.sh
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false
ENV SCRIPT python2.7 ../x.py test distcheck ENV SCRIPT python2.7 ../x.py test distcheck
ENV DIST_SRC 1 ENV DIST_SRC 1

View File

@ -52,7 +52,11 @@ if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
fi fi
else else
# 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" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
fi
# In general we always want to run tests with LLVM assertions enabled, but not # 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. # all platforms currently support that, so we have an option to disable.

View File

@ -1,4 +1,5 @@
sudo: false sudo: false
dist: trusty
language: rust language: rust
cache: cargo cache: cargo
rust: rust:

View File

@ -19,12 +19,14 @@ starting with the second edition.
## Requirements ## 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 [mdBook]: https://github.com/azerupi/mdBook
[rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml
```bash ```bash
$ cargo install mdbook $ cargo install mdbook --vers [version-num]
``` ```
## Building ## Building

View File

@ -9,13 +9,16 @@ aggregator
AGraph AGraph
aliasability aliasability
alignof alignof
allocator
Amir Amir
anotherusername anotherusername
APIs APIs
app's app's
aren aren
args args
associativity
async async
atomics
AveragedCollection AveragedCollection
backend backend
backtrace backtrace
@ -50,6 +53,8 @@ ChangeColorMessage
charset charset
chXX chXX
chYY chYY
coercions
combinator
ConcreteType ConcreteType
config config
Config Config
@ -57,6 +62,7 @@ const
constant's constant's
copyeditor copyeditor
couldn couldn
CPUs
cratesio cratesio
CRLF CRLF
cryptographically cryptographically
@ -64,11 +70,15 @@ CStr
CString CString
ctrl ctrl
Ctrl Ctrl
customizable
CustomSmartPointer CustomSmartPointer
CustomSmartPointers
deallocate
deallocated deallocated
deallocating deallocating
deallocation deallocation
debuginfo debuginfo
decrementing
deps deps
deref deref
Deref Deref
@ -84,12 +94,14 @@ destructured
destructures destructures
destructuring destructuring
Destructuring Destructuring
deterministically
didn didn
Dobrý Dobrý
doccargo doccargo
doccratesio doccratesio
DOCTYPE DOCTYPE
doesn doesn
disambiguating
DraftPost DraftPost
DSTs DSTs
ebooks ebooks
@ -104,6 +116,7 @@ enums
enum's enum's
Enums Enums
eprintln eprintln
Erlang
ErrorKind ErrorKind
Executables Executables
extern extern
@ -124,6 +137,7 @@ FnOnce
formatter formatter
FromIterator FromIterator
frontend frontend
getter
GGraph GGraph
GitHub GitHub
gitignore gitignore
@ -149,7 +163,10 @@ html
Iceburgh Iceburgh
IEEE IEEE
impl impl
implementor
implementors
ImportantExcerpt ImportantExcerpt
incrementing
indices indices
init init
inline inline
@ -157,6 +174,7 @@ instantiation
internet internet
IntoIterator IntoIterator
InvalidDigit InvalidDigit
invariants
ioerror ioerror
iokind iokind
ioresult ioresult
@ -180,6 +198,7 @@ librarys
libreoffice libreoffice
libstd libstd
lifecycle lifecycle
LimitTracker
lobally lobally
locators locators
login login
@ -197,7 +216,9 @@ Mibbit
minigrep minigrep
mixup mixup
mkdir mkdir
MockMessenger
modifiability modifiability
modularity
monomorphization monomorphization
Monomorphization Monomorphization
monomorphized monomorphized
@ -206,10 +227,12 @@ Mozilla
mpsc mpsc
multithreaded multithreaded
mutex mutex
mutex's
Mutex Mutex
mutexes mutexes
Mutexes Mutexes
MutexGuard MutexGuard
MyBox
namespace namespace
namespaced namespaced
namespaces namespaces
@ -230,6 +253,7 @@ OCaml
offsetof offsetof
online online
OpenGL OpenGL
optimizations
OptionalFloatingPointNumber OptionalFloatingPointNumber
OptionalNumber OptionalNumber
OsStr OsStr
@ -245,6 +269,7 @@ PartialOrd
PendingReview PendingReview
PendingReviewPost PendingReviewPost
PlaceholderType PlaceholderType
polymorphism
PoolCreationError PoolCreationError
portia portia
powershell powershell
@ -277,12 +302,15 @@ RefMut
refutability refutability
reimplement reimplement
repr repr
representable
request's request's
resizes resizes
resizing resizing
retweet retweet
rewordings
rint rint
ripgrep ripgrep
runnable
runtime runtime
runtimes runtimes
Rustacean Rustacean
@ -301,6 +329,7 @@ shouldn
Simula Simula
situps situps
sizeof sizeof
Smalltalk
someproject someproject
someusername someusername
SPDX SPDX
@ -322,6 +351,7 @@ Struct
structs structs
struct's struct's
Structs Structs
subclasses
subcommand subcommand
subcommands subcommands
subdirectories subdirectories
@ -364,9 +394,11 @@ unary
Unary Unary
uncomment uncomment
Uncomment Uncomment
unevaluated
Uninstalling Uninstalling
uninstall uninstall
unix unix
unpopulated
unoptimized unoptimized
UnsafeCell UnsafeCell
unsafety unsafety

View File

@ -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 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: Chapter 1, and make a new project using Cargo, like so:
```bash ```
$ cargo new guessing_game --bin $ cargo new guessing_game --bin
$ cd guessing_game $ cd guessing_game
``` ```
@ -34,7 +34,7 @@ Look at the generated *Cargo.toml* file:
Filename: Cargo.toml Filename: Cargo.toml
```toml ```
[package] [package]
name = "guessing_game" name = "guessing_game"
version = "0.1.0" version = "0.1.0"
@ -51,7 +51,7 @@ you. Check out the *src/main.rs* file:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
} }
@ -60,9 +60,10 @@ fn main() {
Now lets compile this “Hello, world!” program and run it in the same step Now lets compile this “Hello, world!” program and run it in the same step
using the `cargo run` command: using the `cargo run` command:
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Hello, world! 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 Filename: src/main.rs
```rust,ignore ```
use std::io; use std::io;
fn main() { fn main() {
@ -98,51 +99,51 @@ fn main() {
} }
``` ```
<caption> Listing 2-1: Code to get a guess from the user and print
Listing 2-1: Code to get a guess from the user and print it out it out
</caption>
This code contains a lot of information, so lets go over it bit by bit. To This code contains a lot of information, so lets go over it bit by bit. To
obtain user input and then print the result as output, we need to import the obtain user input and then print the result as output, we need to bring the
`io` (input/output) library from the standard library (which is known as `std`): `io` (input/output) library into scope. The `io` library comes from the
standard library (which is known as `std`):
```rust,ignore ```
use std::io; use std::io;
``` ```
By default, Rust imports only a few types into every program in the By default, Rust brings only a few types into the scope of every program in
*prelude*. If a type you want to use isnt in the the *prelude*. If a type you want to use isnt in the
prelude, you have to import that type into your program explicitly with a `use` 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 statement. Using the `std::io` library provides you with a number of useful
`io`-related features, including the functionality to accept user input. `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 As you saw in Chapter 1, the `main` function is the entry point into the
program: program:
```rust,ignore ```
fn main() { fn main() {
``` ```
The `fn` syntax declares a new function, the `()` indicate there are no 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 As you also learned in Chapter 1, `println!` is a macro that prints a string to
the screen: the screen:
```rust,ignore ```
println!("Guess the number!"); println!("Guess the number!");
println!("Please input your guess."); println!("Please input your guess.");
``` ```
This code is just printing a prompt stating what the game is and requesting This code is printing a prompt stating what the game is and requesting input
input from the user. from the user.
### Storing Values with Variables ### Storing Values with Variables
Next, well create a place to store the user input, like this: Next, well create a place to store the user input, like this:
```rust,ignore ```
let mut guess = String::new(); let mut guess = String::new();
``` ```
@ -150,7 +151,7 @@ Now the program is getting interesting! Theres a lot going on in this little
line. Notice that this is a `let` statement, which is used to create line. Notice that this is a `let` statement, which is used to create
*variables*. Heres another example: *variables*. Heres another example:
```rust,ignore ```
let foo = bar; 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 `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: how to use `mut` before the variable name to make a variable mutable:
```rust ```
let foo = 5; // immutable let foo = 5; // immutable
let mut bar = 5; // mutable 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 well call an library with `use std::io;` on the first line of the program. Now well call an
associated function, `stdin`, on `io`: associated function, `stdin`, on `io`:
```rust,ignore ```
io::stdin().read_line(&mut guess) io::stdin().read_line(&mut guess)
.expect("Failed to read line"); .expect("Failed to read line");
``` ```
@ -222,7 +223,7 @@ Were not quite done with this line of code. Although its a single line of
text, its only the first part of the single logical line of code. The second text, its only the first part of the single logical line of code. The second
part is this method: part is this method:
```rust,ignore ```
.expect("Failed to read line"); .expect("Failed to read line");
``` ```
@ -230,7 +231,7 @@ When you call a method with the `.foo()` syntax, its often wise to introduce
newline and other whitespace to help break up long lines. We could have newline and other whitespace to help break up long lines. We could have
written this code as: written this code as:
```rust,ignore ```
io::stdin().read_line(&mut guess).expect("Failed to read line"); 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. 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 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 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. entered into standard input.
If we dont call `expect`, the program will compile, but well get a warning: If we dont call `expect`, the program will compile, but well get a warning:
```bash ```
$ cargo build $ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used, warning: unused `std::result::Result` which must be used
#[warn(unused_must_use)] on by default --> src/main.rs:10:5
src/main.rs:10 io::stdin().read_line(&mut guess); |
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | io::stdin().read_line(&mut guess);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_must_use)] on by default
``` ```
Rust warns that we havent used the `Result` value returned from `read_line`, Rust warns that we havent used the `Result` value returned from `read_line`,
indicating that the program hasnt handled a possible error. The right way to indicating that the program hasnt handled a possible error. The right way to
suppress the warning is to actually write error handling, but since we just suppress the warning is to actually write error handling, but since we want to
want to crash this program when a problem occurs, we can use `expect`. Youll crash this program when a problem occurs, we can use `expect`. Youll learn
learn about recovering from errors in Chapter 9. about recovering from errors in Chapter 9.
### Printing Values with `println!` Placeholders ### Printing Values with `println!` Placeholders
Aside from the closing curly brace, theres only one more line to discuss in Aside from the closing curly brackets, theres only one more line to discuss in
the code added so far, which is the following: the code added so far, which is the following:
```rust,ignore ```
println!("You guessed: {}", guess); 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 string, the second set holds the second value, and so on. Printing out multiple
values in one call to `println!` would look like this: values in one call to `println!` would look like this:
```rust ```
let x = 5; let x = 5;
let y = 10; let y = 10;
@ -310,11 +314,13 @@ This code would print out `x = 5 and y = 10`.
### Testing the First Part ### Testing the First Part
Lets test the first part of the guessing game. You can run it using `cargo run`: Lets test the first part of the guessing game. You can run it using
`cargo run`:
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Guess the number! Guess the number!
Please input your guess. Please input your guess.
@ -348,7 +354,7 @@ you:
Filename: Cargo.toml Filename: Cargo.toml
```toml ```
[dependencies] [dependencies]
rand = "0.3.14" rand = "0.3.14"
@ -367,7 +373,7 @@ version 0.3.14.”
Now, without changing any of the code, lets build the project, as shown in Now, without changing any of the code, lets build the project, as shown in
Listing 2-2: Listing 2-2:
```bash ```
$ cargo build $ cargo build
Updating registry `https://github.com/rust-lang/crates.io-index` Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.3.14 Downloading rand v0.3.14
@ -375,12 +381,11 @@ $ cargo build
Compiling libc v0.2.14 Compiling libc v0.2.14
Compiling rand v0.3.14 Compiling rand v0.3.14
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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
Listing 2-2: The output from running `cargo build` after adding the rand crate adding the rand crate as a dependency
as a dependency
</caption>
You may see different version numbers (but they will all be compatible with 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. the code, thanks to SemVer!), and the lines may be in a different order.
@ -405,7 +410,7 @@ doesnt 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, up the *src/main.rs* file, make a trivial change, then save it and build again,
youll only see one line of output: youll only see one line of output:
```bash ```
$ cargo build $ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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, 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`: `0.3.15` and `0.4.0`, you would see the following if you ran `cargo update`:
```bash ```
$ cargo update $ cargo update
Updating registry `https://github.com/rust-lang/crates.io-index` Updating registry `https://github.com/rust-lang/crates.io-index`
Updating rand v0.3.14 -> v0.3.15 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` If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x`
series, youd have to update the *Cargo.toml* file to look like this instead: series, youd have to update the *Cargo.toml* file to look like this instead:
```toml ```
[dependencies] [dependencies]
rand = "0.4.0" rand = "0.4.0"
@ -471,7 +476,7 @@ available and reevaluate your `rand` requirements according to the new version
you specified. you specified.
Theres a lot more to say about Cargo and its Theres 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, thats all you need to know. Cargo makes it very easy to reuse libraries, now, thats 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 so Rustaceans are able to write smaller projects that are assembled from a
number of packages. number of packages.
@ -483,7 +488,7 @@ in Listing 2-3:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; use std::io;
@ -507,9 +512,8 @@ fn main() {
} }
``` ```
<caption> Listing 2-3: Code changes needed in order to generate a
Listing 2-3: Code changes needed in order to generate a random number random number
</caption>
Were adding a `extern crate rand;` line to the top that lets Rust know well be Were adding a `extern crate rand;` line to the top that lets Rust know well be
using that external dependency. This also does the equivalent of calling `use 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. Its inclusiv
on the lower bound but exclusive on the upper bound, so we need to specify `1` 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. 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 isnt something that youll just *know*. Instructions for using a crate crate isnt something that youll just *know*. Instructions for using a crate
are in each crates documentation. Another neat feature of Cargo is that you are in each crates documentation. Another neat feature of Cargo is that you
can run the `cargo doc --open` command that will build documentation provided 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: Try running the program a few times:
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Guess the number! Guess the number!
The secret number is: 7 The secret number is: 7
@ -573,7 +578,7 @@ step is shown in Listing 2-4:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; use std::io;
@ -604,9 +609,8 @@ fn main() {
} }
``` ```
<caption> Listing 2-4: Handling the possible return values of
Listing 2-4: Handling the possible return values of comparing two numbers comparing two numbers
</caption>
The first new bit here is another `use`, bringing a type called The first new bit here is another `use`, bringing a type called
`std::cmp::Ordering` into scope from the standard library. `Ordering` is `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: Then we add five new lines at the bottom that use the `Ordering` type:
```rust,ignore ```
match guess.cmp(&secret_number) { match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"), Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"), 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 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 its compared. It takes a reference to whatever you want to compare with: here its
comparing the `guess` to the `secret_number`. `cmp` returns a variant of the 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 `match` expression to decide what to do next based on
which variant of `Ordering` was returned from the call to `cmp` with the values which variant of `Ordering` was returned from the call to `cmp` with the values
in `guess` and `secret_number`. in `guess` and `secret_number`.
@ -638,7 +642,7 @@ expression fits that arms pattern. Rust takes the value given to `match` and
looks through each arms pattern in turn. The `match` construct and patterns looks through each arms pattern in turn. The `match` construct and patterns
are powerful features in Rust that let you express a variety of situations your 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 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.
Lets walk through an example of what would happen with the `match` expression Lets 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 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` return `Ordering::Greater`, because 50 is greater than 38. `Ordering::Greater`
is the value that the `match` expression gets. It looks at the first arms is the value that the `match` expression gets. It looks at the first arms
pattern, `Ordering::Less`, but the value `Ordering::Greater` does not match 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 arms pattern, `Ordering::Greater`, *does* match The next arms pattern, `Ordering::Greater`, *does* match
`Ordering::Greater`! The associated code in that arm will execute and print `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 `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 wont compile yet. Lets try it: However, the code in Listing 2-4 wont compile yet. Lets try it:
```bash ```
$ cargo build $ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
error[E0308]: mismatched types error[E0308]: mismatched types
@ -687,7 +691,7 @@ that by adding the following two lines to the `main` function body:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; use std::io;
@ -723,7 +727,7 @@ fn main() {
The two new lines are: The two new lines are:
```rust,ignore ```
let guess: u32 = guess.trim().parse() let guess: u32 = guess.trim().parse()
.expect("Please type a number!"); .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 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 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 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 user must press the <span class="keystroke">enter</span> key to satisfy
Return, a newline character is added to the string. For example, if the user `read_line`. When the user presses <span class="keystroke">enter</span>, a
types 5 and presses return, `guess` looks like this: `5\n`. The `\n` represents newline character is added to the string. For example, if the user types <span
“newline,” the return key. The `trim` method eliminates `\n`, resulting in just class="keystroke">5</span> and presses <span class="keystroke"> enter</span>,
`5`. `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 The `parse` method on strings parses a string into some
kind of number. Because this method can parse a variety of number types, we 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 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 might fail, the `parse` method returns a `Result` type, much like the
`read_line` method does as discussed earlier in “Handling Potential Failure `read_line` method does as discussed earlier in “Handling Potential Failure
with the Result Type” on page XX. Well treat this `Result` the same way by with the Result Type”. Well treat this `Result` the same way by
using the `expect` method again. If `parse` returns an `Err` `Result` variant using the `expect` method again. If `parse` returns an `Err` `Result` variant
because it couldnt create a number from the string, the `expect` call will because it couldnt create a number from the string, the `expect` call will
crash the game and print the message we give it. If `parse` can successfully 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.
Lets run the program now! Lets run the program now!
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
Running `target/guessing_game` Running `target/guessing_game`
Guess the number! Guess the number!
The secret number is: 58 The secret number is: 58
@ -797,7 +803,7 @@ chances at guessing the number:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; 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 doesnt seem exactly what we told it to do: ask for another guess forever! It doesnt seem
like the user can quit! like the user can quit!
The user could always halt the program by using the keyboard shortcut `Ctrl-C`. The user could always halt the program by using the keyboard shortcut
But theres another way to escape this insatiable monster that we mentioned in <span class="keystroke">ctrl-C</span>. But theres another way to escape this
the `parse` discussion in “Comparing the Guesses” on page XX: if the user insatiable monster that we mentioned in the `parse` discussion in “Comparing the
enters a non-number answer, the program will crash. The user can take advantage Guess to the Secret Number”: if the user enters a non-number answer, the program
of that in order to quit, as shown here: will crash. The user can take advantage of that in order to quit, as shown here:
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Running `target/guessing_game` Running `target/guessing_game`
@ -880,7 +886,7 @@ Lets program the game to quit when the user wins by adding a `break`:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; use std::io;
@ -930,7 +936,7 @@ the user inputs a non-number, lets make the game ignore a non-number so the
user can continue guessing. We can do that by altering the line where `guess` is user can continue guessing. We can do that by altering the line where `guess` is
converted from a `String` to a `u32`: converted from a `String` to a `u32`:
```rust,ignore ```
let guess: u32 = match guess.trim().parse() { let guess: u32 = match guess.trim().parse() {
Ok(num) => num, Ok(num) => num,
Err(_) => continue, Err(_) => continue,
@ -962,7 +968,7 @@ might encounter!
Now everything in the program should work as expected. Lets try it by running Now everything in the program should work as expected. Lets try it by running
`cargo run`: `cargo run`:
```bash ```
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Running `target/guessing_game` Running `target/guessing_game`
@ -991,7 +997,7 @@ secret number. Listing 2-5 shows the final code:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
extern crate rand; extern crate rand;
use std::io; use std::io;
@ -1030,9 +1036,7 @@ fn main() {
} }
``` ```
<caption>
Listing 2-5: Complete code of the guessing game Listing 2-5: Complete code of the guessing game
</caption>
## Summary ## Summary

View File

@ -12,19 +12,15 @@ Specifically, youll learn about variables, basic types, functions, comments,
and control flow. These foundations will be in every Rust program, and learning and control flow. These foundations will be in every Rust program, and learning
them early will give you a strong core to start from. them early will give you a strong core to start from.
PROD: START BOX > ### Keywords
>
### 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
The Rust language has a set of *keywords* that have been reserved for use by > use these words as names of variables or functions. Most of the keywords have
the language only, much like other languages do. Keep in mind that you cannot > special meanings, and youll be using them to do various tasks in your Rust
use these words as names of variables or functions. Most of the keywords have > programs; a few have no current functionality associated with them but have
special meanings, and youll be using them to do various tasks in your Rust > been reserved for functionality that might be added to Rust in the future. You
programs; a few have no current functionality associated with them but have > can find a list of the keywords in Appendix A.
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
## Variables and Mutability ## Variables and Mutability
@ -43,7 +39,7 @@ code with the following:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let x = 5; let x = 5;
println!("The value of x is: {}", x); println!("The value of x is: {}", x);
@ -97,7 +93,7 @@ For example, change *src/main.rs* to the following:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let mut x = 5; let mut x = 5;
println!("The value of x is: {}", x); println!("The value of x is: {}", x);
@ -108,9 +104,10 @@ fn main() {
When we run this program, we get the following: When we run this program, we get the following:
```bash ```
$ cargo run $ cargo run
Compiling variables v0.1.0 (file:///projects/variables) Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/variables` Running `target/debug/variables`
The value of x is: 5 The value of x is: 5
The value of x is: 6 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 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 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 maximum number of points any player of a game is allowed to earn or the speed
of light. of light.
@ -172,8 +169,8 @@ hardcoded value needed to be updated in the future.
### Shadowing ### Shadowing
As we saw in the guessing game tutorial in Chapter 2, we can declare new As we saw in the guessing game tutorial in Chapter 2, we can declare a new
variables with the same name as a previous variables, and the new variable variable with the same name as a previous variable, and the new variable
*shadows* the previous variable. Rustaceans say that the first variable is *shadows* the previous variable. Rustaceans say that the first variable is
*shadowed* by the second, which means that the second variables value is what *shadowed* by the second, which means that the second variables value is what
well see when we use the variable. We can shadow a variable by using the same well see when we use the variable. We can shadow a variable by using the same
@ -181,7 +178,7 @@ variables name and repeating the use of the `let` keyword as follows:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let x = 5; 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`. 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: When you run this program, it will output the following:
```bash ```
$ cargo run $ cargo run
Compiling variables v0.1.0 (file:///projects/variables) Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/variables` Running `target/debug/variables`
The value of x is: 12 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 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: inputting space characters, but we really want to store that input as a number:
```rust ```
let spaces = " "; let spaces = " ";
let spaces = spaces.len(); 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 `spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
try to use `mut` for this, as shown here: try to use `mut` for this, as shown here:
```rust,ignore ```
let mut spaces = " "; let mut spaces = " ";
spaces = spaces.len(); spaces = spaces.len();
``` ```
@ -237,7 +235,7 @@ spaces = spaces.len();
well get a compile-time error because were not allowed to mutate a variables well get a compile-time error because were not allowed to mutate a variables
type: type:
```bash ```
error[E0308]: mismatched types error[E0308]: mismatched types
--> src/main.rs:3:14 --> src/main.rs:3:14
| |
@ -273,14 +271,15 @@ If we dont add the type annotation here, Rust will display the following
error, which means the compiler needs more information from us to know which error, which means the compiler needs more information from us to know which
possible type we want to use: 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 --> src/main.rs:2:9
| |
2 | let guess = "42".parse().expect("Not a number!"); 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
``` ```
Youll see different type annotations as we discuss the various data types. Youll see different type annotations as we discuss the various data types.
@ -295,16 +294,14 @@ work in Rust.
#### Integer Types #### Integer Types
An *integer* is a number without a fractional component. We used one integer 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 type earlier in this chapter, the `u32` type. This type declaration indicates
that the value its associated with should be a signed integer (hence the `i`, that the value its associated with should be an unsigned integer (signed
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1 integer types start with `i` instead of `u`) that takes up 32 bits of space.
shows the built-in integer types in Rust. Each variant in the Signed and Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
Unsigned columns (for example, *i32*) can be used to declare the type of an and Unsigned columns (for example, *i16*) can be used to declare the type of an
integer value. integer value.
<caption>
Table 3-1: Integer Types in Rust Table 3-1: Integer Types in Rust
</caption>
| Length | Signed | Unsigned | | Length | Signed | Unsigned |
|--------|--------|----------| |--------|--------|----------|
@ -325,11 +322,11 @@ Signed numbers are stored using twos complement representation (if youre
unsure what this is, you can search for it online; an explanation is outside unsure what this is, you can search for it online; an explanation is outside
the scope of this book). 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, Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
where `n` is the number of bits that variant uses. So an `i8` can store numbers 1</sup> - 1 inclusive, where `n` is the number of bits that variant uses. So an
from -2<sup>7</sup> to 2<sup>7</sup> - 1, which equals -128 to 127. Unsigned variants can store `i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup> - 1, which equals
numbers from 0 to 2<sup>n</sup> - 1, so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which -128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
equals 0 to 255. 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 Additionally, the `isize` and `usize` types depend on the kind of computer your
program is running on: 64-bits if youre on a 64-bit architecture and 32-bits program is running on: 64-bits if youre 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 that all number literals except the byte literal allow a type suffix, such as
`57u8`, and `_` as a visual separator, such as `1_000`. `57u8`, and `_` as a visual separator, such as `1_000`.
<caption>
Table 3-2: Integer Literals in Rust Table 3-2: Integer Literals in Rust
</caption>
| Number literals | Example | | Number literals | Example |
|------------------|---------------| |------------------|---------------|
@ -361,18 +356,14 @@ youd use `isize` or `usize` is when indexing some sort of collection.
Rust also has two primitive types for *floating-point numbers*, which are Rust also has two primitive types for *floating-point numbers*, which are
numbers with decimal points. Rusts floating-point types are `f32` and `f64`, numbers with decimal points. Rusts floating-point types are `f32` and `f64`,
which are 32 bits and 64 bits in size, respectively. The default type is `f64` which are 32 bits and 64 bits in size, respectively. The default type is `f64`
because its roughly the same speed as `f32` but is capable of more precision. because on modern CPUs its roughly the same speed as `f32` but is capable of
Its possible to use an `f64` type on 32-bit systems, but it will be slower more precision.
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.
Heres an example that shows floating-point numbers in action: Heres an example that shows floating-point numbers in action:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let x = 2.0; // f64 let x = 2.0; // f64
@ -385,13 +376,13 @@ Floating-point numbers are represented according to the IEEE-754 standard. The
#### Numeric Operations #### Numeric Operations
Rust supports the usual basic mathematic operations youd expect for all of the Rust supports the usual basic mathematical operations youd expect for all of the
number types: addition, subtraction, multiplication, division, and remainder. number types: addition, subtraction, multiplication, division, and remainder.
The following code shows how youd use each one in a `let` statement: The following code shows how youd use each one in a `let` statement:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
// addition // addition
let sum = 5 + 10; let sum = 5 + 10;
@ -422,7 +413,7 @@ For example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let t = true; let t = true;
@ -431,18 +422,19 @@ fn main() {
``` ```
The main way to consume boolean values is through conditionals, such as an `if` The main way to consume boolean values is through conditionals, such as an `if`
statement. Well cover how `if` statements work in Rust in the “Control Flow” expression. Well cover how `if` expressions work in Rust in the “Control Flow”
section. section.
#### The Character Type #### The Character Type
So far weve only worked with numbers, but Rust supports letters too. Rusts So far weve only worked with numbers, but Rust supports letters too. Rusts
`char` type is the languages most primitive alphabetic type, and the following `char` type is the languages 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 Filename: src/main.rs
```rust ```
fn main() { fn main() {
let c = 'z'; let c = 'z';
let z = ''; let z = '';
@ -476,7 +468,7 @@ type annotations in this example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1); 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 Filename: src/main.rs
```rust ```
fn main() { fn main() {
let tup = (500, 6.4, 1); let tup = (500, 6.4, 1);
@ -510,7 +502,7 @@ value we want to access. For example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1); let x: (i32, f64, u8) = (500, 6.4, 1);
@ -538,7 +530,7 @@ inside square brackets:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let a = [1, 2, 3, 4, 5]; let a = [1, 2, 3, 4, 5];
} }
@ -557,7 +549,7 @@ program that needs to know the names of the months of the year. Its very
unlikely that such a program will need to add or remove months, so you can use 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: an array because you know it will always contain 12 items:
```rust ```
let months = ["January", "February", "March", "April", "May", "June", "July", let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"]; "August", "September", "October", "November", "December"];
``` ```
@ -569,7 +561,7 @@ elements of an array using indexing, like this:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let a = [1, 2, 3, 4, 5]; 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 Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let a = [1, 2, 3, 4, 5]; let a = [1, 2, 3, 4, 5];
let index = 10; let index = 10;
@ -602,9 +594,10 @@ fn main() {
Running this code using `cargo run` produces the following result: Running this code using `cargo run` produces the following result:
```bash ```
$ cargo run $ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays) Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays` Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6 10', src/main.rs:6
@ -636,7 +629,7 @@ Heres a program that contains an example function definition:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
@ -649,8 +642,8 @@ fn another_function() {
``` ```
Function definitions in Rust start with `fn` and have a set of parentheses 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 after the function name. The curly brackets tell the compiler where the
body begins and ends. function body begins and ends.
We can call any function weve defined by entering its name followed by a set We can call any function weve defined by entering its name followed by a set
of parentheses. Because `another_function` is defined in the program, it can be of parentheses. Because `another_function` is defined in the program, it can be
@ -663,9 +656,10 @@ Lets start a new binary project named *functions* to explore functions
further. Place the `another_function` example in *src/main.rs* and run it. You further. Place the `another_function` example in *src/main.rs* and run it. You
should see the following output: should see the following output:
```bash ```
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
Running `target/debug/functions` Running `target/debug/functions`
Hello, world! Hello, world!
Another function. Another function.
@ -690,7 +684,7 @@ look like in Rust:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
another_function(5); another_function(5);
} }
@ -702,16 +696,17 @@ fn another_function(x: i32) {
Try running this program; you should get the following output: Try running this program; you should get the following output:
```bash ```
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
``` ```
The declaration of `another_function` has one parameter named `x`. The type of 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 `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. string.
In function signatures, you *must* declare the type of each parameter. This is 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 Filename: src/main.rs
```rust ```
fn main() { fn main() {
another_function(5, 6); another_function(5, 6);
} }
@ -744,9 +739,10 @@ Lets try running this code. Replace the program currently in your *function*
projects *src/main.rs* file with the preceding example, and run it using projects *src/main.rs* file with the preceding example, and run it using
`cargo run`: `cargo run`:
```bash ```
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
The value of y is: 6 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 Filename: src/main.rs
```rust ```
fn main() { fn main() {
let y = 6; let y = 6;
} }
``` ```
<caption>
Listing 3-3: A `main` function declaration containing one statement. Listing 3-3: A `main` function declaration containing one statement.
</caption>
Function definitions are also statements; the entire preceding example is a Function definitions are also statements; the entire preceding example is a
statement in itself. statement in itself.
@ -794,7 +788,7 @@ to another variable, as the following code tries to do:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let x = (let y = 6); let x = (let y = 6);
} }
@ -802,7 +796,7 @@ fn main() {
When you run this program, youll get an error like this: When you run this program, youll get an error like this:
```bash ```
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
error: expected expression, found statement (`let`) error: expected expression, found statement (`let`)
@ -830,7 +824,7 @@ new scopes, `{}`, is an expression, for example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let x = 5; let x = 5;
@ -845,7 +839,7 @@ fn main() {
This expression: This expression:
```rust,ignore ```
{ {
let x = 3; let x = 3;
x + 1 x + 1
@ -853,23 +847,25 @@ This expression:
``` ```
is a block that, in this case, evaluates to `4`. That value gets bound to `y` 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, as part of the `let` statement. Note the `x + 1` line without a semicolon at
unlike most of the lines youve seen so far. Expressions do not include ending the end, unlike most of the lines youve seen so far. Expressions do not
semicolons. If you add a semicolon to the end of an expression, you turn it include ending semicolons. If you add a semicolon to the end of an expression,
into a statement, which will then not return a value. Keep this in mind as you you turn it into a statement, which will then not return a value. Keep this in
explore function return values and expressions next. mind as you explore function return values and expressions next.
### Functions with Return Values ### Functions with Return Values
Functions can return values to the code that calls them. We dont name return Functions can return values to the code that calls them. We dont name return
values, but we do declare their type after an arrow (`->`). In Rust, the 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 value of the function is synonymous with the value of the final expression in
the block of the body of a function. Heres an example of a function that the block of the body of a function. You can return early from a function by
returns a value: using the `return` keyword and specifying a value, but most functions return
the last expression implicitly. Heres an example of a function that returns a
value:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn five() -> i32 { fn five() -> i32 {
5 5
} }
@ -886,9 +882,10 @@ function—just the number `5` by itself. Thats a perfectly valid function in
Rust. Note that the functions return type is specified, too, as `-> i32`. Try Rust. Note that the functions return type is specified, too, as `-> i32`. Try
running this code; the output should look like this: running this code; the output should look like this:
```bash ```
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
``` ```
@ -899,7 +896,7 @@ first, the line `let x = five();` shows that were using the return value of a
function to initialize a variable. Because the function `five` returns a `5`, function to initialize a variable. Because the function `five` returns a `5`,
that line is the same as the following: that line is the same as the following:
```rust ```
let x = 5; let x = 5;
``` ```
@ -910,7 +907,7 @@ example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let x = plus_one(5); let x = plus_one(5);
@ -928,7 +925,7 @@ expression to a statement?
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let x = plus_one(5); let x = plus_one(5);
@ -947,18 +944,14 @@ error[E0308]: mismatched types
--> src/main.rs:7:28 --> src/main.rs:7:28
| |
7 | fn plus_one(x: i32) -> i32 { 7 | fn plus_one(x: i32) -> i32 {
| ____________________________^ starting here... | ____________________________^
8 | | x + 1; 8 | | x + 1;
| | - help: consider removing this semicolon
9 | | } 9 | | }
| |_^ ...ending here: expected i32, found () | |_^ expected i32, found ()
| |
= note: expected type `i32` = note: expected type `i32`
found type `()` 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 The main error message, “mismatched types,” reveals the core issue with this
@ -978,7 +971,7 @@ reading the source code may find useful.
Heres a simple comment: Heres a simple comment:
```rust ```
// Hello, world. // 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, youll need to include line. For comments that extend beyond a single line, youll need to include
`//` on each line, like this: `//` on each line, like this:
```rust ```
// So were doing something complicated here, long enough that we need // So were doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will // multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain whats going on. // explain whats going on.
@ -996,7 +989,7 @@ Comments can also be placed at the end of lines containing code:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let lucky_number = 7; // Im feeling lucky today. let lucky_number = 7; // Im feeling lucky today.
} }
@ -1007,14 +1000,15 @@ separate line above the code its annotating:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
// Im feeling lucky today. // Im feeling lucky today.
let lucky_number = 7; let lucky_number = 7;
} }
``` ```
Thats all there is to comments. Theyre not particularly complicated. Rust also has another kind of comment, documentation comments, which well
discuss in Chapter 14.
## Control Flow ## Control Flow
@ -1035,7 +1029,7 @@ the `if` expression. In the *src/main.rs* file, input the following:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let number = 3; 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 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 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 `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 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 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 discussed in the “Comparing the Guess to the Secret Number” section of
2. Optionally, we can also include an `else` expression, which we chose to do Chapter 2. Optionally, we can also include an `else` expression, which we chose
here, to give the program an alternative block of code to execute should the to do here, to give the program an alternative block of code to execute should
condition evaluate to false. If you dont provide an `else` expression and the the condition evaluate to false. If you dont provide an `else` expression and
condition is false, the program will just skip the `if` block and move on to the condition is false, the program will just skip the `if` block and move on
the next bit of code. to the next bit of code.
Try running this code; you should see the following output: Try running this code; you should see the following output:
```bash ```
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
condition was true condition was true
``` ```
@ -1072,15 +1069,16 @@ condition was true
Lets try changing the value of `number` to a value that makes the condition Lets try changing the value of `number` to a value that makes the condition
`false` to see what happens: `false` to see what happens:
```rust,ignore ```
let number = 7; let number = 7;
``` ```
Run the program again, and look at the output: Run the program again, and look at the output:
```bash ```
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
condition was false condition was false
``` ```
@ -1091,7 +1089,7 @@ code:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let number = 3; let number = 3;
@ -1124,7 +1122,7 @@ expression to the following:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let number = 3; let number = 3;
@ -1143,7 +1141,7 @@ expression. For example:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let number = 6; let number = 6;
@ -1162,9 +1160,10 @@ fn main() {
This program has four possible paths it can take. After running it, you should This program has four possible paths it can take. After running it, you should
see the following output: see the following output:
```bash ```
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
number is divisible by 3 number is divisible by 3
``` ```
@ -1187,7 +1186,7 @@ statement, for instance in Listing 3-4:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let condition = true; let condition = true;
let number = if condition { let number = if condition {
@ -1200,16 +1199,16 @@ fn main() {
} }
``` ```
<caption> Listing 3-4: Assigning the result of an `if` expression
Listing 3-4: Assigning the result of an `if` expression to a variable to a variable
</caption>
The `number` variable will be bound to a value based on the outcome of the `if` The `number` variable will be bound to a value based on the outcome of the `if`
expression. Run this code to see what happens: expression. Run this code to see what happens:
```bash ```
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/branches` Running `target/debug/branches`
The value of number is: 5 The value of number is: 5
``` ```
@ -1224,7 +1223,7 @@ the following example?
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
let condition = true; let condition = true;
@ -1247,15 +1246,15 @@ error[E0308]: if and else have incompatible types
--> src/main.rs:4:18 --> src/main.rs:4:18
| |
4 | let number = if condition { 4 | let number = if condition {
| __________________^ starting here... | __________________^
5 | | 5 5 | | 5
6 | | } else { 6 | | } else {
7 | | "six" 7 | | "six"
8 | | }; 8 | | };
| |_____^ ...ending here: expected integral variable, found reference | |_____^ expected integral variable, found reference
| |
= note: expected type `{integer}` = 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 The expression in the `if` block evaluates to an integer, and the expression in
@ -1286,7 +1285,7 @@ like this:
Filename: src/main.rs Filename: src/main.rs
```rust,ignore ```
fn main() { fn main() {
loop { loop {
println!("again!"); println!("again!");
@ -1296,11 +1295,13 @@ fn main() {
When we run this program, well see `again!` printed over and over continuously When we run this program, well see `again!` printed over and over continuously
until we stop the program manually. Most terminals support a keyboard shortcut, 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 $ cargo run
Compiling loops v0.1.0 (file:///projects/loops) Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
Running `target/debug/loops` Running `target/debug/loops`
again! again!
again! again!
@ -1309,9 +1310,9 @@ again!
^Cagain! ^Cagain!
``` ```
The symbol `^C` represents where you pressed ctrl-C. You may or may not see the The symbol `^C` represents where you pressed <span class="keystroke">ctrl-C
word `again!` printed after the `^C`, depending on where the code was in the </span>. You may or may not see the word `again!` printed after the `^C`,
loop when it received the halt signal. 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. 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 You can place the `break` keyword within the loop to tell the program when to
@ -1334,7 +1335,7 @@ prints another message and exits:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let mut number = 3; let mut number = 3;
@ -1355,11 +1356,11 @@ true, the code runs; otherwise, it exits the loop.
#### Looping Through a Collection with `for` #### Looping Through a Collection with `for`
You could use the `while` construct to loop over the elements of a collection, 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, lets look at Listing 3-5:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
let a = [10, 20, 30, 40, 50]; let a = [10, 20, 30, 40, 50];
let mut index = 0; let mut index = 0;
@ -1372,18 +1373,18 @@ fn main() {
} }
``` ```
<caption> Listing 3-5: Looping through each element of a collection
Listing 3-5: Looping through each element of a collection using a `while` loop using a `while` loop
</caption>
Here, the code counts up through the elements in the array. It starts at index 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, `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 when `index < 5` is no longer true). Running this code will print out every
element in the array: element in the array:
```bash ```
$ cargo run $ cargo run
Compiling loops v0.1.0 (file:///projects/loops) Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
Running `target/debug/loops` Running `target/debug/loops`
the value is: 10 the value is: 10
the value is: 20 the value is: 20
@ -1402,11 +1403,11 @@ code to perform the conditional check on every element on every iteration
through the loop. through the loop.
As a more efficient alternative, you can use a `for` loop and execute some code 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 Filename: src/main.rs
```rust ```
fn main() { fn main() {
let a = [10, 20, 30, 40, 50]; let a = [10, 20, 30, 40, 50];
@ -1416,9 +1417,8 @@ fn main() {
} }
``` ```
<caption> Listing 3-6: Looping through each element of a collection
Listing 3-6: Looping through each element of a collection using a `for` loop using a `for` loop
</caption>
When we run this code, well see the same output as in Listing 3-5. More When we run this code, well see the same output as in Listing 3-5. More
importantly, weve now increased the safety of the code and eliminated the importantly, weve now increased the safety of the code and eliminated the
@ -1443,7 +1443,7 @@ weve not yet talked about, `rev`, to reverse the range:
Filename: src/main.rs Filename: src/main.rs
```rust ```
fn main() { fn main() {
for number in (1..4).rev() { for number in (1..4).rev() {
println!("{}!", number); println!("{}!", number);

View File

@ -8,10 +8,10 @@ together multiple related values that make up a meaningful group. If youre
familiar with an object-oriented language, a *struct* is like an objects data familiar with an object-oriented language, a *struct* is like an objects data
attributes. In this chapter, well compare and contrast tuples with structs, attributes. In this chapter, well compare and contrast tuples with structs,
demonstrate how to use structs, and discuss how to define methods and demonstrate how to use structs, and discuss how to define methods and
associated functions on structs to specify behavior associated with a structs associated functions to specify behavior associated with a structs data. The
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the struct and *enum* (which is discussed in Chapter 6) concepts are the building
building blocks for creating new types in your programs domain to take full blocks for creating new types in your programs domain to take full advantage
advantage of Rusts compile time type checking. of Rusts compile time type checking.
## Defining and Instantiating Structs ## 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 To get a specific value from a struct, we can use dot notation. If we wanted
just this users email address, we can use `user1.email` wherever we want to just this users 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 use this value. If the instance is mutable, we can change a value by using the
can use the dot notation and assign into a particular field. Listing 5-3 shows dot notation and assigning into a particular field. Listing 5-3 shows how to
how to change the value in the `email` field of a mutable `User` instance: change the value in the `email` field of a mutable `User` instance:
``` ```
let mut user1 = User { 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 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 Note that the entire instance must be mutable; Rust doesnt allow us to mark
function by constructing the new instance as the last expression in the only certain fields as mutable. Also note that with any expression, we can
function body. Listing 5-4 shows a `build_user` function that returns a `User` construct a new instance of the struct as the last expression in the function
instance with the given `email` and `username`. The `active` field gets the body to implicitly return that new instance.
value of `true`, and the `sign_in_count` gets a value of `1`.
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 { 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 Listing 5-4: A `build_user` function that takes an email and username and
returns a `User` instance returns a `User` instance
Repeating the `email` field name and `email` variable, and the same for It makes sense to name the function arguments with the same name as the struct
`username`, is a bit tedious, though. It makes sense to name the function fields, but having to repeat the `email` and `username` field names and
arguments with the same name as the struct fields, but if the struct had more variables is a bit tedious. If the struct had more fields, repeating each name
fields, repeating each name would get even more annoying. Luckily, there's a would get even more annoying. Luckily, there's a convenient shorthand!
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 Because the parameter names and the struct field names are exactly the same in
init shorthand*. This can make functions that create new instances of structs Listing 5-4, we can use the *field init shorthand* syntax to rewrite
more concise. `build_user` so that it behaves exactly the same but doesnt have the
repetition of `email` and `username` in the way shown in Listing 5-5.
In Listing 5-4, the parameter names `email` and `username` are the same as the
`User` structs 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.
``` ```
fn build_user(email: String, username: String) -> User { 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 `email` and `username` parameters have the same name as struct fields
Here, were creating a new instance of the `User` struct, which has a field
named `email`. We want to set the `email` fields 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 ### Creating Instances From Other Instances With Struct Update Syntax
Its often useful to create a new instance from an old instance, using most of Its often useful to create a new instance of a struct that uses most of an old
the old instances values but changing some. Listing 5-6 shows an example of instances values, but changes some. We do this using *struct update syntax*.
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 First, Listing 5-6 shows how we create a new `User` instance in `user2` without
`user1` instance we created in Listing 5-2: 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 { let user2 = User {
@ -148,15 +151,12 @@ let user2 = User {
}; };
``` ```
Listing 5-6: Creating a new `User` instance, `user2`, and setting some fields Listing 5-6: Creating a new `User` instance using some of the values from
to the values of the same fields from `user1` `user1`
The *struct update syntax* achieves the same effect as the code in Listing 5-6 Using struct update syntax, we can achieve the same effect with less code,
using less code. The struct update syntax uses `..` to specify that the shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
remaining fields not set explicitly should have the same value as the fields in explicitly set should have the same value as the fields in the given instance.
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:
``` ```
let user2 = User { 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 values for a `User` instance but use the rest of the values from the fields of
the instance in the `user1` variable 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 ### Tuple Structs without Named Fields to Create Different Types
We can also define structs that look similar to tuples, called *tuple structs*, We can also define structs that look similar to tuples, called *tuple structs*,
that have the added meaning the struct name provides, but dont have names that have the added meaning the struct name provides, but dont have names
associated with their fields, just the types of the fields. The definition of a associated with their fields, just the types of the fields. Tuple structs are
tuple struct still starts with the `struct` keyword and the struct name, which useful when you want to give the whole tuple a name and make the tuple be a
are followed by the types in the tuple. For example, here are definitions and different type than other tuples, but naming each field as in a regular struct
usages of tuple structs named `Color` and `Point`: 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); 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 theyre Note that the `black` and `origin` values are different types, since theyre
instances of different tuple structs. Each struct we define is its own type, 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 even though the fields within the struct have the same types. For example, a
struct instances behave like tuples, which we covered in Chapter 3. 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 ### Unit-Like Structs without Any Fields
@ -204,10 +216,10 @@ PROD: START BOX
### Ownership of Struct Data ### Ownership of Struct Data
In the `User` struct definition in Listing 5-1, we used the owned `String` In the `User` struct definition in Listing 5-1, we used the owned `String` type
type rather than the `&str` string slice type. This is a deliberate choice rather than the `&str` string slice type. This is a deliberate choice because
because we want instances of this struct to own all of its data and for that we want instances of this struct to own all of its data and for that data to be
data to be valid for as long as the entire struct is valid. valid for as long as the entire struct is valid.
Its possible for structs to store references to data owned by something else, Its 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 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 | ^ expected lifetime parameter
``` ```
Well discuss how to fix these errors so you can store references in structs Well discuss how to fix these errors so you can store references in structs in
in Chapter 10, but for now, well fix errors like these using owned types like Chapter 10, but for now, well fix errors like these using owned types like
`String` instead of references like `&str`. `String` instead of references like `&str`.
PROD: END BOX PROD: END BOX
@ -264,7 +276,7 @@ calculates the area of a rectangle. Well start with single variables, and the
refactor the program until were using structs instead. refactor the program until were using structs instead.
Lets make a new binary project with Cargo called *rectangles* that will take Lets 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 area of the rectangle. Listing 5-8 shows a short program with one way of doing
just that in our projects *src/main.rs*: just that in our projects *src/main.rs*:
@ -272,22 +284,22 @@ Filename: src/main.rs
``` ```
fn main() { fn main() {
let length1 = 50;
let width1 = 30; let width1 = 30;
let height1 = 50;
println!( println!(
"The area of the rectangle is {} square pixels.", "The area of the rectangle is {} square pixels.",
area(length1, width1) area(width1, height1)
); );
} }
fn area(length: u32, width: u32) -> u32 { fn area(width: u32, height: u32) -> u32 {
length * width width * height
} }
``` ```
Listing 5-8: Calculating the area of a rectangle specified by its length and Listing 5-8: Calculating the area of a rectangle specified by its width and
width in separate variables height in separate variables
Now, run this program using `cargo run`: Now, run this program using `cargo run`:
@ -298,20 +310,20 @@ The area of the rectangle is 1500 square pixels.
### Refactoring with Tuples ### Refactoring with Tuples
Even though Listing 5-8 works and figures out the area of the rectangle by 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 calling the `area` function with each dimension, we can do better. The width
and the width are related to each other because together they describe one and the height are related to each other because together they describe one
rectangle. rectangle.
The issue with this method is evident in the signature of `area`: 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 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 thats function we wrote has two parameters. The parameters are related, but thats
not expressed anywhere in our program. It would be more readable and more not expressed anywhere in our program. It would be more readable and more
manageable to group length and width together. Weve already discussed one way manageable to group width and height together. Weve already discussed one way
we might do that in the Grouping Values into Tuples section of Chapter 3 on 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 page XX: by using tuples. Listing 5-9 shows another version of our program that
uses tuples: uses tuples:
@ -320,7 +332,7 @@ Filename: src/main.rs
``` ```
fn main() { fn main() {
let rect1 = (50, 30); let rect1 = (30, 50);
println!( println!(
"The area of the rectangle is {} square pixels.", "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 In one way, this program is better. Tuples let us add a bit of structure, and
were now passing just one argument. But in another way this version is less were now passing just one argument. But in another way this version is less
clear: tuples dont name their elements, so our calculation has become more clear: tuples dont name their elements, so our calculation has become more
confusing because we have to index into the parts of the tuple. confusing because we have to index into the parts of the tuple.
It doesnt matter if we mix up length and width for the area calculation, but It doesnt 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 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 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 out and keep it in mind as well. It would be easy to forget or mix up these
values and cause errors, because we havent conveyed the meaning of our data in values and cause errors, because we havent conveyed the meaning of our data in
@ -358,12 +370,12 @@ Filename: src/main.rs
``` ```
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!( println!(
"The area of the rectangle is {} square pixels.", "The area of the rectangle is {} square pixels.",
@ -372,16 +384,16 @@ fn main() {
} }
fn area(rectangle: &Rectangle) -> u32 { fn area(rectangle: &Rectangle) -> u32 {
rectangle.length * rectangle.width rectangle.width * rectangle.height
} }
``` ```
Listing 5-10: Defining a `Rectangle` struct Listing 5-10: Defining a `Rectangle` struct
Here weve defined a struct and named it `Rectangle`. Inside the `{}` we Here weve defined a struct and named it `Rectangle`. Inside the `{}` we
defined the fields as `length` and `width`, both of which have type `u32`. Then 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 length of in `main` we create a particular instance of a `Rectangle` that has a width of
50 and a width of 30. 30 and a height of 50.
Our `area` function is now defined with one parameter, which weve named Our `area` function is now defined with one parameter, which weve named
`rectangle`, whose type is an immutable borrow of a struct `Rectangle` `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 using `rect1`, which is the reason we use the `&` in the function signature and
where we call the function. 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: 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 calculate the area of a `Rectangle` using its `width` and `height` fields. This
conveys that the length and width are related to each other, and gives 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` descriptive names to the values rather than using the tuple index values of `0`
and `1`—a win for clarity. and `1`—a win for clarity.
@ -408,12 +420,12 @@ Filename: src/main.rs
``` ```
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {}", rect1); println!("rect1 is {}", rect1);
} }
@ -473,12 +485,12 @@ Filename: src/main.rs
``` ```
#[derive(Debug)] #[derive(Debug)]
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1); println!("rect1 is {:?}", rect1);
} }
@ -491,7 +503,7 @@ Now when we run the program, we wont get any errors and well see the
following output: following output:
``` ```
rect1 is Rectangle { length: 50, width: 30 } rect1 is Rectangle { width: 30, height: 50 }
``` ```
Nice! Its not the prettiest output, but it shows the values of all the fields Nice! Its 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 { rect1 is Rectangle {
length: 50, width: 30,
width: 30 height: 50
} }
``` ```
@ -539,18 +551,18 @@ Filename: src/main.rs
``` ```
#[derive(Debug)] #[derive(Debug)]
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!( println!(
"The area of the rectangle is {} square pixels.", "The area of the rectangle is {} square pixels.",
@ -638,9 +650,9 @@ Filename: src/main.rs
``` ```
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { length: 40, width: 10 }; let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { length: 45, width: 60 }; let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); 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 wed need a mutable borrow), read `rect2` (rather than write, which would mean wed need a mutable borrow),
and we want `main` to retain ownership of `rect2` so we can use it again after and we want `main` to retain ownership of `rect2` so we can use it again after
calling the `can_hold` method. The return value of `can_hold` will be a calling the `can_hold` method. The return value of `can_hold` will be a
boolean, and the implementation will check whether the length and width of boolean, and the implementation will check whether the width and height of
`self` are both greater than the length and width of the other `Rectangle`, `self` are both greater than the width and height of the other `Rectangle`,
respectively. Lets add the new `can_hold` method to the `impl` block from respectively. Lets add the new `can_hold` method to the `impl` block from
Listing 5-13, shown in Listing 5-15: Listing 5-13, shown in Listing 5-15:
@ -677,11 +689,11 @@ Filename: src/main.rs
``` ```
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
fn can_hold(&self, other: &Rectangle) -> bool { 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 Associated functions are often used for constructors that will return a new
instance of the struct. For example, we could provide an associated function 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 thus making it easier to create a square `Rectangle` rather than having to
specify the same value twice: specify the same value twice:
@ -714,7 +726,7 @@ Filename: src/main.rs
``` ```
impl Rectangle { impl Rectangle {
fn square(size: u32) -> 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 { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
} }
impl Rectangle { impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool { 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

View File

@ -14,10 +14,7 @@ advanced features to show you how to:
* Extend Cargo with your own custom commands * Extend Cargo with your own custom commands
Cargo can do even more than what we can cover in this chapter too, so for a 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/*. full explanation, see its documentation at *https://doc.rust-lang.org/cargo/*.
<!--can you give a link to the documentation?-->
<!-- done /Carol -->
## Customizing Builds with Release Profiles ## 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 options for compiling your code. Each profile is configured independently of
the others. the others.
<!-- To be clear, are these release profiles pre-defined profiles that you use Cargo has two main profiles you should know about: the `dev` profile Cargo uses
for different things? Can you lay that out more explicitly, give a more when you run `cargo build`, and the `release` profile Cargo uses when you run
detailed definition? That seems super useful, but I'm not sure I'm following `cargo build --release`. The `dev` profile is defined with good defaults for
what they actually are. --> developing, and likewise the `release` profile has good defaults for release
<!-- They are pre-defined, we've tried to clarify /Carol --> builds.
Cargo has four profiles defined with good default configurations for each use These names may be familiar from the output of your builds, which shows the
case. Cargo uses the different profiles based on which command youre running. profile used in the build:
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 -->
``` ```
$ cargo build $ cargo build
@ -64,31 +39,19 @@ $ cargo build --release
Finished release [optimized] target(s) in 0.0 secs Finished release [optimized] target(s) in 0.0 secs
``` ```
The “dev” and “release” notifications here indicate that the compiler is The “dev” and “release” notifications here indicate that the compiler is using
using different profiles. 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 -->
### Customizing Release 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 Cargo has default settings for each of the profiles that apply when there
arent any `[profile.*]` sections in the projects *Cargo.toml* file. By adding arent any `[profile.*]` sections in the projects *Cargo.toml* file. By adding
`[profile.*]` sections for any profile we want to customize, we can choose to `[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 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: values for the `opt-level` setting for the `dev` and `release` profiles:
Filename: Cargo.toml
``` ```
[profile.dev] [profile.dev]
opt-level = 0 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 projects development profile, for example, we can add these two lines to our projects
*Cargo.toml*: *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 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. 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 For the full list of configuration options and defaults for each profile, see
Cargos documentation at *http://doc.rust-lang.org/cargo/*. Cargos documentation at *https://doc.rust-lang.org/cargo/*.
## Publishing a Crate to Crates.io ## 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 programmers interested in knowing how to *use* your crate, as opposed to how
your crate is *implemented*. your crate is *implemented*.
<!-- Doc comments support markdown but dont require markdown, is that right?
Just wanted to make that distinction -->
<!-- yes -->
Documentation comments use `///` instead of `//` and support Markdown notation Documentation comments use `///` instead of `//` and support Markdown notation
for formatting the text if youd like. You place documentation comments just for formatting the text if youd 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`: for an `add_one` function in a crate named `my_crate`:
Filename: src/lib.rs Filename: src/lib.rs
@ -184,11 +134,7 @@ pub fn add_one(x: i32) -> i32 {
} }
``` ```
Listing 14-2: A documentation comment for a function Listing 14-1: 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 -->
Here, we give a description of what the `add_one` function does, then start a 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 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 crates documentation (as well as the documentation for all of your current crates documentation (as well as the documentation for all of your
crates dependencies) and open the result in a web browser. Navigate to the crates dependencies) and open the result in a web browser. Navigate to the
`add_one` function and youll see how the text in the documentation comments `add_one` function and youll 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" /> <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 Figure 14-2: 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 -->
#### Commonly Used Sections #### 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 in the HTML with the title “Examples”. Some other sections that crate authors
commonly use in their documentation include: commonly use in their documentation include:
- Panics: The scenarios in which this function could `panic!`. Callers of this * **Panics**: The scenarios in which this function could `panic!`. Callers of
function who dont want their programs to panic should make sure that they this function who dont want their programs to panic should make sure that
dont call this function in these situations. they dont call this function in these situations.
- Errors: If this function returns a `Result`, describing the kinds of errors * **Errors**: If this function returns a `Result`, describing the kinds of
that might occur and what conditions might cause those errors to be returned errors that might occur and what conditions might cause those errors to be
can be helpful to callers so that they can write code to handle the different returned can be helpful to callers so that they can write code to handle the
kinds of errors in different ways. different kinds of errors in different ways.
- Safety: If this function uses `unsafe` code (which we will discuss in Chapter * **Safety**: If this function is `unsafe` to call (we will discuss unsafety in
19), there should be a section covering the invariants that this function Chapter 19), there should be a section explaining why the function is unsafe
expects callers to uphold in order for the code in `unsafe` blocks to and covering the invariants that this function expects callers to uphold.
function correctly.
Most documentation comment sections dont need all of these sections, but this Most documentation comment sections dont need all of these sections, but this
is a good list to check to remind you of the kinds of things that people 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 Nothing is better than documentation with examples. Nothing is worse than
examples that dont actually work because the code has changed since the examples that dont actually work because the code has changed since the
documentation has been written. Try running `cargo test` with the documentation 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: 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 #### 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 -->
Theres another style of doc comment, `//!`, that adds documentation to the Theres another style of doc comment, `//!`, that adds documentation to the
item that contains the comments, rather than adding documentation to the items item that contains the comments, rather than adding documentation to the items
following the comments. These are typically used inside the crate root file following the comments. These are typically used inside the crate root file
(*src/lib.rs*) or inside a modules root (*mod.rs*) to document the crate or (*src/lib.rs* by convention) or inside a module to document the crate or the
the module as a whole. module as a whole.
For example, if we wanted to add documentation that described the purpose of 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 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* 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 Filename: src/lib.rs
@ -290,7 +223,7 @@ Filename: src/lib.rs
// ...snip... // ...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 isnt any code after the last line that begins with `//!`. Because Notice there isnt any code after the last line that begins with `//!`. Because
we started the comments with `//!` instead of `///`, were documenting the item we started the comments with `//!` instead of `///`, were documenting the item
@ -300,22 +233,18 @@ is the crate root. These comments describe the entire crate.
If we run `cargo doc --open`, well see these comments displayed on the front If we run `cargo doc --open`, well see these comments displayed on the front
page of the documentation for `my_crate` above the list of public items in the 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" /> <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 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 Documentation comments within items are useful for describing crates and
modules especially. Use them to talk about the purpose of the container overall modules especially. Use them to talk about the purpose of the container overall
to help users of your crate understand your organization. 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` 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 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::some_module::another_module::UsefulType;` rather than `use
my_crate::UsefulType;`. 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 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 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 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 one location and makes it public in another location as if it was defined in
the other location instead. 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. For example, say we made a library named `art` for modeling artistic concepts.
Within this library is a `kinds` module containing two enums named Within this library is a `kinds` module containing two enums named
`PrimaryColor` and `SecondaryColor` and a `utils` module containing a function `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 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 modules
The front page of the documentation for this crate generated by `cargo doc` 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" /> <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` Figure 14-6: Front page of the documentation for `art` that lists the `kinds`
that lists the `kinds` and `utils` modules and `utils` modules
Note that the `PrimaryColor` and `SecondaryColor` types arent listed on the Note that the `PrimaryColor` and `SecondaryColor` types arent listed on the
front page, nor is the `mix` function. We have to click on `kinds` and `utils` 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 Another crate depending on this library would need `use` statements that import
the items from `art` including specifying the module structure thats currently the items from `art` including specifying the module structure thats 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: and `mix` items from the `art` crate:
Filename: src/main.rs Filename: src/main.rs
@ -422,16 +343,10 @@ fn main() {
} }
``` ```
Listing 14-8: A crate using the `art` crates items with its internal structure Listing 14-7: A crate using the `art` crates items with its internal structure
exported exported
<!--Below -- just to clarify, the "users of this crate" refers to people using The author of the code in Listing 14-7 that uses the `art` crate had to figure
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
out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils` 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 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 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. specify the module names in the `use` statements.
To remove the internal organization from the public API, we can take the `art` 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 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-9: items at the top level, as shown in Listing 14-8:
Filename: src/lib.rs Filename: src/lib.rs
@ -465,22 +380,20 @@ pub mod utils {
} }
``` ```
Listing 14-9: Adding `pub use` statements to re-export items Listing 14-8: Adding `pub use` statements to re-export items
<!-- Will add ghosting in libreoffice /Carol -->
The API documentation generated with `cargo doc` for this crate will now list 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. 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" /> <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 re-exports
Users of the `art` crate can still see and choose to use the internal structure 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 as in Listing 14-7, or they can use the more convenient structure from Listing
14-9, as shown in Listing 14-11: 14-8, as shown in Listing 14-10:
Filename: src/main.rs Filename: src/main.rs
@ -495,9 +408,7 @@ fn main() {
} }
``` ```
Listing 14-11: A program using the re-exported items from the `art` crate Listing 14-10: A program using the re-exported items from the `art` crate
<!-- Will add ghosting in libreoffice /Carol -->
In cases where there are many nested modules, re-exporting the types at the top 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 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 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 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 accountthe GitHub account is a requirement for now, but
the site may support other ways of creating an account in the future. Once the site may support other ways of creating an account in the future. Once
youre logged in, visit your account settings at *https://crates.io/me* and youre 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, 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 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 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. revoke it and generate a new token on Crates.io.
@ -535,9 +446,6 @@ Now you have an account, and lets say you already have a crate you want to
publish. Before publishing, youll need to add some metadata to your crate by publish. Before publishing, youll need to add some metadata to your crate by
adding it to the `[package]` section of the crates *Cargo.toml*. adding it to the `[package]` section of the crates *Cargo.toml*.
<!-- Is this right, everything here is relevant to cargo.toml?-->
<!-- Yep /Carol -->
Your crate will first need a unique name. While youre working on a crate Your crate will first need a unique name. While youre working on a crate
locally, you may name a crate whatever youd like. However, crate names on locally, you may name a crate whatever youd like. However, crate names on
Crates.io are allocated on a first-come-first-serve basis. Once a crate name is Crates.io are allocated on a first-come-first-serve basis. Once a crate name is
@ -546,6 +454,8 @@ youd like to use on the site to find out if it has been taken. If it hasnt
edit the name in *Cargo.toml* under `[package]` to have the name you want to edit the name in *Cargo.toml* under `[package]` to have the name you want to
use for publishing like so: use for publishing like so:
Filename: Cargo.toml
``` ```
[package] [package]
name = "guessing_game" 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 youve identifiers you can use for this value. For example, to specify that youve
licensed your crate using the MIT License, add the `MIT` identifier: licensed your crate using the MIT License, add the `MIT` identifier:
Filename: Cargo.toml
``` ```
[package] [package]
name = "guessing_game" name = "guessing_game"
license = "MIT" 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 doesnt appear in the SPDX, you need to place If you want to use a license that doesnt appear in the SPDX, you need to place
the text of that license in a file, include the file in your project, then use 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` `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 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 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 demonstrates that you can also specify multiple license identifiers separated
by a slash. 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, when you created the crate, your description, and the license you chose added,
the *Cargo.toml* for a project thats ready to publish might look like this: the *Cargo.toml* for a project thats ready to publish might look like this:
Filename: Cargo.toml
``` ```
[package] [package]
name = "guessing_game" name = "guessing_game"
@ -611,7 +521,7 @@ license = "MIT/Apache-2.0"
[dependencies] [dependencies]
``` ```
Cargo's documentation at *http://doc.rust-lang.org/cargo/* describes other Cargos documentation at *https://doc.rust-lang.org/cargo/* describes other
metadata you can specify to ensure your crate can be discovered and used more metadata you can specify to ensure your crate can be discovered and used more
easily! easily!
@ -648,10 +558,9 @@ anyone can easily add your crate as a dependency of their project.
When youve made changes to your crate and are ready to release a new version, When youve 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. 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 Use the Semantic Versioning rules at *http://semver.org/* to decide what an
version number is based on the kinds of changes youve made. Then run `cargo appropriate next version number is based on the kinds of changes youve made.
publish` to upload the new version. Then run `cargo publish` to upload the new version.
### Removing Versions from Crates.io with `cargo yank` ### 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 a crate ends up being broken for one reason or another. For situations such as
this, Cargo supports *yanking* a version of a crate. this, Cargo supports *yanking* a version of a crate.
Yanking a version prevents new projects from starting to depend on that Yanking a version prevents new projects from starting to depend on that version
version while allowing all existing projects that depend on it to continue to while allowing all existing projects that depend on it to continue to download
download and depend on that version. Essentially, a yank means that all and depend on that version. Essentially, a yank means that all projects with a
projects with a *Cargo.lock* will not break, while any future *Cargo.lock* *Cargo.lock* will not break, while any future *Cargo.lock* files generated will
files generated will not use the yanked version. not use the yanked version.
To yank a version of a crate, run `cargo yank` and specify which version you To yank a version of a crate, run `cargo yank` and specify which version you
want to yank: want to yank:
@ -711,6 +620,8 @@ We need to modify the binary packages *Cargo.toml* and add a `[workspace]`
section to tell Cargo the `adder` package is a workspace. Add this at the section to tell Cargo the `adder` package is a workspace. Add this at the
bottom of the file: bottom of the file:
Filename: Cargo.toml
``` ```
[workspace] [workspace]
``` ```
@ -719,26 +630,20 @@ Like many Cargo features, workspaces support convention over configuration: we
dont need to add anything more than this to *Cargo.toml* to define our dont need to add anything more than this to *Cargo.toml* to define our
workspace as long as we follow the convention. 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 ### Specifying Workspace Dependencies
The workspace convention says any crates in any subdirectories that the By default, Cargo will include all transitive path dependencies. A *path
top-level crate depends on are part of the workspace. Any crate, whether in a dependency* is when any crate, whether in a workspace or not, specifies that it
workspace or not, can specify that it has a dependency on a crate in a local has a dependency on a crate in a local directory by using the `path` attribute
directory by using the `path` attribute on the dependency specification in on the dependency specification in *Cargo.toml*. If a crate has the
*Cargo.toml*. If a crate has the `[workspace]` key and we specify path `[workspace]` key, or if the crate is itself part of a workspace, and we
dependencies where the paths are subdirectories of the crates directory, those specify path dependencies where the paths are subdirectories of the crates
dependent crates will be considered part of the workspace. Lets specify in the directory, those dependent crates will be considered part of the workspace.
*Cargo.toml* for the top-level `adder` crate that it will have a dependency on Lets specify in the *Cargo.toml* for the top-level `adder` crate that it will
an `add-one` crate that will be in the `add-one` subdirectory, by changing have a dependency on an `add-one` crate that will be in the `add-one`
*Cargo.toml* to look like this: subdirectory, by changing *Cargo.toml* to look like this:
<!-- Above, what is the path dependency actually doing here, can you fill out Filename: Cargo.toml
the paragraph above? -->
<!-- done /Carol -->
``` ```
[dependencies] [dependencies]
@ -751,10 +656,6 @@ and are assumed to come from Crates.io.
### Creating the Second Crate in the Workspace ### 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: 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 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 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; 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
Lets build the `adder` crate by running `cargo build` in the *adder* directory! Lets 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 the workspace can avoid rebuilding the other crates in the workspace more than
necessary. 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 #### Depending on an External Crate in a Workspace
Also notice the workspace only has one *Cargo.lock*, rather than having a 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 crates directory and run `cargo publish` on each crate in the workspace each crates directory and run `cargo publish` on each crate in the workspace
in order to publish them. 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 Now try adding an `add-two` crate to this workspace in a similar way as the
`add-one` crate for some more practice! `add-one` crate for some more practice!
@ -1024,13 +913,10 @@ target that isnt runnable on its own but is suitable for including within
other programs. Usually, crates have information in the *README* file about other programs. Usually, crates have information in the *README* file about
whether a crate is a library, has a binary target, or both. 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 roots *bin* All binaries from `cargo install` are put into the installation roots *bin*
folder. If you installed Rust using *rustup.rs* and dont have any custom folder. If you installed Rust using *rustup.rs* and dont have any custom
configurations, this will be `$HOME/.cargo/bin`. Add that directory to your configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in
`$PATH` to be able to run programs youve gotten through `cargo install`. your `$PATH` to be able to run programs youve gotten through `cargo install`.
For example, we mentioned in Chapter 12 that theres a Rust implementation of For example, we mentioned in Chapter 12 that theres a Rust implementation of
the `grep` tool for searching files called `ripgrep`. If we want to install 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

View File

@ -171,25 +171,25 @@ Any such expression always has the `unit` type.
#### Operator precedence #### Operator precedence
The precedence of Rust binary operators is ordered as follows, going from The precedence of Rust operators is ordered as follows, going from strong to
strong to weak: 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 | Operator | Associativity |
operators have the same precedence level and are stronger than any of the |-----------------------------|---------------------|
binary operators. | `?` | |
| Unary `-` `*` `!` `&` `&mut` | |
| `as` `:` | left to right |
| `*` `/` `%` | left to right |
| `+` `-` | left to right |
| `<<` `>>` | left to right |
| `&` | left to right |
| `^` | left to right |
| <code>&#124;</code> | left to right |
| `==` `!=` `<` `>` `<=` `>=` | Require parentheses |
| `&&` | left to right |
| <code>&#124;&#124;</code> | left to right |
| `..` `...` | Require parentheses |
| `<-` | right to left |
| `=` `+=` `-=` `*=` `/=` `%=` <br> `&=` <code>&#124;=</code> `^=` `<<=` `>>=` | right to left |

View File

@ -92,10 +92,10 @@ says, “Im declaring a function named `main` that has no parameters and retu
nothing.” If there were parameters, their names would go inside the nothing.” If there were parameters, their names would go inside the
parentheses, `(` and `)`. parentheses, `(` and `)`.
Also note that the function body is wrapped in curly braces, `{` and `}`. Rust Also note that the function body is wrapped in curly brackets, `{` and `}`.
requires these around all function bodies. Its considered good style to put Rust requires these around all function bodies. Its considered good style to
the opening curly brace on the same line as the function declaration, with one put the opening curly bracket on the same line as the function declaration,
space in between. with one space in between.
Inside the `main` function: Inside the `main` function:
@ -319,7 +319,7 @@ $ cargo build
``` ```
This should have created an executable file in *target/debug/hello_cargo* (or This should have created an executable file in *target/debug/hello_cargo* (or
*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 ```text
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows $ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows

View File

@ -60,6 +60,7 @@ using the `cargo run` command:
```text ```text
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Hello, world! Hello, world!
``` ```
@ -134,8 +135,8 @@ println!("Guess the number!");
println!("Please input your guess."); println!("Please input your guess.");
``` ```
This code is just printing a prompt stating what the game is and requesting This code is printing a prompt stating what the game is and requesting input
input from the user. from the user.
### Storing Values with Variables ### Storing Values with Variables
@ -284,21 +285,24 @@ If we dont call `expect`, the program will compile, but well get a warning
```text ```text
$ cargo build $ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used, warning: unused `std::result::Result` which must be used
#[warn(unused_must_use)] on by default --> src/main.rs:10:5
src/main.rs:10 io::stdin().read_line(&mut guess); |
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | io::stdin().read_line(&mut guess);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_must_use)] on by default
``` ```
Rust warns that we havent used the `Result` value returned from `read_line`, Rust warns that we havent used the `Result` value returned from `read_line`,
indicating that the program hasnt handled a possible error. The right way to indicating that the program hasnt handled a possible error. The right way to
suppress the warning is to actually write error handling, but since we just suppress the warning is to actually write error handling, but since we want to
want to crash this program when a problem occurs, we can use `expect`. Youll crash this program when a problem occurs, we can use `expect`. Youll learn
learn about recovering from errors in Chapter 9. about recovering from errors in Chapter 9.
### Printing Values with `println!` Placeholders ### Printing Values with `println!` Placeholders
Aside from the closing curly brace, theres only one more line to discuss in Aside from the closing curly brackets, theres only one more line to discuss in
the code added so far, which is the following: the code added so far, which is the following:
```rust,ignore ```rust,ignore
@ -328,6 +332,7 @@ Lets test the first part of the guessing game. You can run it using
```text ```text
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Guess the number! Guess the number!
Please input your guess. Please input your guess.
@ -391,6 +396,7 @@ $ cargo build
Compiling libc v0.2.14 Compiling libc v0.2.14
Compiling rand v0.3.14 Compiling rand v0.3.14
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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 <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 ```text
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) 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` Running `target/debug/guessing_game`
Guess the number! Guess the number!
The secret number is: 7 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 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 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 the beginning and end. `u32` can only contain numerical characters, but the
user must press the <span class="keystroke">return</span> key to satisfy user must press the <span class="keystroke">enter</span> key to satisfy
`read_line`. When the user presses <span class="keystroke">return</span>, a `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 newline character is added to the string. For example, if the user types <span
<span class="keystroke">5</span> and presses <span class="keystroke"> class="keystroke">5</span> and presses <span class="keystroke"> enter</span>,
return</span>, `guess` looks like this: `5\n`. The `\n` represents “newline,” `guess` looks like this: `5\n`. The `\n` represents “newline,” the enter key.
the <span class="keystroke">return</span> key. The `trim` method eliminates The `trim` method eliminates `\n`, resulting in just `5`.
`\n`, resulting in just `5`.
The [`parse` method on strings][parse]<!-- ignore --> parses a string into some 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 kind of number. Because this method can parse a variety of number types, we
@ -795,6 +801,7 @@ Lets run the program now!
```text ```text
$ cargo run $ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
Running `target/guessing_game` Running `target/guessing_game`
Guess the number! Guess the number!
The secret number is: 58 The secret number is: 58

View File

@ -83,6 +83,7 @@ When we run this program, we get the following:
```text ```text
$ cargo run $ cargo run
Compiling variables v0.1.0 (file:///projects/variables) Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/variables` Running `target/debug/variables`
The value of x is: 5 The value of x is: 5
The value of x is: 6 The value of x is: 6
@ -174,6 +175,7 @@ When you run this program, it will output the following:
```text ```text
$ cargo run $ cargo run
Compiling variables v0.1.0 (file:///projects/variables) Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/variables` Running `target/debug/variables`
The value of x is: 12 The value of x is: 12
``` ```

View File

@ -21,13 +21,14 @@ error, which means the compiler needs more information from us to know which
possible type we want to use: possible type we want to use:
```text ```text
error[E0282]: unable to infer enough type information about `_` error[E0282]: type annotations needed
--> src/main.rs:2:9 --> src/main.rs:2:9
| |
2 | let guess = "42".parse().expect("Not a number!"); 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
``` ```
Youll see different type annotations as we discuss the various data types. Youll see different type annotations as we discuss the various data types.
@ -42,11 +43,11 @@ work in Rust.
#### Integer Types #### Integer Types
An *integer* is a number without a fractional component. We used one integer 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 type earlier in this chapter, the `u32` type. This type declaration indicates
that the value its associated with should be a signed integer (hence the `i`, that the value its associated with should be an unsigned integer (signed
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1 integer types start with `i` instead of `u`) that takes up 32 bits of space.
shows the built-in integer types in Rust. Each variant in the Signed and Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
Unsigned columns (for example, *i32*) can be used to declare the type of an and Unsigned columns (for example, *i16*) can be used to declare the type of an
integer value. integer value.
<span class="caption">Table 3-1: Integer Types in Rust</span> <span class="caption">Table 3-1: Integer Types in Rust</span>
@ -104,12 +105,8 @@ youd use `isize` or `usize` is when indexing some sort of collection.
Rust also has two primitive types for *floating-point numbers*, which are Rust also has two primitive types for *floating-point numbers*, which are
numbers with decimal points. Rusts floating-point types are `f32` and `f64`, numbers with decimal points. Rusts floating-point types are `f32` and `f64`,
which are 32 bits and 64 bits in size, respectively. The default type is `f64` which are 32 bits and 64 bits in size, respectively. The default type is `f64`
because its roughly the same speed as `f32` but is capable of more precision. because on modern CPUs its roughly the same speed as `f32` but is capable of
Its possible to use an `f64` type on 32-bit systems, but it will be slower more precision.
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.
Heres an example that shows floating-point numbers in action: Heres an example that shows floating-point numbers in action:
@ -181,7 +178,8 @@ section.
So far weve only worked with numbers, but Rust supports letters too. Rusts So far weve only worked with numbers, but Rust supports letters too. Rusts
`char` type is the languages most primitive alphabetic type, and the following `char` type is the languages 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> <span class="filename">Filename: src/main.rs</span>
@ -348,6 +346,7 @@ Running this code using `cargo run` produces the following result:
```text ```text
$ cargo run $ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays) Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays` Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6 10', src/main.rs:6

View File

@ -24,8 +24,8 @@ fn another_function() {
``` ```
Function definitions in Rust start with `fn` and have a set of parentheses 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 after the function name. The curly brackets tell the compiler where the
body begins and ends. function body begins and ends.
We can call any function weve defined by entering its name followed by a set We can call any function weve defined by entering its name followed by a set
of parentheses. Because `another_function` is defined in the program, it can be of parentheses. Because `another_function` is defined in the program, it can be
@ -41,6 +41,7 @@ should see the following output:
```text ```text
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
Running `target/debug/functions` Running `target/debug/functions`
Hello, world! Hello, world!
Another function. Another function.
@ -80,13 +81,14 @@ Try running this program; you should get the following output:
```text ```text
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
``` ```
The declaration of `another_function` has one parameter named `x`. The type of 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 `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. string.
In function signatures, you *must* declare the type of each parameter. This is In function signatures, you *must* declare the type of each parameter. This is
@ -122,6 +124,7 @@ projects *src/main.rs* file with the preceding example, and run it using
```text ```text
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
The value of y is: 6 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` 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, as part of the `let` statement. Note the `x + 1` line without a semicolon at
unlike most of the lines youve seen so far. Expressions do not include ending the end, unlike most of the lines youve seen so far. Expressions do not
semicolons. If you add a semicolon to the end of an expression, you turn it include ending semicolons. If you add a semicolon to the end of an expression,
into a statement, which will then not return a value. Keep this in mind as you you turn it into a statement, which will then not return a value. Keep this in
explore function return values and expressions next. mind as you explore function return values and expressions next.
### Functions with Return Values ### Functions with Return Values
Functions can return values to the code that calls them. We dont name return Functions can return values to the code that calls them. We dont name return
values, but we do declare their type after an arrow (`->`). In Rust, the 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 value of the function is synonymous with the value of the final expression in
the block of the body of a function. Heres an example of a function that the block of the body of a function. You can return early from a function by
returns a value: using the `return` keyword and specifying a value, but most functions return
the last expression implicitly. Heres an example of a function that returns a
value:
<span class="filename">Filename: src/main.rs</span> <span class="filename">Filename: src/main.rs</span>
@ -262,6 +267,7 @@ running this code; the output should look like this:
```text ```text
$ cargo run $ cargo run
Compiling functions v0.1.0 (file:///projects/functions) Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/functions` Running `target/debug/functions`
The value of x is: 5 The value of x is: 5
``` ```
@ -322,16 +328,12 @@ error[E0308]: mismatched types
7 | fn plus_one(x: i32) -> i32 { 7 | fn plus_one(x: i32) -> i32 {
| ____________________________^ | ____________________________^
8 | | x + 1; 8 | | x + 1;
| | - help: consider removing this semicolon
9 | | } 9 | | }
| |_^ expected i32, found () | |_^ expected i32, found ()
| |
= note: expected type `i32` = note: expected type `i32`
found type `()` 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 The main error message, “mismatched types,” reveals the core issue with this

View File

@ -43,4 +43,5 @@ fn main() {
} }
``` ```
Thats all there is to comments. Theyre not particularly complicated. Rust also has another kind of comment, documentation comments, which well
discuss in Chapter 14.

View File

@ -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 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 `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 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 sometimes called *arms*, just like the arms in `match` expressions that we
discussed in the “Comparing the Guess to the Secret Number” section of 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 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 ```text
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
condition was true condition was true
``` ```
@ -65,6 +66,7 @@ Run the program again, and look at the output:
```text ```text
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
condition was false condition was false
``` ```
@ -149,6 +151,7 @@ see the following output:
```text ```text
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches` Running `target/debug/branches`
number is divisible by 3 number is divisible by 3
``` ```
@ -193,6 +196,7 @@ expression. Run this code to see what happens:
```text ```text
$ cargo run $ cargo run
Compiling branches v0.1.0 (file:///projects/branches) Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/branches` Running `target/debug/branches`
The value of number is: 5 The value of number is: 5
``` ```
@ -238,7 +242,7 @@ error[E0308]: if and else have incompatible types
| |_____^ expected integral variable, found reference | |_____^ expected integral variable, found reference
| |
= note: expected type `{integer}` = 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 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 ```text
$ cargo run $ cargo run
Compiling loops v0.1.0 (file:///projects/loops) Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
Running `target/debug/loops` Running `target/debug/loops`
again! again!
again! again!
@ -339,7 +344,7 @@ true, the code runs; otherwise, it exits the loop.
#### Looping Through a Collection with `for` #### Looping Through a Collection with `for`
You could use the `while` construct to loop over the elements of a collection, 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, lets look at Listing 3-5:
<span class="filename">Filename: src/main.rs</span> <span class="filename">Filename: src/main.rs</span>
@ -367,6 +372,7 @@ element in the array:
```text ```text
$ cargo run $ cargo run
Compiling loops v0.1.0 (file:///projects/loops) Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
Running `target/debug/loops` Running `target/debug/loops`
the value is: 10 the value is: 10
the value is: 20 the value is: 20
@ -385,7 +391,7 @@ code to perform the conditional check on every element on every iteration
through the loop. through the loop.
As a more efficient alternative, you can use a `for` loop and execute some code 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> <span class="filename">Filename: src/main.rs</span>

View File

@ -5,7 +5,7 @@ together multiple related values that make up a meaningful group. If youre
familiar with an object-oriented language, a *struct* is like an objects data familiar with an object-oriented language, a *struct* is like an objects data
attributes. In this chapter, well compare and contrast tuples with structs, attributes. In this chapter, well compare and contrast tuples with structs,
demonstrate how to use structs, and discuss how to define methods and demonstrate how to use structs, and discuss how to define methods and
associated functions on structs to specify behavior associated with a structs associated functions to specify behavior associated with a structs data. The
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the struct and *enum* (which is discussed in Chapter 6) concepts are the building
building blocks for creating new types in your programs domain to take full blocks for creating new types in your programs domain to take full advantage
advantage of Rusts compile time type checking. of Rusts compile time type checking.

View File

@ -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 To define a struct, we enter the keyword `struct` and name the entire struct. A
structs name should describe the significance of the pieces of data being structs 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 the pieces of data, which we call *fields*. For example, Listing 5-1 shows a
struct to store information about a user account: struct to store information about a user account:
@ -25,7 +25,7 @@ struct User {
To use a struct after weve defined it, we create an *instance* of that struct To use a struct after weve defined it, we create an *instance* of that struct
by specifying concrete values for each of the fields. We create an instance by 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 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 dont have to specify the fields in data we want to store in those fields. We dont have to specify the fields in
the same order in which we declared them in the struct. In other words, the 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 To get a specific value from a struct, we can use dot notation. If we wanted
just this users email address, we can use `user1.email` wherever we want to just this users 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 use this value. If the instance is mutable, we can change a value by using the
can use the dot notation and assign into a particular field. Listing 5-3 shows dot notation and assigning into a particular field. Listing 5-3 shows how to
how to change the value in the `email` field of a mutable `User` instance: change the value in the `email` field of a mutable `User` instance:
```rust ```rust
# struct User { # 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 <span class="caption">Listing 5-3: Changing the value in the `email` field of a
`User` instance</span> `User` instance</span>
Like any expression, we can implicitly return a new instance of a struct from a Note that the entire instance must be mutable; Rust doesnt allow us to mark
function by constructing the new instance as the last expression in the only certain fields as mutable. Also note that as with any expression, we can
function body. Listing 5-4 shows a `build_user` function that returns a `User` construct a new instance of the struct as the last expression in the function
instance with the given `email` and `username`. The `active` field gets the body to implicitly return that new instance.
value of `true`, and the `sign_in_count` gets a value of `1`.
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 ```rust
# struct User { # 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 <span class="caption">Listing 5-4: A `build_user` function that takes an email
and username and returns a `User` instance</span> and username and returns a `User` instance</span>
Repeating the `email` field name and `email` variable, and the same for It makes sense to name the function arguments with the same name as the struct
`username`, is a bit tedious, though. It makes sense to name the function fields, but having to repeat the `email` and `username` field names and
arguments with the same name as the struct fields, but if the struct had more variables is a bit tedious. If the struct had more fields, repeating each name
fields, repeating each name would get even more annoying. Luckily, theres a would get even more annoying. Luckily, there's a convenient shorthand!
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 Because the parameter names and the struct field names are exactly the same in
init shorthand*. This can make functions that create new instances of structs Listing 5-4, we can use the *field init shorthand* syntax to rewrite
more concise. `build_user` so that it behaves exactly the same but doesnt have the
repetition of `email` and `username` in the way shown in Listing 5-5.
In Listing 5-4, the parameter names `email` and `username` are the same as the
`User` structs 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.
```rust ```rust
# struct User { # 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 <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 shorthand since the `email` and `username` parameters have the same name as
fields</span> struct fields</span>
Here, were creating a new instance of the `User` struct, which has a field
named `email`. We want to set the `email` fields 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 ### Creating Instances From Other Instances With Struct Update Syntax
Its often useful to create a new instance from an old instance, using most of Its often useful to create a new instance of a struct that uses most of an old
the old instances values but changing some. Listing 5-6 shows an example of instances values, but changes some. We do this using *struct update syntax*.
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 First, Listing 5-6 shows how we create a new `User` instance in `user2` without
`user1` instance we created in Listing 5-2: 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 ```rust
# struct User { # struct User {
@ -178,15 +181,12 @@ let user2 = User {
}; };
``` ```
<span class="caption">Listing 5-6: Creating a new `User` instance, `user2`, and <span class="caption">Listing 5-6: Creating a new `User` instance using some of
setting some fields to the values of the same fields from `user1`</span> the values from `user1`</span>
The *struct update syntax* achieves the same effect as the code in Listing 5-6 Using struct update syntax, we can achieve the same effect with less code,
using less code. The struct update syntax uses `..` to specify that the shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
remaining fields not set explicitly should have the same value as the fields in explicitly set should have the same value as the fields in the given instance.
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:
```rust ```rust
# struct User { # struct User {
@ -214,14 +214,22 @@ let user2 = User {
`email` and `username` values for a `User` instance but use the rest of the `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> 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 ### Tuple Structs without Named Fields to Create Different Types
We can also define structs that look similar to tuples, called *tuple structs*, We can also define structs that look similar to tuples, called *tuple structs*,
that have the added meaning the struct name provides, but dont have names that have the added meaning the struct name provides, but dont have names
associated with their fields, just the types of the fields. The definition of a associated with their fields, just the types of the fields. Tuple structs are
tuple struct still starts with the `struct` keyword and the struct name, which useful when you want to give the whole tuple a name and make the tuple be a
are followed by the types in the tuple. For example, here are definitions and different type than other tuples, but naming each field as in a regular struct
usages of tuple structs named `Color` and `Point`: 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 ```rust
struct Color(i32, i32, i32); 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 theyre Note that the `black` and `origin` values are different types, since theyre
instances of different tuple structs. Each struct we define is its own type, 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 even though the fields within the struct have the same types. For example, a
struct instances behave like tuples, which we covered in Chapter 3. 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 ### Unit-Like Structs without Any Fields

View File

@ -5,7 +5,7 @@ calculates the area of a rectangle. Well start with single variables, and the
refactor the program until were using structs instead. refactor the program until were using structs instead.
Lets make a new binary project with Cargo called *rectangles* that will take Lets 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 area of the rectangle. Listing 5-8 shows a short program with one way of doing
just that in our projects *src/main.rs*: just that in our projects *src/main.rs*:
@ -13,22 +13,22 @@ just that in our projects *src/main.rs*:
```rust ```rust
fn main() { fn main() {
let length1 = 50;
let width1 = 30; let width1 = 30;
let height1 = 50;
println!( println!(
"The area of the rectangle is {} square pixels.", "The area of the rectangle is {} square pixels.",
area(length1, width1) area(width1, height1)
); );
} }
fn area(length: u32, width: u32) -> u32 { fn area(width: u32, height: u32) -> u32 {
length * width width * height
} }
``` ```
<span class="caption">Listing 5-8: Calculating the area of a rectangle <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`: Now, run this program using `cargo run`:
@ -39,20 +39,20 @@ The area of the rectangle is 1500 square pixels.
### Refactoring with Tuples ### Refactoring with Tuples
Even though Listing 5-8 works and figures out the area of the rectangle by 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 calling the `area` function with each dimension, we can do better. The width
and the width are related to each other because together they describe one and the height are related to each other because together they describe one
rectangle. rectangle.
The issue with this method is evident in the signature of `area`: The issue with this method is evident in the signature of `area`:
```rust,ignore ```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 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 thats function we wrote has two parameters. The parameters are related, but thats
not expressed anywhere in our program. It would be more readable and more not expressed anywhere in our program. It would be more readable and more
manageable to group length and width together. Weve already discussed one way manageable to group width and height together. Weve already discussed one way
we might do that in the Grouping Values into Tuples section of Chapter 3 on 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 page XX: by using tuples. Listing 5-9 shows another version of our program that
uses tuples: uses tuples:
@ -61,7 +61,7 @@ uses tuples:
```rust ```rust
fn main() { fn main() {
let rect1 = (50, 30); let rect1 = (30, 50);
println!( println!(
"The area of the rectangle is {} square pixels.", "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> rectangle with a tuple</span>
In one way, this program is better. Tuples let us add a bit of structure, and In one way, this program is better. Tuples let us add a bit of structure, and
@ -82,9 +82,9 @@ were now passing just one argument. But in another way this version is less
clear: tuples dont name their elements, so our calculation has become more clear: tuples dont name their elements, so our calculation has become more
confusing because we have to index into the parts of the tuple. confusing because we have to index into the parts of the tuple.
It doesnt matter if we mix up length and width for the area calculation, but It doesnt 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 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 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 out and keep it in mind as well. It would be easy to forget or mix up these
values and cause errors, because we havent conveyed the meaning of our data in values and cause errors, because we havent conveyed the meaning of our data in
@ -100,12 +100,12 @@ parts, as shown in Listing 5-10:
```rust ```rust
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!( println!(
"The area of the rectangle is {} square pixels.", "The area of the rectangle is {} square pixels.",
@ -114,16 +114,16 @@ fn main() {
} }
fn area(rectangle: &Rectangle) -> u32 { fn area(rectangle: &Rectangle) -> u32 {
rectangle.length * rectangle.width rectangle.width * rectangle.height
} }
``` ```
<span class="caption">Listing 5-10: Defining a `Rectangle` struct</span> <span class="caption">Listing 5-10: Defining a `Rectangle` struct</span>
Here weve defined a struct and named it `Rectangle`. Inside the `{}` we Here weve defined a struct and named it `Rectangle`. Inside the `{}` we
defined the fields as `length` and `width`, both of which have type `u32`. Then 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 length of in `main` we create a particular instance of a `Rectangle` that has a width of
50 and a width of 30. 30 and a height of 50.
Our `area` function is now defined with one parameter, which weve named Our `area` function is now defined with one parameter, which weve named
`rectangle`, whose type is an immutable borrow of a struct `Rectangle` `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 using `rect1`, which is the reason we use the `&` in the function signature and
where we call the function. 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: 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 calculate the area of a `Rectangle` using its `width` and `height` fields. This
conveys that the length and width are related to each other, and gives 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` descriptive names to the values rather than using the tuple index values of `0`
and `1`—a win for clarity. and `1`—a win for clarity.
@ -150,12 +150,12 @@ chapters:
```rust,ignore ```rust,ignore
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {}", rect1); println!("rect1 is {}", rect1);
} }
@ -176,7 +176,7 @@ direct end user consumption. The primitive types weve seen so far implement
`Display` by default, because theres only one way youd want to show a `1` or `Display` by default, because theres only one way youd want to show a `1` or
any other primitive type to a user. But with structs, the way `println!` should 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: 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 doesnt try to guess what we the fields be shown? Due to this ambiguity, Rust doesnt try to guess what we
want and structs dont have a provided implementation of `Display`. want and structs dont have a provided implementation of `Display`.
@ -216,12 +216,12 @@ definition, as shown in Listing 5-12:
```rust ```rust
#[derive(Debug)] #[derive(Debug)]
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1); println!("rect1 is {:?}", rect1);
} }
@ -234,7 +234,7 @@ Now when we run the program, we wont get any errors and well see the
following output: following output:
```text ```text
rect1 is Rectangle { length: 50, width: 30 } rect1 is Rectangle { width: 30, height: 50 }
``` ```
Nice! Its not the prettiest output, but it shows the values of all the fields Nice! Its 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 ```text
rect1 is Rectangle { rect1 is Rectangle {
length: 50, width: 30,
width: 30 height: 50
} }
``` ```

View File

@ -19,18 +19,18 @@ in Listing 5-13:
```rust ```rust
#[derive(Debug)] #[derive(Debug)]
struct Rectangle { struct Rectangle {
length: u32,
width: u32, width: u32,
height: u32,
} }
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
} }
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
println!( println!(
"The area of the rectangle is {} square pixels.", "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` To define the function within the context of `Rectangle`, we start an `impl`
(*implementation*) block. Then we move the `area` function within the `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 `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 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. use *method syntax* to call the `area` method on our `Rectangle` instance.
@ -131,9 +131,9 @@ method:
```rust,ignore ```rust,ignore
fn main() { fn main() {
let rect1 = Rectangle { length: 50, width: 30 }; let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { length: 40, width: 10 }; let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { length: 45, width: 60 }; let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); 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 wed need a mutable borrow), read `rect2` (rather than write, which would mean wed need a mutable borrow),
and we want `main` to retain ownership of `rect2` so we can use it again after and we want `main` to retain ownership of `rect2` so we can use it again after
calling the `can_hold` method. The return value of `can_hold` will be a calling the `can_hold` method. The return value of `can_hold` will be a
boolean, and the implementation will check whether the length and width of boolean, and the implementation will check whether the width and height of
`self` are both greater than the length and width of the other `Rectangle`, `self` are both greater than the width and height of the other `Rectangle`,
respectively. Lets add the new `can_hold` method to the `impl` block from respectively. Lets add the new `can_hold` method to the `impl` block from
Listing 5-13, shown in Listing 5-15: Listing 5-13, shown in Listing 5-15:
@ -171,17 +171,17 @@ Listing 5-13, shown in Listing 5-15:
```rust ```rust
# #[derive(Debug)] # #[derive(Debug)]
# struct Rectangle { # struct Rectangle {
# length: u32,
# width: u32, # width: u32,
# height: u32,
# } # }
# #
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
fn can_hold(&self, other: &Rectangle) -> bool { 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 Associated functions are often used for constructors that will return a new
instance of the struct. For example, we could provide an associated function 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 thus making it easier to create a square `Rectangle` rather than having to
specify the same value twice: specify the same value twice:
@ -214,13 +214,13 @@ specify the same value twice:
```rust ```rust
# #[derive(Debug)] # #[derive(Debug)]
# struct Rectangle { # struct Rectangle {
# length: u32,
# width: u32, # width: u32,
# height: u32,
# } # }
# #
impl Rectangle { impl Rectangle {
fn square(size: u32) -> 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 ```rust
# #[derive(Debug)] # #[derive(Debug)]
# struct Rectangle { # struct Rectangle {
# length: u32,
# width: u32, # width: u32,
# height: u32,
# } # }
# #
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
self.length * self.width self.width * self.height
} }
} }
impl Rectangle { impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool { fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width self.width > other.width && self.height > other.height
} }
} }
``` ```

View File

@ -249,7 +249,7 @@ m.call();
The body of the method would use `self` to get the value that we called the The body of the method would use `self` to get the value that we called the
method on. In this example, weve created a variable `m` that has the value method on. In this example, weve 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. `call` method when `m.call()` runs.
Lets look at another enum in the standard library that is very common and Lets look at another enum in the standard library that is very common and

View File

@ -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 the expression in the matching arm is the value that gets returned for the
entire `match` expression. entire `match` expression.
Curly braces typically arent used if the match arm code is short, as it is in Curly brackets typically arent used if the match arm code is short, as it is
Listing 6-3 where each arm just returns a value. If you want to run multiple 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 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 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`: with a `Coin::Penny` but would still return the last value of the block, `1`:
@ -99,7 +99,7 @@ As an example, lets change one of our enum variants to hold data inside it.
From 1999 through 2008, the United States minted quarters with different 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 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 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 weve done here in Listing 6-4: inside it, which weve done here in Listing 6-4:
```rust ```rust

View File

@ -14,7 +14,7 @@ you can choose whether those definitions are visible outside their module
(public) or not (private). Heres an overview of how modules work: (public) or not (private). Heres an overview of how modules work:
* The `mod` keyword declares a new module. Code within the module appears * 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. another file.
* By default, functions, types, constants, and modules are private. The `pub` * By default, functions, types, constants, and modules are private. The `pub`
keyword makes an item public and therefore visible outside its namespace. keyword makes an item public and therefore visible outside its namespace.

View File

@ -63,7 +63,7 @@ mod network {
``` ```
After the `mod` keyword, we put the name of the module, `network`, and then a 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 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 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: 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 modules name. 1. Make a new *directory* named *network*, the parent modules name.
2. Move the *src/network.rs* file into the new *network* directory, and 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. 3. Move the submodule file *src/server.rs* into the *network* directory.
Here are commands to carry out these steps: Here are commands to carry out these steps:

View File

@ -83,7 +83,7 @@ directly.
Because enums also form a sort of namespace like modules, we can import an Because enums also form a sort of namespace like modules, we can import an
enums variants with `use` as well. For any kind of `use` statement, if youre enums variants with `use` as well. For any kind of `use` statement, if youre
importing multiple items from one namespace, you can list them using curly 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 ```rust
enum TrafficLight { enum TrafficLight {

View File

@ -1,18 +1,18 @@
# Common Collections # Common Collections
Rusts standard library includes a number of really useful data structures Rusts standard library includes a number of very useful data structures called
called *collections*. Most other data types represent one specific value, but *collections*. Most other data types represent one specific value, but
collections can contain multiple values. Unlike the built-in array and tuple 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 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 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 shrink as the program runs. Each kind of collection has different capabilities
and costs, and choosing an appropriate one for the situation youre in is a and costs, and choosing an appropriate one for your current situation is a
skill youll develop over time. In this chapter, well go over three skill youll develop over time. In this chapter, well discuss three
collections which are used very often in Rust programs: 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 *vector* allows us to store a variable number of values next to each other.
* A *string* is a collection of characters. Weve seen the `String` type * A *string* is a collection of characters. Weve discussed the `String` type
before, but well talk about it in depth now. previously, but in this chapter well talk about it in depth.
* A *hash map* allows us to associate a value with a particular key. Its a * A *hash map* allows us to associate a value with a particular key. Its a
particular implementation of the more general data structure called a *map*. particular implementation of the more general data structure called a *map*.
@ -21,5 +21,5 @@ see [the documentation][collections].
[collections]: ../../std/collections/index.html [collections]: ../../std/collections/index.html
Were going to discuss how to create and update vectors, strings, and hash Well discuss how to create and update vectors, strings, and hash maps, as well
maps, as well as what makes each special. as what makes each special.

View File

@ -1,46 +1,55 @@
## Vectors ## Vectors
The first type well look at is `Vec<T>`, also known as a *vector*. Vectors The first collection type well look at is `Vec<T>`, also known as a *vector*.
allow us to store more than one value in a single data structure that puts all Vectors allow us to store more than one value in a single data structure that
the values next to each other in memory. Vectors can only store values of the puts all the values next to each other in memory. Vectors can only store values
same type. They are useful in situations where you have a list of items, such of the same type. They are useful in situations in which you have a list of
as the lines of text in a file or the prices of items in a shopping cart. items, such as the lines of text in a file or the prices of items in a shopping
cart.
### Creating a New Vector ### 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 ```rust
let v: Vec<i32> = Vec::new(); let v: Vec<i32> = Vec::new();
``` ```
Note that we added a type annotation here. Since we arent inserting any values <span class="caption">Listing 8-1: Creating a new, empty vector to hold values
into this vector, Rust doesnt know what kind of elements we intend to store. of type `i32`</span>
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 Note that we added a type annotation here. Because we arent inserting any
using generics, which Chapter 10 will cover how to use in your own types. For values into this vector, Rust doesnt know what kind of elements we intend to
now, all you need to know is that the `Vec` type provided by the standard store. This is an important point. Vectors are implemented using generics;
library can hold any type, and when a specific `Vec` holds a specific type, the well cover how to use generics with your own types in Chapter 10. For now,
type goes within angle brackets. Weve told Rust that the `Vec` in `v` will 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, weve told Rust that the `Vec<T>` in `v` will
hold elements of the `i32` type. hold elements of the `i32` type.
In real code, Rust can infer the type of value we want to store once we insert In more realistic code, Rust can often infer the type of value we want to store
values, so you rarely need to do this type annotation. Its more common to once we insert values, so you rarely need to do this type annotation. Its more
create a `Vec` that has initial values, and Rust provides the `vec!` macro for common to create a `Vec<T>` that has initial values, and Rust provides the
convenience. The macro will create a new `Vec` that holds the values we give `vec!` macro for convenience. The macro will create a new vector that holds the
it. This will create a new `Vec<i32>` that holds the values `1`, `2`, and `3`: values we give it. Listing 8-2 creates a new `Vec<i32>` that holds the values
`1`, `2`, and `3`:
```rust ```rust
let v = vec![1, 2, 3]; let v = vec![1, 2, 3];
``` ```
<span class="caption">Listing 8-2: Creating a new vector containing
values</span>
Because weve given initial `i32` values, Rust can infer that the type of `v` Because weve given initial `i32` values, Rust can infer that the type of `v`
is `Vec<i32>`, and the type annotation isnt necessary. Lets look at how to is `Vec<i32>`, and the type annotation isnt necessary. Next, well look at how
modify a vector next. to modify a vector.
### Updating 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 ```rust
let mut v = Vec::new(); let mut v = Vec::new();
@ -51,14 +60,18 @@ v.push(7);
v.push(8); v.push(8);
``` ```
As with any variable as we discussed in Chapter 3, if we want to be able to <span class="caption">Listing 8-3: Using the `push` method to add values to a
change its value, we need to make it mutable with the `mut` keyword. The 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 numbers we place inside are all of type `i32`, and Rust infers this from the
data, so we dont need the `Vec<i32>` annotation. data, so we dont 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 ```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 } // <- 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 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 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. Lets tackle that next! introduce references to the elements of the vector. Lets tackle that next!
### Reading Elements of Vectors ### 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, weve annotated the types of the value stored in a vector. In the examples, weve annotated the types of the
values that are returned from these functions for extra clarity. 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: indexing syntax or the `get` method:
```rust ```rust
@ -91,17 +107,20 @@ let third: &i32 = &v[2];
let third: Option<&i32> = v.get(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` <span class="caption">Listing 8-5: Using indexing syntax or the `get` method to
to get the third element: vectors are indexed by number, starting at zero. access an item in a vector</span>
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>`.
The reason Rust has two ways to reference an element is so that you can choose Note two details here. First, we use the index value of `2` to get the third
how the program behaves when you try to use an index value that the vector element: vectors are indexed by number, starting at zero. Second, the two
doesnt have an element for. As an example, what should a program do if it has different ways to get the third element are by using `&` and `[]`, which gives
a vector that holds five elements then tries to access an element at index 100 us a reference, or by using the `get` method with the index passed as an
like this: 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 doesnt
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 ```rust,should_panic
let v = vec![1, 2, 3, 4, 5]; 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); let does_not_exist = v.get(100);
``` ```
When you run this, you will find that with the first `[]` method, Rust will <span class="caption">Listing 8-6: Attempting to access the element at index
cause a `panic!` when a non-existent element is referenced. This method would 100 in a vector containing 5 elements</span>
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.
When the `get` method is passed an index that is outside the array, it will When you run this code, the first `[]` method will cause a `panic!` because it
return `None` without panicking. You would use this if accessing an element references a nonexistent element. This method is best used when you want your
beyond the range of the vector will happen occasionally under normal program to consider an attempt to access an element past the end of the vector
circumstances. Your code can then have logic to handle having either to be a fatal error that crashes the program.
`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 When the `get` method is passed an index that is outside the vector, it returns
enter a number thats too large and your program gets a `None` value, you could `None` without panicking. You would use this method if accessing an element
tell the user how many items are in the current `Vec` and give them another beyond the range of the vector happens occasionally under normal circumstances.
chance to enter a valid value. That would be more user-friendly than crashing Your code will then have logic to handle having either `Some(&element)` or
the program for a typo! `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 thats 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 #### Invalid References
Once the program has a valid reference, the borrow checker will enforce the 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 ownership and borrowing rules (covered in Chapter 4) to ensure this reference
any other references to the contents of the vector stay valid. Recall the rule and any other references to the contents of the vector remain valid. Recall the
that says we cant have mutable and immutable references in the same scope. rule that states we cant have mutable and immutable references in the same
That rule applies in this example, where we hold an immutable reference to the scope. That rule applies in Listing 8-7 where we hold an immutable reference to
first element in a vector and try to add an element to the end: the first element in a vector and try to add an element to the end:
```rust,ignore ```rust,ignore
let mut v = vec![1, 2, 3, 4, 5]; let mut v = vec![1, 2, 3, 4, 5];
@ -144,7 +164,10 @@ let first = &v[0];
v.push(6); 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 ```text
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
@ -159,33 +182,67 @@ immutable
| - immutable borrow ends here | - immutable borrow ends here
``` ```
This code might look like it should work: why should a reference to the first The code in Listing 8-7 might look like it should work: why should a reference
element care about what changes about the end of the vector? The reason why to the first element care about what changes at the end of the vector? The
this code isnt allowed is due to the way vectors work. Adding a new element 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 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 isnt enough old elements to the new space if there isnt enough room to put all the
room to put all the elements next to each other where the vector was. In that elements next to each other where the vector was. In that case, the reference
case, the reference to the first element would be pointing to deallocated to the first element would be pointing to deallocated memory. The borrowing
memory. The borrowing rules prevent programs from ending up in that situation. rules prevent programs from ending up in that situation.
> Note: For more on this, see The Nomicon at > Note: For more on the implementation details of the `Vec<T>` type, see “The
*https://doc.rust-lang.org/stable/nomicon/vec.html*. > 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 ### Using an Enum to Store Multiple Types
At the beginning of this chapter, we said that vectors can only store values 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 that are 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 cases for needing to store a list of items of different types. Fortunately, the
variants of an enum are all defined under the same enum type, so when we need variants of an enum are defined under the same enum type, so when we need to
to store elements of a different type in a vector, we can define and use an store elements of a different type in a vector, we can define and use an enum!
enum!
For example, lets say we want to get values from a row in a spreadsheet, where For example, lets 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, 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 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 value types, and then all the enum variants will be considered the same type,
type, that of the enum. Then we can create a vector that holds that enum and that of the enum. Then we can create a vector that holds that enum and so,
so, ultimately, holds different types: ultimately, holds different types. Weve demonstrated this in Listing 8-8:
```rust ```rust
enum SpreadsheetCell { enum SpreadsheetCell {
@ -201,25 +258,24 @@ let row = vec![
]; ];
``` ```
<span class="caption">Listing 8-1: Defining an enum to be able to hold <span class="caption">Listing 8-8: Defining an `enum` to store values of
different types of data in a vector</span> different types in one vector</span>
The reason Rust needs to know exactly what types will be in the vector at The reason Rust needs to know what types will be in the vector at compile time
compile time is so that it knows exactly how much memory on the heap will be is so it knows exactly how much memory on the heap will be needed to store each
needed to store each element. A secondary advantage to this is that we can be element. A secondary advantage is that we can be explicit about what types are
explicit about what types are allowed in this vector. If Rust allowed a vector allowed in this vector. If Rust allowed a vector to hold any type, there would
to hold any type, there would be a chance that one or more of the types would be a chance that one or more of the types would cause errors with the
cause errors with the operations performed on the elements of the vector. Using operations performed on the elements of the vector. Using an enum plus a
an enum plus a `match` means that Rust will ensure at compile time that we `match` expression means that Rust will ensure at compile time that we always
always handle every possible case, as we discussed in Chapter 6. handle every possible case, as discussed in Chapter 6.
If you dont know at the time that youre writing a program the exhaustive set If you dont know when youre writing a program the exhaustive set of types the
of types the program will get at runtime to store in a vector, the enum program will get at runtime to store in a vector, the enum technique wont
technique wont work. Instead, you can use a trait object, which well cover in work. Instead, you can use a trait object, which well cover in Chapter 17.
Chapter 17.
Now that weve gone over some of the most common ways to use vectors, be sure Now that weve discussed 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 to review the API documentation for all the many useful methods defined on
defined on `Vec` by the standard library. For example, in addition to `push` `Vec` by the standard library. For example, in addition to `push`, a `pop`
theres a `pop` method that will remove and return the last element. Lets move method removes and returns the last element. Lets move on to the next
on to the next collection type: `String`! collection type: `String`!

View File

@ -1,61 +1,62 @@
## Strings ## Strings
Weve already talked about strings a bunch in Chapter 4, but lets take a more We talked about strings in Chapter 4, but well look at them in more depth now.
in-depth look at them now. Strings are an area that new Rustaceans commonly get New Rustaceans commonly get stuck on strings due to a combination of three
stuck on. This is due to a combination of three things: Rusts propensity for concepts: Rusts propensity for exposing possible errors, strings being a more
making sure to expose possible errors, strings being a more complicated data complicated data structure than many programmers give them credit for, and
structure than many programmers give them credit for, and UTF-8. These things UTF-8. These concepts combine in a way that can seem difficult when youre
combine in a way that can seem difficult when coming from other languages. 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 implemented as a collection of bytes plus some methods to provide useful
functionality when those bytes are interpreted as text. In this section, well functionality when those bytes are interpreted as text. In this section, well
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. Well also discuss the ways in which `String` creating, updating, and reading. Well also discuss the ways in which `String`
is different than the other collections, namely how indexing into a `String` is is different than the other collections, namely how indexing into a `String` is
complicated by the differences between how people and computers interpret complicated by the differences between how people and computers interpret
`String` data. `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 Well first define what we mean by the term *string*. Rust has only one string
mean by the term *string*. Rust actually only has one string type in the core type in the core language, which is the string slice `str` that is usually seen
language itself: `str`, the string slice, which is usually seen in its borrowed in its borrowed form `&str`. In Chapter 4, we talked about *string slices*,
form, `&str`. We talked about *string slices* in Chapter 4: these are a which are references to some UTF-8 encoded string data stored elsewhere. String
reference to some UTF-8 encoded string data stored elsewhere. String literals, literals, for example, are stored in the binary output of the program and are
for example, are stored in the binary output of the program, and are therefore therefore string slices.
string slices.
The type called `String` is provided in Rusts standard library rather than The `String` type is provided in Rusts standard library rather than coded into
coded into the core language, and is a growable, mutable, owned, UTF-8 encoded the core language and is a growable, mutable, owned, UTF-8 encoded string type.
string type. When Rustaceans talk about “strings” in Rust, they usually mean When Rustaceans refer to “strings” in Rust, they usually mean the `String` and
both the `String` and the string slice `&str` types, not just one of those. the string slice `&str` types, not just one of those types. Although this
This section is largely about `String`, but both these types are used heavily section is largely about `String`, both types are used heavily in Rusts
in Rusts standard library. Both `String` and string slices are UTF-8 encoded. standard library and both `String` and string slices are UTF-8 encoded.
Rusts standard library also includes a number of other string types, such as Rusts 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, more options for storing string data. Similar to the `*String`/`*Str` naming,
they often provide an owned and borrowed variant, just like `String`/`&str`. 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 These string types can store text in different encodings or be represented in
a different way, for example. We wont be talking about these other string memory in a different way, for example. We wont discuss these other string
types in this chapter; see their API documentation for more about how to use types in this chapter; see their API documentation for more about how to use
them and when each is appropriate. them and when each is appropriate.
### Creating a New String ### Creating a New String
Many of the same operations available with `Vec` are available with `String` as 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 ```rust
let mut s = String::new(); 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, well have some initial data that wed like to start the string off This line creates a new empty string called `s` that we can then load data
into. Often, well 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 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 ```rust
let data = "initial contents"; let data = "initial contents";
@ -66,78 +67,104 @@ let s = data.to_string();
let s = "initial contents".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 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 ```rust
let s = String::from("initial contents"); let s = String::from("initial contents");
``` ```
Because strings are used for so many things, there are many different generic <span class="caption">Listing 8-11: Using the `String::from` function to create
APIs that can be used for strings, so there are a lot of options. Some of them a `String` from a string literal</span>
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 Because strings are used for so many things, we can use many different generic
matter of style. 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 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 ```rust
let hello = "السلام عليكم"; let hello = String::from("السلام عليكم");
let hello = "Dobrý den"; let hello = String::from("Dobrý den");
let hello = "Hello"; let hello = String::from("Hello");
let hello = "שָׁלוֹם"; let hello = String::from("שָׁלוֹם");
let hello = "नमस्ते"; let hello = String::from("नमस्ते");
let hello = "こんにちは"; let hello = String::from("こんにちは");
let hello = "안녕하세요"; let hello = String::from("안녕하세요");
let hello = "你好"; let hello = String::from("你好");
let hello = "Olá"; let hello = String::from("Olá");
let hello = "Здравствуйте"; let hello = String::from("Здравствуйте");
let hello = "Hola"; 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 ### Updating a String
A `String` can grow in size and its contents can change just like the contents 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 of a `Vec`, by pushing more data into it. In addition, we can conveniently use
concatenation operations implemented with the `+` operator for convenience. 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 ```rust
let mut s = String::from("foo"); let mut s = String::from("foo");
s.push_str("bar"); 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 dont necessarily want to take ownership of the string slice because we dont necessarily want to take ownership of the
parameter. For example, it would be unfortunate if we werent able to use `s2` parameter. For example, the code in Listing 8-14 shows that it would be
after appending its contents to `s1`: unfortunate if we werent able to use `s2` after appending its contents to `s1`:
```rust ```rust
let mut s1 = String::from("foo"); let mut s1 = String::from("foo");
let s2 = String::from("bar"); let s2 = "bar";
s1.push_str(&s2); s1.push_str(&s2);
println!("s2 is {}", s2);
``` ```
The `push` method is defined to have a single character as a parameter and add <span class="caption">Listing 8-14: Using a string slice after appending its
it to the `String`: contents to a `String`</span>
If the `push_str` method took ownership of `s2`, we wouldnt be able to print
out its value on the last line. However, this code works as wed 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 ```rust
let mut s = String::from("lo"); let mut s = String::from("lo");
s.push('l'); 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, well want to combine two existing strings together. One way is to use #### Concatenation with the `+` Operator or the `format!` Macro
the `+` operator like this:
Often, well want to combine two existing strings. One way is to use the `+`
operator, as shown in Listing 8-16:
```rust ```rust
let s1 = String::from("Hello, "); 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 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 <span class="caption">Listing 8-16: Using the `+` operator to combine two
`s1` is no longer valid after the addition and the reason that we used a `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 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 when we use the `+` operator. The `+` operator uses the `add` method, whose
signature looks something like this: signature looks something like this:
@ -155,32 +185,32 @@ signature looks something like this:
fn add(self, s: &str) -> String { fn add(self, s: &str) -> String {
``` ```
This isnt the exact signature thats in the standard library; there `add` is This isnt the exact signature thats in the standard library: in the standard
defined using generics. Here, were looking at the signature of `add` with library, `add` is defined using generics. Here, were looking at the signature
concrete types substituted for the generic ones, which is what happens when we of `add` with concrete types substituted for the generic ones, which is what
call this method with `String` values. Well be discussing generics in happens when we call this method with `String` values. Well discuss generics
Chapter 10. This signature gives us the clues we need to understand the tricky in Chapter 10. This signature gives us the clues we need to understand the
bits of the `+` operator. tricky bits of the `+` operator.
First of all, `s2` has an `&`, meaning that we are adding a *reference* of the First, `s2` has an `&`, meaning that were adding a *reference* of the second
second string to the first string. This is because of the `s` parameter in the string to the first string because of the `s` parameter in the `add` function:
`add` function: we can only add a `&str` to a `String`, we cant add two we can only add a `&str` to a `String`; we cant add two `String` values
`String` values together. But wait - the type of `&s2` is `&String`, not together. But wait - the type of `&s2` is `&String`, not `&str`, as specified
`&str`, as specified in the second parameter to `add`. Why does our example in the second parameter to `add`. Why does Listing 8-16 compile? We are able to
compile? We are able to use `&s2` in the call to `add` because a `&String` use `&s2` in the call to `add` because the compiler can *coerce* the `&String`
argument can be *coerced* into a `&str` - when the `add` function is called, argument into a `&str`. When we call the `add` method, Rust uses something
Rust uses something called a *deref coercion*, which you could think of here as called a *deref coercion*, which you could think of here as turning `&s2` into
turning `&s2` into `&s2[..]` for use in the `add` function. Well discuss deref `&s2[..]`. Well discuss deref coercion in more depth in Chapter 15. Because
coercion in more depth in Chapter 15. Because `add` does not take ownership of `add` does not take ownership of the `s` parameter, `s2` will still be a valid
the parameter, `s2` will still be a valid `String` after this operation. `String` after this operation.
Second, we can see in the signature that `add` takes ownership of `self`, 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 because `self` does *not* have an `&`. This means `s1` in Listing 8-16 will be
will be moved into the `add` call and no longer be valid after that. So while moved into the `add` call and no longer be valid after that. So although `let
`let s3 = s1 + &s2;` looks like it will copy both strings and create a new one, s3 = s1 + &s2;` looks like it will copy both strings and create a new one, this
this statement actually takes ownership of `s1`, appends a copy of the contents statement actually takes ownership of `s1`, appends a copy of the contents of
of `s2`, then returns ownership of the result. In other words, it looks like `s2`, and then returns ownership of the result. In other words, it looks like
its making a lot of copies, but isnt: the implementation is more efficient its making a lot of copies but isnt: the implementation is more efficient
than copying. than copying.
If we need to concatenate multiple strings, the behavior of `+` gets unwieldy: 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; let s = s1 + "-" + &s2 + "-" + &s3;
``` ```
`s` will be “tic-tac-toe” at this point. With all of the `+` and `"` At this point, `s` will be `tic-tac-toe`. With all of the `+` and `"`
characters, it gets hard to see whats going on. For more complicated string characters, its difficult to see whats going on. For more complicated string
combining, we can use the `format!` macro: combining, we can use the `format!` macro:
```rust ```rust
@ -205,24 +235,27 @@ let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3); let s = format!("{}-{}-{}", s1, s2, s3);
``` ```
This code will also set `s` to “tic-tac-toe”. The `format!` macro works in the This code also sets `s` to `tic-tac-toe`. The `format!` macro works in the same
same way as `println!`, but instead of printing the output to the screen, it way as `println!`, but instead of printing the output to the screen, it returns
returns a `String` with the contents. This version is much easier to read, and a `String` with the contents. The version of the code using `format!` is much
also does not take ownership of any of its parameters. easier to read and also doesnt take ownership of any of its parameters.
### Indexing into Strings ### Indexing into Strings
In many other languages, accessing individual characters in a string by In many other programming languages, accessing individual characters in a
referencing them by index is a valid and common operation. In Rust, however, if string by referencing them by index is a valid and common operation. However,
we try to access parts of a `String` using indexing syntax, well get an error. if we try to access parts of a `String` using indexing syntax in Rust, well
That is, this code: get an error. Consider the code in Listing 8-17:
```rust,ignore ```rust,ignore
let s1 = String::from("hello"); let s1 = String::from("hello");
let h = s1[0]; 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 ```text
error: the trait bound `std::string::String: std::ops::Index<_>` is not 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 `_` note: the type `std::string::String` cannot be indexed by `_`
``` ```
The error and the note tell the story: Rust strings dont support indexing. So The error and the note tell the story: Rust strings dont support indexing. But
the follow-up question is, why not? In order to answer that, we have to talk a why not? To answer that question, we need to discuss how Rust stores strings in
bit about how Rust stores strings in memory. memory.
#### Internal Representation #### Internal Representation
A `String` is a wrapper over a `Vec<u8>`. Lets take a look at some of our A `String` is a wrapper over a `Vec<u8>`. Lets look at some of our properly
properly-encoded UTF-8 example strings from before. First, this one: encoded UTF-8 example strings from Listing 8-12. First, this one:
```rust ```rust
let len = String::from("Hola").len(); let len = String::from("Hola").len();
``` ```
In this case, `len` will be four, which means the `Vec` storing the string 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 “Hola” is four bytes long. Each of these letters takes one byte when encoded in
UTF-8. What about this example, though? UTF-8. But what about the following line?
```rust ```rust
let len = String::from("Здравствуйте").len(); let len = String::from("Здравствуйте").len();
``` ```
A person asked how long the string is might say 12. However, Rusts answer Asked how long the string is, you might say 12. However, Rusts answer is 24:
is 24. This is the number of bytes that it takes to encode “Здравствуйте” in thats the number of bytes it takes to encode “Здравствуйте” in UTF-8, because
UTF-8, since each Unicode scalar value takes two bytes of storage. Therefore, each Unicode scalar value takes two bytes of storage. Therefore, an index into
an index into the strings bytes will not always correlate to a valid Unicode the strings bytes will not always correlate to a valid Unicode scalar value.
scalar value.
To demonstrate, consider this invalid Rust code: To demonstrate, consider this invalid Rust code:
```rust,ignore ```rust,ignore
@ -270,19 +301,20 @@ let answer = &hello[0];
What should the value of `answer` be? Should it be `З`, the first letter? When 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 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 `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 own. Returning `208` is likely not what a user would want if they asked for the
the first letter of this string, but thats the only data that Rust has at byte first letter of this string; however, thats the only data that Rust has at
index 0. Returning the byte value is probably not what people want, even with byte index 0. Returning the byte value is probably not what users want, even if
only Latin letters: `&"hello"[0]` would return `104`, not `h`. To avoid the string contains only Latin letters: if `&"hello"[0]` was valid code that
returning an unexpected value and causing bugs that might not be discovered returned the byte value, it would return `104`, not `h`. To avoid returning an
immediately, Rust chooses to not compile this code at all and prevent unexpected value and causing bugs that might not be discovered immediately,
misunderstandings earlier. Rust doesnt 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 Another point about UTF-8 is that there are actually three relevant ways to
to look at strings, from Rusts perspective: as bytes, scalar values, and look at strings from Rusts perspective: as bytes, scalar values, and grapheme
grapheme clusters (the closest thing to what people would call *letters*). clusters (the closest thing to what we would call *letters*).
If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is 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: 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] 224, 165, 135]
``` ```
Thats 18 bytes, and is how computers ultimately store this data. If we look at Thats 18 bytes and is how computers ultimately store this data. If we look at
them as Unicode scalar values, which are what Rusts `char` type is, those them as Unicode scalar values, which are what Rusts `char` type is, those
bytes look like this: 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:
theyre diacritics that dont make sense on their own. Finally, if we look at theyre diacritics that dont make sense on their own. Finally, if we look at
them as grapheme clusters, wed get what a person would call the four letters them as grapheme clusters, wed get what a person would call the four letters
that make up this word: that make up the Hindi word:
```text ```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 store so that each program can choose the interpretation it needs, no matter
what human language the data is in. 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 doesnt allow us to index into a `String` to get a
character is that indexing operations are expected to always take constant time character is that indexing operations are expected to always take constant time
(O(1)). It isnt possible to guarantee that performance with a `String`, (O(1)). But it isnt possible to guarantee that performance with a `String`,
though, since Rust would have to walk through the contents from the beginning because Rust would have to walk through the contents from the beginning to the
to the index to determine how many valid characters there were. index to determine how many valid characters there were.
### Slicing Strings ### Slicing Strings
Because its not clear what the return type of string indexing should be, and Indexing into a string is often a bad idea because its not clear what the
it is often a bad idea to index into a string, Rust dissuades you from doing so return type of the string indexing operation should be: a byte value, a
by asking you to be more specific if you really need it. The way you can be character, a grapheme cluster, or a string slice. Therefore, Rust asks you to
more specific than indexing using `[]` with a single number is using `[]` with be more specific if you really need to use indices to create string slices. To
a range to create a string slice containing particular bytes: 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 ```rust
let hello = "Здравствуйте"; 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. 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 Earlier, we mentioned that each of these characters was two bytes, which means
means that `s` will be “Зд”. `s` will be `Зд`.
What would happen if we did `&hello[0..1]`? The answer: it will panic at 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: runtime in the same way that accessing an invalid index in a vector does:
```text ```text
thread 'main' panicked at 'index 0 and/or 1 in `Здравствуйте` do not lie on thread 'main' panicked at 'index 0 and/or 1 in `Здравствуйте` do not lie on
character boundary', ../src/libcore/str/mod.rs:1694 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 ### 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 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 “नमस्ते” way to do so is to use the `chars` method. Calling `chars` on “नमस्ते” separates
separates out and returns six values of type `char`, and you can iterate over out and returns six values of type `char`, and you can iterate over the result
the result in order to access each element: in order to access each element:
```rust ```rust
for c in "नमस्ते".chars() { for c in "नमस्ते".chars() {
@ -362,7 +397,7 @@ for c in "नमस्ते".chars() {
} }
``` ```
This code will print: This code will print the following:
```text ```text
@ -392,22 +427,22 @@ This code will print the 18 bytes that make up this `String`, starting with:
// ... etc // ... etc
``` ```
But make sure to remember that valid Unicode scalar values may be made up of But be sure to remember that valid Unicode scalar values may be made up of more
more than one byte. than one byte.
Getting grapheme clusters from strings is complex, so this functionality is not 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 provided by the standard library. Crates are available on
this is the functionality you need. [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 To summarize, strings are complicated. Different programming languages make
different choices about how to present this complexity to the programmer. Rust 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 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 for all Rust programs, which means programmers have to put more thought into
into handling UTF-8 data upfront. This tradeoff exposes more of the complexity handling UTF-8 data upfront. This trade-off exposes more of the complexity of
of strings than other programming languages do, but this will prevent you from strings than other programming languages do but prevents you from having to
having to handle errors involving non-ASCII characters later in your handle errors involving non-ASCII characters later in your development life
development lifecycle. cycle.
Lets switch to something a bit less complex: hash maps! Lets switch to something a bit less complex: hash maps!

View File

@ -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 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 *hashing function*, which determines how it places these keys and values into
memory. Many different programming languages support this kind of data memory. Many different programming languages support this kind of data
structure, but often with a different name: hash, map, object, hash table, or structure, but often use a different name, such as hash, map, object, hash
associative array, just to name a few. 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 Hash maps are useful for when you want to look up data not by an index, as you
index, as you can with vectors, but by using a key that can be of any type. For can with vectors, but by using a key that can be of any type. For example, in a
example, in a game, you could keep track of each teams score in a hash map game, you could keep track of each teams score in a hash map where each key is
where each key is a teams name and the values are each teams score. Given a a teams name and the values are each teams score. Given a team name, you can
team name, you can retrieve their score. retrieve its score.
Well go over the basic API of hash maps in this chapter, but there are many Well go over the basic API of hash maps in this section, but many more goodies
more goodies hiding in the functions defined on `HashMap` by the standard are hiding in the functions defined on `HashMap<K, V>` by the standard library.
library. As always, check the standard library documentation for more As always, check the standard library documentation for more information.
information.
### Creating a New Hash Map ### Creating a New Hash Map
We can create an empty `HashMap` with `new`, and add elements with `insert`. We can create an empty hash map with `new` and add elements with `insert`. In
Here were keeping track of the scores of two teams whose names are Blue and Listing 8-18, were keeping track of the scores of two teams whose names are
Yellow. The Blue team will start with 10 points and the Yellow team starts with Blue and Yellow. The Blue team will start with 10 points, and the Yellow team
50: starts with 50:
```rust ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -34,6 +33,9 @@ scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50); 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 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 the standard library. Of our three common collections, this one is the least
often used, so its not included in the features imported automatically in the often used, so its 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 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 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 `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 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` 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 ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -62,17 +64,20 @@ let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect(); 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 its possible to The type annotation `HashMap<_, _>` is needed here because its possible to
`collect` into many different data structures, and Rust doesnt know which you `collect` into many different data structures, and Rust doesnt know which you
want unless you specify. For the type parameters for the key and value types, 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 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. contains based on the types of the data in the vectors.
### Hash Maps and Ownership ### Hash Maps and Ownership
For types that implement the `Copy` trait, like `i32`, the values are copied 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 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 ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -82,20 +87,25 @@ let field_value = String::from("Blue");
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert(field_name, field_value); 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 <span class="caption">Listing 8-20: Showing that keys and values are owned by
they have been moved into the hash map with the call to `insert`. the hash map once theyre inserted</span>
If we insert references to values into the hash map, the values themselves will We arent able to use the variables `field_name` and `field_value` after
not be moved into the hash map. The values that the references point to must be theyve been moved into the hash map with the call to `insert`.
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. If we insert references to values into the hash map, the values wont 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. Well talk more about these issues in
the “Validating References with Lifetimes” section in Chapter 10.
### Accessing Values in a Hash Map ### 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 ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -109,11 +119,14 @@ let team_name = String::from("Blue");
let score = scores.get(&team_name); 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 thats associated with the Blue team, and the Here, `score` will have the value thats associated with the Blue team, and the
result will be `Some(&10)`. The result is wrapped in `Some` because `get` result will be `Some(&10)`. The result is wrapped in `Some` because `get`
returns an `Option<&V>`; if theres no value for that key in the hash map, `get` returns an `Option<&V>`; if theres no value for that key in the hash map,
will return `None`. The program will need to handle the `Option` in one of the `get` will return `None`. The program will need to handle the `Option` in one
ways that we covered in Chapter 6. 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 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: 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 ```text
Yellow: 50 Yellow: 50
@ -140,22 +153,22 @@ Blue: 10
### Updating a Hash Map ### Updating a Hash Map
While the number of keys and values is growable, each individual key can only Although the number of keys and values is growable, each key can only have one
have one value associated with it at a time. When we want to change the data in value associated with it at a time. When we want to change the data in a hash
a hash map, we have to decide how to handle the case when a key already has a map, we have to decide how to handle the case when a key already has a value
value assigned. We could choose to replace the old value with the new value, assigned. We could replace the old value with the new value, completely
completely disregarding the old value. We could choose to keep the old value disregarding the old value. We could keep the old value and ignore the new
and ignore the new value, and only add the new value if the key *doesnt* value, and only add the new value if the key *doesnt* already have a value. Or
already have a value. Or we could combine the old value and the new value. we could combine the old value and the new value. Lets look at how to do each
Lets look at how to do each of these! of these!
#### Overwriting a Value #### Overwriting a Value
If we insert a key and a value into a hash map, then insert that same key with If we insert a key and a value into a hash map, and then insert that same key
a different value, the value associated with that key will be replaced. Even with a different value, the value associated with that key will be replaced.
though this following code calls `insert` twice, the hash map will only contain Even though the code in Listing 8-22 calls `insert` twice, the hash map will
one key/value pair because were inserting the value for the Blue teams key only contain one key/value pair because were inserting the value for the Blue
both times: teams key both times:
```rust ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -168,18 +181,22 @@ scores.insert(String::from("Blue"), 25);
println!("{:?}", scores); 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 #### Only Insert If the Key Has No Value
Its common to want to check if a particular key has a value and, if it does Its common to check whether a particular key has a value, and if it doesnt,
not, insert a value for it. Hash maps have a special API for this, called insert a value for it. Hash maps have a special API for this called `entry`
`entry`, that takes the key we want to check as an argument. The return value that takes the key we want to check as a parameter. The return value of the
of the `entry` function is an enum, `Entry`, that represents a value that might `entry` function is an enum called `Entry` that represents a value that might
or might not exist. Lets say that we want to check if the key for the Yellow or might not exist. Lets say we want to check whether the key for the Yellow
team has a value associated with it. If it doesnt, we want to insert the value team has a value associated with it. If it doesnt, we want to insert the value
50, and the same for the Blue team. With the entry API, the code for this looks 50, and the same for the Blue team. Using the `entry` API, the code looks like
like: Listing 8-23:
```rust ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -193,23 +210,29 @@ scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores); println!("{:?}", scores);
``` ```
The `or_insert` method on `Entry` returns the value for the corresponding <span class="caption">Listing 8-23: Using the `entry` method to only insert if
`Entry` key if it exists, and if not, inserts its argument as the new value for the key does not already have a value</span>
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.
This code will print `{"Yellow": 50, "Blue": 10}`. The first call to `entry` The `or_insert` method on `Entry` is defined to return the value for the
will insert the key for the Yellow team with the value 50, since the Yellow corresponding `Entry` key if that key exists, and if not, inserts the parameter
team doesnt have a value already. The second call to `entry` will not change as the new value for this key and returns the modified `Entry`. This technique
the hash map since the Blue team already has the value 10. 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 doesnt 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 keys value then update #### Updating a Value Based on the Old Value
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 Another common use case for hash maps is to look up a keys value and then
and increment the value to keep track of how many times weve seen that word. update it based on the old value. For instance, Listing 8-24 shows code that
If this is the first time weve seen a word, well first insert the value `0`. 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 weve
seen that word. If its the first time weve seen a word, well first insert
the value `0`:
```rust ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -226,47 +249,51 @@ for word in text.split_whitespace() {
println!("{:?}", map); println!("{:?}", map);
``` ```
This will print `{"world": 2, "hello": 1, "wonderful": 1}`. The `or_insert` <span class="caption">Listing 8-24: Counting occurrences of words using a hash
method actually returns a mutable reference (`&mut V`) to the value for this map that stores words and counts</span>
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 This code will print `{"world": 2, "hello": 1, "wonderful": 1}`. The
(`*`). The mutable reference goes out of scope at the end of the `for` loop, so `or_insert` method actually returns a mutable reference (`&mut V`) to the value
all of these changes are safe and allowed by the borrowing rules. 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 ### Hashing Function
By default, `HashMap` uses a cryptographically secure hashing function that can By default, `HashMap` uses a cryptographically secure hashing function that can
provide resistance to Denial of Service (DoS) attacks. This is not the fastest 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 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 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 another function by specifying a different *hasher*. A hasher is a type that
implements the `BuildHasher` trait. Well be talking about traits and how to implements the `BuildHasher` trait. Well talk about traits and how to
implement them in Chapter 10. You dont necessarily have to implement your own implement them in Chapter 10. You dont necessarily have to implement your own
hasher from scratch; crates.io has libraries that others have shared that hasher from scratch; [crates.io](https://crates.io) has libraries shared by
provide hashers implementing many common hashing algorithms. other Rust users that provide hashers implementing many common hashing
algorithms.
## Summary ## Summary
Vectors, strings, and hash maps will take you far in programs where you need to Vectors, strings, and hash maps will provide a large amount of functionality
store, access, and modify data. Here are some exercises you should now be that you need in programs where you need to store, access, and modify data.
equipped to solve: 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 * 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 (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. 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 * Convert strings to pig latin. The first consonant of each word is moved to
to the end of the word with an added “ay”, so “first” becomes “irst-fay. the end of the word and “ay” is added, so “first” becomes “irst-fay.” Words
Words that start with a vowel get “hay” added to the end instead (“apple” that start with a vowel have “hay” added to the end instead (“apple” becomes
becomes “apple-hay”). Remember about UTF-8 encoding! “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 * 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 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 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 people in a department or all people in the company by department, sorted
alphabetically. alphabetically.
The standard library API documentation describes methods these types have that The standard library API documentation describes methods that vectors, strings,
will be helpful for these exercises! and hash maps have that will be helpful for these exercises!
Were getting into more complex programs where operations can fail, which means Were getting into more complex programs in which operations can fail; so, its
its a perfect time to go over error handling next! a perfect time to discuss error handling next!

View File

@ -2,22 +2,23 @@
Rusts commitment to reliability extends to error handling. Errors are a fact Rusts 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 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 in which something goes wrong. In many cases, Rust requires you to acknowledge
acknowledge the possibility of an error occurring and take some action before the possibility of an error occurring and take some action before your code
your code will compile. This makes your program more robust by ensuring that will compile. This requirement makes your program more robust by ensuring that
you wont only discover errors after youve deployed your code to production. youll discover errors and handle them appropriately before youve deployed
your code to production!
Rust groups errors into two major categories: *recoverable* and *unrecoverable* Rust groups errors into two major categories: *recoverable* and *unrecoverable*
errors. Recoverable errors are situations when its usually reasonable to errors. Recoverable errors are situations in which its reasonable to report
report the problem to the user and retry the operation, like a file not being the problem to the user and retry the operation, like a file not found error.
found. Unrecoverable errors are always symptoms of bugs, like trying to access Unrecoverable errors are always symptoms of bugs, like trying to access a
a location beyond the end of an array. location beyond the end of an array.
Most languages dont distinguish between the two kinds of errors, and handle Most languages dont distinguish between these two kinds of errors and handle
both in the same way using mechanisms like exceptions. Rust doesnt have both in the same way using mechanisms like exceptions. Rust doesnt have
exceptions. Instead, it has the value `Result<T, E>` for recoverable errors and exceptions. Instead, it has the value `Result<T, E>` for recoverable errors and
the `panic!` macro that stops execution when it encounters unrecoverable the `panic!` macro that stops execution when it encounters unrecoverable
errors. This chapter will cover calling `panic!` first, then talk about errors. This chapter covers calling `panic!` first and then talks about
returning `Result<T, E>` values. Finally, well discuss considerations to take returning `Result<T, E>` values. Additionally, well explore considerations to
into account when deciding whether to try to recover from an error or to stop take into account when deciding whether to try to recover from an error or to
execution. stop execution.

View File

@ -1,30 +1,31 @@
## Unrecoverable Errors with `panic!` ## Unrecoverable Errors with `panic!`
Sometimes, bad things happen, and theres nothing that you can do about it. For Sometimes, bad things happen in your code, and theres nothing you can do about
these cases, Rust has the `panic!` macro. When this macro executes, your it. In these cases, Rust has the `panic!` macro. When the `panic!` macro
program will print a failure message, unwind and clean up the stack, and then executes, your program will print a failure message, unwind and clean up the
quit. The most common situation this occurs in is when a bug of some kind has stack, and then quit. The most common situation this occurs in is when a bug of
been detected and its not clear to the programmer how to handle the error. some kind has been detected, and its 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 > By default, when a `panic!` occurs, the program starts *unwinding*, which
> *unwinding*, which means Rust walks back up the stack and cleans up the data > means Rust walks back up the stack and cleans up the data from each function
> from each function it encounters, but this walking and cleanup is a lot of > it encounters. But this walking back and cleanup is a lot of work. The
> work. The alternative is to immediately *abort*, which ends the program > alternative is to immediately *abort*, which ends the program without
> without cleaning up. Memory that the program was using will then need to be > cleaning up. Memory that the program was using will then need to be cleaned
> cleaned up by the operating system. If in your project you need to make the > up by the operating system. If in your project you need to make the resulting
> resulting binary as small as possible, you can switch from unwinding to > binary as small as possible, you can switch from unwinding to aborting on
> aborting on panic by adding `panic = 'abort'` to the appropriate `[profile]` > panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in
> sections in your *Cargo.toml*. For example, if you want to abort on panic in > your *Cargo.toml* file. For example, if you want to abort on panic in release
> release mode: > mode, add this:
> >
> ```toml > ```toml
> [profile.release] > [profile.release]
> panic = 'abort' > panic = 'abort'
> ``` > ```
Lets try calling `panic!` with a simple program: Lets try calling `panic!` in a simple program:
<span class="filename">Filename: src/main.rs</span> <span class="filename">Filename: src/main.rs</span>
@ -34,7 +35,7 @@ fn main() {
} }
``` ```
If you run it, youll see something like this: When you run the program, youll see something like this:
```text ```text
$ cargo run $ 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) 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 call to `panic!` causes the error message contained in the last three
The first line shows our panic message and the place in our source code where lines. The first line shows our panic message and the place in our source code
the panic occurred: *src/main.rs:2* indicates that its the second line of our where the panic occurred: *src/main.rs:2* indicates that its the second line
*src/main.rs* file. of our *src/main.rs* file.
In this case, the line indicated is part of our code, and if we go to that line In this case, the line indicated is part of our code, and if we go to that
we see the `panic!` macro call. In other cases, the `panic!` call might be in line, we see the `panic!` macro call. In other cases, the `panic!` call might
code that our code calls. The filename and line number reported by the error be in code that our code calls. The filename and line number reported by the
message will be someone elses code where the `panic!` macro is called, not the error message will be someone elses code where the `panic!` macro is called,
line of our code that eventually led to the `panic!`. We can use the backtrace not the line of our code that eventually led to the `panic!` call. We can use
of the functions the `panic!` call came from to figure this out. the backtrace of the functions the `panic!` call came from to figure out the
part of our code that is causing the problem. Well discuss what a backtrace is
in more detail next.
### Using a `panic!` Backtrace ### Using a `panic!` Backtrace
Lets look at another example to see what its like when a `panic!` call comes Lets look at another example to see what its like when a `panic!` call comes
from a library because of a bug in our code instead of from our code calling 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> <span class="filename">Filename: src/main.rs</span>
@ -74,22 +78,25 @@ fn main() {
} }
``` ```
Were attempting to access the hundredth element of our vector, but it only has <span class="caption">Listing 9-1: Attempting to access an element beyond the
three elements. In this situation, Rust will panic. Using `[]` is supposed to end of a vector, which will cause a `panic!`</span>
return an element, but if you pass an invalid index, theres 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 Here, were 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, theres 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 isnt what you want: youll get whatever is at this situation, even though it isnt what you want: youll get whatever is at
the location in memory that would correspond to that element in the vector, the location in memory that would correspond to that element in the vector,
even though the memory doesnt belong to the vector. This is called a *buffer even though the memory doesnt 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 shouldnt be allowed to manipulate the index in such a way as to read data they shouldnt be allowed to
that is stored after the array. that is stored after the array.
In order to protect your program from this sort of vulnerability, if you try to To protect your program from this sort of vulnerability, if you try to read an
read an element at an index that doesnt exist, Rust will stop execution and element at an index that doesnt exist, Rust will stop execution and refuse to
refuse to continue. Lets try it and see: continue. Lets try it and see:
```text ```text
$ cargo run $ 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) error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
``` ```
This points at a file we didnt write, *libcollections/vec.rs*. Thats the This error points at a file we didnt write, *libcollections/vec.rs*. Thats
implementation of `Vec<T>` in the standard library. The code that gets run when the implementation of `Vec<T>` in the standard library. The code that gets run
we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is where when we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is
the `panic!` is actually happening. where the `panic!` is actually happening.
The next note line tells us that we can set the `RUST_BACKTRACE` environment The next note line tells us that we can set the `RUST_BACKTRACE` environment
variable to get a backtrace of exactly what happened to cause the error. Lets variable to get a backtrace of exactly what happened to cause the error. A
try that. Listing 9-1 shows the output: *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. Thats 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 youre using. Lets try getting a backtrace:
Listing 9-2 shows output similar to what youll see:
```text ```text
$ RUST_BACKTRACE=1 cargo run $ RUST_BACKTRACE=1 cargo run
@ -151,29 +165,26 @@ stack backtrace:
17: 0x0 - <unknown> 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> `panic!` displayed when the environment variable `RUST_BACKTRACE` is set</span>
Thats a lot of output! Line 11 of the backtrace points to the line in our Thats a lot of output! The exact output you see might be different depending
project causing the problem: *src/main.rs*, line four. A backtrace is a list of on your operating system and Rust version. In order to get backtraces with this
all the functions that have been called to get to this point. Backtraces in information, debug symbols must be enabled. Debug symbols are enabled by
Rust work like they do in other languages: the key to reading the backtrace is default when using cargo build or cargo run without the --release flag, as we
to start from the top and read until you see files you wrote. Thats the spot have here.
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
youre using.
If we dont want our program to panic, the location pointed to by the first In the output in Listing 9-2, line 11 of the backtrace points to the line in
line mentioning a file we wrote is where we should start investigating in order our project thats causing the problem: *src/main.rs* in line 4. If we dont
to figure out how we got to this location with values that caused the panic. In want our program to panic, the location pointed to by the first line mentioning
our example where we deliberately wrote code that would panic in order to a file we wrote is where we should start investigating to figure out how we got
demonstrate how to use backtraces, the way to fix the panic is to not try to to this location with values that caused the panic. In Listing 9-1 where we
request an element at index 100 from a vector that only contains three items. deliberately wrote code that would panic in order to demonstrate how to use
When your code panics in the future, youll need to figure out for your backtraces, the way to fix the panic is to not request an element at index 100
particular case what action the code is taking with what values that causes the from a vector that only contains three items. When your code panics in the
panic and what the code should do instead. future, youll need to figure out what action the code is taking with what
values that causes the panic and what the code should do instead.
Well come back to `panic!` and when we should and should not use these methods Well come back to `panic!` and when we should and should not use `panic!` to
later in the chapter. Next, well now look at how to recover from an error with handle error conditions later in the chapter. Next, well look at how to
`Result`. recover from an error using `Result`.

View File

@ -6,8 +6,8 @@ interpret and respond to. For example, if we try to open a file and that
operation fails because the file doesnt exist, we might want to create the operation fails because the file doesnt exist, we might want to create the
file instead of terminating the process. file instead of terminating the process.
Recall from Chapter 2 the section on “[Handling Potential Failure with the Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result`
`Result` Type][handle_failure]<!-- ignore -->” that the `Result` enum is defined Type][handle_failure]<!-- ignore --> section that the `Result` enum is defined
as having two variants, `Ok` and `Err`, as follows: as having two variants, `Ok` and `Err`, as follows:
[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type [handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type
@ -19,7 +19,7 @@ enum Result<T, E> {
} }
``` ```
The `T` and `E` are generic type parameters; well go into generics in more The `T` and `E` are generic type parameters: well discuss generics in more
detail in Chapter 10. What you need to know right now is that `T` represents 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` 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 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. value and error value we want to return may differ.
Lets call a function that returns a `Result` value because the function could Lets 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> <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 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 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 dont then we try to compile the code, the compiler will tell us that the types dont
match. The error message will then tell us what the type of `f` *is*! Lets try match. The error message will then tell us what the type of `f` *is*. Lets try
it: we know that the return type of `File::open` isnt of type `u32`, so lets it: we know that the return type of `File::open` isnt of type `u32`, so lets
change the `let f` statement to: change the `let f` statement to this:
```rust,ignore ```rust,ignore
let f: u32 = File::open("hello.txt"); let f: u32 = File::open("hello.txt");
``` ```
Attempting to compile now gives us: Attempting to compile now gives us the following output:
```text ```text
error[E0308]: mismatched types 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 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 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 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 handle or error information. This information is exactly what the `Result` enum
conveys. 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 it fails, the value in `f` will be an instance of `Err` that contains more
information about the kind of error that happened. information about the kind of error that happened.
We need to add to the code from Listing 9-2 to take different actions depending We need to add to the code in Listing 9-3 to take different actions depending
on the value `File::open` returned. Listing 9-3 shows one way to handle the on the value `File::open` returned. Listing 9-4 shows one way to handle the
`Result` with a basic tool: the `match` expression that we learned about in `Result` using a basic tool: the `match` expression that we discussed in
Chapter 6. Chapter 6.
<span class="filename">Filename: src/main.rs</span> <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> `Result` variants we might have</span>
Note that, like the `Option` enum, the `Result` enum and its variants have been 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 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 ### Matching on Different Errors
The code in Listing 9-3 will `panic!` no matter the reason that `File::open` The code in Listing 9-4 will `panic!` no matter the reason that `File::open`
failed. What wed really like to do instead is take different actions for failed. What we want to do instead is take different actions for different
different failure reasons: if `File::open` failed because the file doesnt failure reasons: if `File::open` failed because the file doesnt exist, we want
exist, we want to create the file and return the handle to the new file. If to create the file and return the handle to the new file. If `File::open`
`File::open` failed for any other reason, for example because we didnt have failed for any other reason, for example because we didnt have permission to
permission to open the file, we still want to `panic!` in the same way as we open the file, we still want the code to `panic!` in the same way as it did in
did in Listing 9-3. Lets look at Listing 9-4, which adds another arm to the Listing 9-4. Look at Listing 9-5, which adds another arm to the `match`:
`match`:
<span class="filename">Filename: src/main.rs</span> <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 ```rust,ignore
use std::fs::File; use std::fs::File;
use std::io::ErrorKind; 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> different ways</span>
The type of the value that `File::open` returns inside the `Err` variant is 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. 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 `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` representing the different kinds of errors that might result from an `io`
operation. The variant were interested in is `ErrorKind::NotFound`, which operation. The variant we want to use is `ErrorKind::NotFound`, which indicates
indicates the file were trying to open doesnt exist yet. the file were trying to open doesnt exist yet.
The condition `if error.kind() == ErrorKind::NotFound` is called a *match The condition `if error.kind() == ErrorKind::NotFound` is called a *match
guard*: its an extra condition on a `match` arm that further refines the arms guard*: its an extra condition on a `match` arm that further refines the arms
pattern. This condition must be true in order for that arms code to get run; pattern. This condition must be true for that arms code to be run; otherwise,
otherwise, the pattern matching will move on to consider the next arm in the the pattern matching will move on to consider the next arm in the `match`. The
`match`. The `ref` in the pattern is needed so that `error` is not moved into `ref` in the pattern is needed so `error` is not moved into the guard condition
the guard condition but is merely referenced by it. The reason `ref` is used to but is merely referenced by it. The reason `ref` is used to take a reference in
take a reference in a pattern instead of `&` will be covered in detail in a pattern instead of `&` will be covered in detail in Chapter 18. In short, in
Chapter 18. In short, in the context of a pattern, `&` matches a reference and the context of a pattern, `&` matches a reference and gives us its value, but
gives us its value, but `ref` matches a value and gives us a reference to it. `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 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, 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` 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 could also fail, we need to add an inner `match` statement as well. When the
file cant be opened, a different error message will be printed. The last arm file cant 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 of the outer `match` stays the same so the program panics on any error besides
besides the missing file error. the missing file error.
### Shortcuts for Panic on Error: `unwrap` and `expect` ### Shortcuts for Panic on Error: `unwrap` and `expect`
Using `match` works well enough, but it can be a bit verbose and doesnt always Using `match` works well enough, but it can be a bit verbose and doesnt always
communicate intent well. The `Result<T, E>` type has many helper methods 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 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 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 ```rust,should_panic
use std::fs::File; 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 /stable-dist-rustc/build/src/libcore/result.rs:868
``` ```
Theres another method similar to `unwrap` that lets us also choose the Another method, `expect`, which is similar to `unwrap`, lets us also choose the
`panic!` error message: `expect`. Using `expect` instead of `unwrap` and `panic!` error message. Using `expect` instead of `unwrap` and providing good
providing good error messages can convey your intent and make tracking down the error messages can convey your intent and make tracking down the source of a
source of a panic easier. The syntax of `expect` looks like this: panic easier. The syntax of `expect` looks like this:
<span class="filename">Filename: src/main.rs</span>
```rust,should_panic ```rust,should_panic
use std::fs::File; 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 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 the `panic!` macro. The error message used by `expect` in its call to `panic!`
`panic!` will be the parameter that we pass to `expect` instead of the default will be the parameter that we pass to `expect`, rather than the default
`panic!` message that `unwrap` uses. Heres what it looks like: `panic!` message that `unwrap` uses. Heres what it looks like:
```text ```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 /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 ### Propagating Errors
When writing a function whose implementation calls something that might fail, When youre writing a function whose implementation calls something that might
instead of handling the error within this function, you can choose to let your fail, instead of handling the error within this function, you can return the
caller know about the error so they can decide what to do. This is known as 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 *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 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. 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 doesnt exist or cant be read, this function will return those errors the file doesnt exist or cant be read, this function will return those errors
to the code that called this function: to the code that called this function:
<span class="filename">Filename: src/main.rs</span>
```rust ```rust
use std::io; use std::io;
use std::io::Read; 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> calling code using `match`</span>
Lets look at the return type of the function first: `Result<String, Lets 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 `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 `String`, and the generic type `E` has been filled in with the
concrete type `io::Error`. If this function succeeds without any problems, 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 code that calls this function will receive an `Ok` value that holds a
username that this function read from the file. If this function encounters any `String`—the username that this function read from the file. If this function
problems, the caller of this function will receive an `Err` value that holds an encounters any problems, the code that calls this function will receive an
instance of `io::Error` that contains more information about what the problems `Err` value that holds an instance of `io::Error` that contains more
were. We chose `io::Error` as the return type of this function because that information about what the problems were. We chose `io::Error` as the return
happens to be the type of the error value returned from both of the operations type of this function because that happens to be the type of the error value
were calling in this functions body that might fail: the `File::open` returned from both of the operations were calling in this functions body that
function and the `read_to_string` method. 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 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 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 early from this function and pass the error value from `File::open` back to the
caller as this functions error value. If `File::open` succeeds, we store the calling code as this functions error value. If `File::open` succeeds, we store
file handle in the variable `f` and continue. the file handle in the variable `f` and continue.
Then we create a new `String` in variable `s` and call the `read_to_string` 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 method on the file handle in `f` to read the contents of the file into `s`. The
`s`. The `read_to_string` method also returns a `Result` because it might fail, `read_to_string` method also returns a `Result` because it might fail, even
even though `File::open` succeeded. So we need another `match` to handle that 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 `Result`: if `read_to_string` succeeds, then our function has succeeded, and we
return the username from the file thats now in `s` wrapped in an `Ok`. If return the username from the file thats now in `s` wrapped in an `Ok`. If
`read_to_string` fails, we return the error value in the same way that we `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 returned the error value in the `match` that handled the return value of
`File::open`. We dont need to explicitly say `return`, however, since this is `File::open`. However, we dont need to explicitly say `return`, because this
the last expression in the function. is the last expression in the function.
The code that calls this code will then handle getting either an `Ok` value 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 that contains a username or an `Err` value that contains an `io::Error`. We
dont know what the caller will do with those values. If they get an `Err` dont know what the calling code will do with those values. If the calling code
value, they could choose to call `panic!` and crash their program, use a 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 default username, or look up the username from somewhere other than a file, for
example. We dont have enough information on what the caller is actually trying example. We dont have enough information on what the calling code is actually
to do, so we propagate all the success or error information upwards for them to trying to do, so we propagate all the success or error information upwards for
handle as they see fit. it to handle appropriately.
This pattern of propagating errors is so common in Rust that there is dedicated This pattern of propagating errors is so common in Rust that Rust provides the
syntax to make this easier: `?`. 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 Listing 9-7 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 same functionality as it had in Listing 9-6, but this implementation uses the
question mark operator: question mark operator:
<span class="filename">Filename: src/main.rs</span>
```rust ```rust
use std::io; use std::io;
use std::io::Read; 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> calling code using `?`</span>
The `?` placed after a `Result` value is defined to work the exact same way as The `?` placed after a `Result` value is defined to work in almost the same way
the `match` expressions we defined to handle the `Result` values in Listing 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 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 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 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 function as if we had used the `return` keyword so the error value gets
propagated to the caller. 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 were 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, `?` 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 will return early out of the whole function and give any `Err` value to the
caller. The same thing applies to the `?` at the end of the `read_to_string` calling code. The same thing applies to the `?` at the end of the
call. `read_to_string` call.
The `?` eliminates a lot of boilerplate and makes this functions The `?` eliminates a lot of boilerplate and makes this functions
implementation simpler. We could even shorten this code further by chaining 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 ```rust
use std::io; 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>
Weve moved the creation of the new `String` in `s` to the beginning of the Weve moved the creation of the new `String` in `s` to the beginning of the
function; that part hasnt changed. Instead of creating a variable `f`, weve function; that part hasnt changed. Instead of creating a variable `f`, weve
chained the call to `read_to_string` directly onto the result of 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 `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 `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 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 returning errors. The functionality is again the same as in Listing 9-6 and
Listing 9-6, this is just a different, more ergonomic way to write it. 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`, 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 because it is defined to work in the same way as the `match` expression we
we defined in Listing 9-5. The part of the `match` that requires a return type defined in Listing 9-6. The part of the `match` that requires a return type of
of `Result` is `return Err(e)`, so the return type of the function must be a `Result` is `return Err(e)`, so the return type of the function must be a
`Result` to be compatible with this `return`. `Result` to be compatible with this `return`.
Lets look at what happens if we use `?` in the `main` function, which youll Lets look at what happens if we use `?` in the `main` function, which youll
@ -418,34 +455,28 @@ fn main() {
} }
``` ```
<!-- NOTE: as of 2016-12-21, the error message when calling `?` in a function When we compile this code, we get the following error message:
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:
```text ```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")?; 4 | let f = File::open("hello.txt")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum | ------------------------
`std::result::Result` | |
| cannot use the `?` operator in a function that returns `()`
| in this macro invocation
| |
= note: expected type `()` = help: the trait `std::ops::Try` is not implemented for `()`
= note: found type `std::result::Result<_, _>` = note: required by `std::ops::Try::from_error`
``` ```
This error is pointing out that we have mismatched types: the `main` function This error points out that were only allowed to use the question mark operator
has a return type of `()`, but the `?` might return a `Result`. In functions in a function that returns `Result`. In functions that dont return `Result`,
that dont return `Result`, when you call other functions that return `Result`, when you call other functions that return `Result`, youll need to use a
youll need to use a `match` or one of the `Result` methods to handle it, `match` or one of the `Result` methods to handle it instead of using `?` to
instead of using `?` to potentially propagate the error to the caller. potentially propagate the error to the calling code.
Now that weve discussed the details of calling `panic!` or returning `Result`, Now that weve discussed the details of calling `panic!` or returning `Result`,
lets return to the topic of how to decide which is appropriate to use in which lets return to the topic of how to decide which is appropriate to use in which

View File

@ -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 So how do you decide when you should `panic!` and when you should return
`Result`? When code panics, theres no way to recover. You could choose to call `Result`? When code panics, theres no way to recover. You could call `panic!`
`panic!` for any error situation, whether theres a possible way to recover or for any error situation, whether theres a possible way to recover or not, but
not, but then youre making the decision for your callers that a situation is then youre making the decision on behalf of the code calling your code that a
unrecoverable. When you choose to return a `Result` value, you give your caller situation is unrecoverable. When you choose to return a `Result` value, you
options, rather than making the decision for them. They could choose to attempt give the calling code options rather than making the decision for it. The
to recover in a way thats appropriate for their situation, or they could calling code could choose to attempt to recover in a way thats appropriate for
decide that actually, an `Err` value in this case is unrecoverable, so they can its situation, or it could decide that an `Err` value in this case is
call `panic!` and turn your recoverable error into an unrecoverable one. unrecoverable, so it can call `panic!` and turn your recoverable error into an
Therefore, returning `Result` is a good default choice when youre defining a unrecoverable one. Therefore, returning `Result` is a good default choice when
function that might fail. youre defining a function that might fail.
There are a few situations in which its more appropriate to write code that In a few situations its more appropriate to write code that panics instead of
panics instead of returning a `Result`, but they are less common. Lets discuss returning a `Result`, but they are less common. Lets explore why its
why its appropriate to panic in examples, prototype code, and tests, then appropriate to panic in examples, prototype code, and tests; then in situations
situations where you as a human can know a method wont fail that the compiler where you as a human can know a method wont fail that the compiler cant
cant reason about, and conclude with some general guidelines on how to decide reason about; and conclude with some general guidelines on how to decide
whether to panic in library code. whether to panic in library code.
### Examples, Prototype Code, and Tests: Perfectly Fine to Panic ### Examples, Prototype Code, and Tests Are All Places its Perfectly Fine to Panic
When youre writing an example to illustrate some concept, having robust error When youre writing an example to illustrate some concept, having robust error
handling code in the example as well can make the example less clear. In handling code in the example as well can make the example less clear. In
examples, its understood that a call to a method like `unwrap` that could examples, its understood that a call to a method like `unwrap` that could
`panic!` is meant as a placeholder for the way that youd actually like your `panic!` is meant as a placeholder for the way that youd want your application
application to handle errors, which can differ based on what the rest of your to handle errors, which can differ based on what the rest of your code is doing.
code is doing.
Similarly, the `unwrap` and `expect` methods are very handy when prototyping, Similarly, the `unwrap` and `expect` methods are very handy when prototyping,
before youre ready to decide how to handle errors. They leave clear markers in before youre ready to decide how to handle errors. They leave clear markers in
@ -34,10 +33,10 @@ your code for when youre ready to make your program more robust.
If a method call fails in a test, wed want the whole test to fail, even if If a method call fails in a test, wed want the whole test to fail, even if
that method isnt the functionality under test. Because `panic!` is how a test that method isnt the functionality under test. Because `panic!` is how a test
gets marked as a failure, calling `unwrap` or `expect` is exactly what makes is marked as a failure, calling `unwrap` or `expect` is exactly what should
sense to do. 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 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 isnt that ensures the `Result` will have an `Ok` value, but the logic isnt
@ -45,7 +44,7 @@ something the compiler understands. Youll still have a `Result` value that yo
need to handle: whatever operation youre calling still has the possibility of need to handle: whatever operation youre calling still has the possibility of
failing in general, even though its logically impossible in your particular failing in general, even though its logically impossible in your particular
situation. If you can ensure by manually inspecting the code that youll never situation. If you can ensure by manually inspecting the code that youll never
have an `Err` variant, it is perfectly acceptable to call `unwrap`. Heres an have an `Err` variant, its perfectly acceptable to call `unwrap`. Heres an
example: example:
```rust ```rust
@ -59,62 +58,62 @@ that `127.0.0.1` is a valid IP address, so its acceptable to use `unwrap`
here. However, having a hardcoded, valid string doesnt change the return type here. However, having a hardcoded, valid string doesnt change the return type
of the `parse` method: we still get a `Result` value, and the compiler will 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 still make us handle the `Result` as if the `Err` variant is still a
possibility since the compiler isnt smart enough to see that this string is possibility because the compiler isnt smart enough to see that this string is
always a valid IP address. If the IP address string came from a user instead of always a valid IP address. If the IP address string came from a user rather
being hardcoded into the program, and therefore *did* have a possibility of than being hardcoded into the program, and therefore *did* have a possibility
failure, wed definitely want to handle the `Result` in a more robust way of failure, wed definitely want to handle the `Result` in a more robust way
instead. instead.
### Guidelines for Error Handling ### Guidelines for Error Handling
Its advisable to have your code `panic!` when its possible that you could end Its advisable to have your code `panic!` when its possible that your code
up in a bad state—in this context, bad state is when some assumption, could end up in a bad state. In this context, bad state is when some
guarantee, contract, or invariant has been broken, such as when invalid values, assumption, guarantee, contract, or invariant has been broken, such as when
contradictory values, or missing values are passed to your code—plus one or invalid values, contradictory values, or missing values are passed to your
more of the following: code—plus one or more of the following:
* The bad state is not something thats *expected* to happen occasionally * The bad state is not something thats *expected* to happen occasionally.
* Your code after this point needs to rely on not being in this bad state * Your code after this point needs to rely on not being in this bad state.
* Theres not a good way to encode this information in the types you use * Theres not a good way to encode this information in the types you use.
If someone calls your code and passes in values that dont make sense, the best If someone calls your code and passes in values that dont make sense, the best
thing might be to `panic!` and alert the person using your library to the bug choice 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!` in their code so they can fix it during development. Similarly, `panic!` is
is often appropriate if youre calling external code that is out of your often appropriate if youre calling external code that is out of your control,
control, and it returns an invalid state that you have no way of fixing. and it returns an invalid state that you have no way of fixing.
When a bad state is reached, but its expected to happen no matter how well you When a bad state is reached, but its expected to happen no matter how well you
write your code, its still more appropriate to return a `Result` rather than write your code, its still more appropriate to return a `Result` rather than
calling `panic!`. Examples of this include a parser being given malformed data, making a `panic!` call. Examples of this include a parser being given malformed
or an HTTP request returning a status that indicates you have hit a rate limit. data or an HTTP request returning a status that indicates you have hit a rate
In these cases, you should indicate that failure is an expected possibility by limit. In these cases, you should indicate that failure is an expected
returning a `Result` in order to propagate these bad states upwards so that the possibility by returning a `Result` to propagate these bad states upwards so
caller can decide how they would like to handle the problem. To `panic!` the calling code can decide how to handle the problem. To `panic!` wouldnt be
wouldnt be the best way to handle these cases. the best way to handle these cases.
When your code performs operations on values, your code should verify the When your code performs operations on values, your code should verify the
values are valid first, and `panic!` if the values arent valid. This is mostly values are valid first, and `panic!` if the values arent valid. This is mostly
for safety reasons: attempting to operate on invalid data can expose your code 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 to vulnerabilities. This is the main reason the standard library will `panic!`
`panic!` if you attempt an out-of-bounds array access: trying to access memory if you attempt an out-of-bounds memory access: trying to access memory that
that doesnt belong to the current data structure is a common security problem. doesnt belong to the current data structure is a common security problem.
Functions often have *contracts*: their behavior is only guaranteed if the Functions often have *contracts*: their behavior is only guaranteed if the
inputs meet particular requirements. Panicking when the contract is violated inputs meet particular requirements. Panicking when the contract is violated
makes sense because a contract violation always indicates a caller-side bug, 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 and its not a kind of error you want the calling code to have to explicitly
fact, theres no reasonable way for calling code to recover: the calling handle. In fact, theres no reasonable way for calling code to recover: the
*programmers* need to fix the code. Contracts for a function, especially when a calling *programmers* need to fix the code. Contracts for a function,
violation will cause a panic, should be explained in the API documentation for especially when a violation will cause a panic, should be explained in the API
the function. documentation for the function.
Having lots of error checks in all of your functions would be verbose and However, having lots of error checks in all of your functions would be verbose
annoying, though. Luckily, you can use Rusts type system (and thus the type and annoying. Fortunately, you can use Rusts type system (and thus the type
checking the compiler does) to do a lot of the checks for you. If your function 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 codes logic has a particular type as a parameter, you can proceed with your codes logic
knowing that the compiler has already ensured you have a valid value. For 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 example, if you have a type rather than an `Option`, your program expects to
have *something* rather than *nothing*. Your code then doesnt have to handle have *something* rather than *nothing*. Your code then doesnt 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 wont definitely having a value. Code trying to pass nothing to your function wont
even compile, so your function doesnt have to check for that case at runtime. even compile, so your function doesnt have to check for that case at runtime.
Another example is using an unsigned integer type like `u32`, which ensures the 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 ### Creating Custom Types for Validation
Lets take the idea of using Rusts type system to ensure we have a valid value Lets take the idea of using Rusts type system to ensure we have a valid value
one step further, and look at creating a custom type for validation. Recall the 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 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 users guess was between 1 and 100. We never validated that the users guess was between those
between those numbers before checking it against our secret number, only that numbers before checking it against our secret number; we only validated that
it was positive. In this case, the consequences were not very dire: our output the guess was positive. In this case, the consequences were not very dire: our
of “Too high” or “Too low” would still be correct. It would be a useful 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 enhancement to guide the user toward valid guesses and have different behavior
behavior when a user guesses a number thats out of range versus when a user when a user guesses a number thats out of range versus when a user types, for
types, for example, letters instead. example, letters instead.
One way to do this would be to parse the guess as an `i32` instead of only a 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 `u32` to allow potentially negative numbers, and then add a check for the
being in range: number being in range, like so:
```rust,ignore ```rust,ignore
loop { 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 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 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 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 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 an instance of the type rather than repeating the validations everywhere. That
way, its safe for functions to use the new type in their signatures and way, its 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 `Guess` type that will only create an instance of `Guess` if the `new` function
receives a value between 1 and 100: 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> values between 1 and 100</span>
First, we define a struct named `Guess` that has a field named `value` that 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 Then we implement an associated function named `new` on `Guess` that creates
instances of `Guess` values. The `new` function is defined to have one 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 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. body of the `new` function tests `value` to make sure its between 1 and 100.
If `value` doesnt pass this test, we call `panic!`, which will alert the If `value` doesnt pass this test, we make a `panic!` call, which will alert
programmer who is calling this code that they have a bug they need to fix, the programmer who is writing the calling code that they have a bug they need
since creating a `Guess` with a `value` outside this range would violate the to fix, because creating a `Guess` with a `value` outside this range would
contract that `Guess::new` is relying on. The conditions in which `Guess::new` violate the contract that `Guess::new` is relying on. The conditions in which
might panic should be discussed in its public-facing API documentation; well `Guess::new` might panic should be discussed in its public-facing API
cover documentation conventions around indicating the possibility of a `panic!` documentation; well cover documentation conventions indicating the possibility
in the API documentation that you create in Chapter 14. If `value` does pass of a `panic!` in the API documentation that you create in Chapter 14. If
the test, we create a new `Guess` with its `value` field set to the `value` `value` does pass the test, we create a new `Guess` with its `value` field set
parameter and return the `Guess`. to the `value` parameter and return the `Guess`.
Next, we implement a method named `value` that borrows `self`, doesnt have any Next, we implement a method named `value` that borrows `self`, doesnt have any
other parameters, and returns a `u32`. This is a kind of method sometimes 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 return it. This public method is necessary because the `value` field of the
`Guess` struct is private. Its important that the `value` field is private so `Guess` struct is private. Its important that the `value` field is private so
that code using the `Guess` struct is not allowed to set `value` directly: code using the `Guess` struct is not allowed to set `value` directly: code
callers outside the module *must* use the `Guess::new` function to create an outside the module *must* use the `Guess::new` function to create an instance
instance of `Guess`, which ensures theres no way for a `Guess` to have a of `Guess`, which ensures theres no way for a `Guess` to have a `value` that
`value` that hasnt been checked by the conditions in the `Guess::new` function. hasnt 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 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 then declare in its signature that it takes or returns a `Guess` rather than a
`u32`, and wouldnt need to do any additional checks in its body. `u32` and wouldnt need to do any additional checks in its body.
## Summary ## Summary
Rusts error handling features are designed to help you write more robust code. Rusts error handling features are designed to help you write more robust code.
The `panic!` macro signals that your program is in a state it cant handle, and The `panic!` macro signals that your program is in a state it cant handle and
lets you tell the process to stop instead of trying to proceed with invalid or lets you tell the process to stop instead of trying to proceed with invalid or
incorrect values. The `Result` enum uses Rusts type system to indicate that incorrect values. The `Result` enum uses Rusts type system to indicate that
operations might fail in a way that your code could recover from. You can use 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 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. situations will make your code more reliable in the face of inevitable problems.
Now that weve seen useful ways that the standard library uses generics with Now that youve seen useful ways that the standard library uses generics with
the `Option` and `Result` enums, lets talk about how generics work and how you the `Option` and `Result` enums, well talk about how generics work and how you
can make use of them in your code. can use them in your code in the next chapter.

View File

@ -42,14 +42,14 @@ pub trait Summarizable {
consists of the behavior provided by a `summary` method</span> consists of the behavior provided by a `summary` method</span>
We declare a trait with the `trait` keyword, then the traits name, in this We declare a trait with the `trait` keyword, then the traits name, in this
case `Summarizable`. Inside curly braces we declare the method signatures that case `Summarizable`. Inside curly brackets we declare the method signatures
describe the behaviors that types that implement this trait will need to have, that describe the behaviors that types that implement this trait will need to
in this case `fn summary(&self) -> String`. After the method signature, instead have, in this case `fn summary(&self) -> String`. After the method signature,
of providing an implementation within curly braces, we put a semicolon. Each instead of providing an implementation within curly brackets, we put a
type that implements this trait must then provide its own custom behavior for semicolon. Each type that implements this trait must then provide its own
the body of the method, but the compiler will enforce that any type that has custom behavior for the body of the method, but the compiler will enforce that
the `Summarizable` trait will have the method `summary` defined for it with any type that has the `Summarizable` trait will have the method `summary`
this signature exactly. defined for it with this signature exactly.
A trait can have multiple methods in its body, with the method signatures A trait can have multiple methods in its body, with the method signatures
listed one per line and each line ending in a semicolon. 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 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 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 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 specific behavior that we want the methods of the trait to have for the
particular type. particular type.
@ -187,7 +187,7 @@ behavior. When we implement the trait on a particular type, we can choose to
keep or override each methods default behavior. keep or override each methods default behavior.
Listing 10-15 shows how we could have chosen to specify a default string for 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: define the method signature like we did in Listing 10-12:
<span class="filename">Filename: lib.rs</span> <span class="filename">Filename: lib.rs</span>
@ -240,7 +240,7 @@ implementation.
Default implementations are allowed to call the other methods in the same Default implementations are allowed to call the other methods in the same
trait, even if those other methods dont have a default implementation. In this trait, even if those other methods dont have a default implementation. In this
way, a trait can provide a lot of useful functionality and only require 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 `Summarizable` trait also have an `author_summary` method whose implementation
is required, then a `summary` method that has a default implementation that is required, then a `summary` method that has a default implementation that
calls the `author_summary` method: 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 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 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 order to reduce duplication, but still specify to the compiler exactly what

View File

@ -66,7 +66,7 @@ error: `x` does not live long enough
``` ```
The variable `x` doesnt “live long enough.” Why not? Well, `x` is going to go The variable `x` doesnt “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 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 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 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 correct? I want to leave a note for production, make sure we can make that
clear --> clear -->
<!-- Yes, the inside block for the `'b` lifetime starts with the `let x = 5;` <!-- 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 line and ends with the first closing curly bracket on the 7th line. Do you
the text art comments work or should we make an SVG diagram that has nicer think the text art comments work or should we make an SVG diagram that has
looking arrows and labels? /Carol --> nicer looking arrows and labels? /Carol -->
Weve annotated the lifetime of `r` with `'a` and the lifetime of `x` with Weve 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` `'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. interested to know if rereading Chapter 4 clears up that confusion.
/Carol --> /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. discussion about why these are the arguments we want.
If we try to implement the `longest` function as shown in Listing 10-22, it If we try to implement the `longest` function as shown in Listing 10-22, it

View File

@ -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` 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. Lets put this struct and a `can_hold` method, repeated here in Listing 11-5. Lets put this
code in *src/lib.rs* instead of *src/main.rs* and write some tests for it using code in *src/lib.rs* and write some tests for it using the `assert!` macro.
the `assert!` macro.
<span class="filename">Filename: src/lib.rs</span> <span class="filename">Filename: src/lib.rs</span>
@ -592,7 +591,7 @@ Listing 11-8 shows how wed write a test that checks the error conditions of
<span class="filename">Filename: src/lib.rs</span> <span class="filename">Filename: src/lib.rs</span>
```rust ```rust
struct Guess { pub struct Guess {
value: u32, value: u32,
} }
@ -638,7 +637,7 @@ Looks good! Now lets introduce a bug in our code, by removing the condition
that the `new` function will panic if the value is greater than 100: that the `new` function will panic if the value is greater than 100:
```rust ```rust
# struct Guess { # pub struct Guess {
# value: u32, # 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> <span class="filename">Filename: src/lib.rs</span>
```rust ```rust
struct Guess { pub struct Guess {
value: u32, 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, 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, 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 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! 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