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
Thank you for your interest in contributing to Rust! There are many ways to
contribute, and we appreciate all of them. This document is a bit long, so here's
@ -18,11 +19,12 @@ hop on [#rust-internals][pound-rust-internals].
As a reminder, all contributors are expected to follow our [Code of Conduct][coc].
[pound-rust-internals]: http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals
[pound-rust-internals]: https://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust-internals
[internals]: https://internals.rust-lang.org
[coc]: https://www.rust-lang.org/conduct.html
## Feature Requests
[feature-requests]: #feature-requests
To request a change to the way that the Rust language works, please open an
issue in the [RFCs repository](https://github.com/rust-lang/rfcs/issues/new)
@ -30,6 +32,7 @@ rather than this one. New features and other significant language changes
must go through the RFC process.
## Bug Reports
[bug-reports]: #bug-reports
While bugs are unfortunate, they're a reality in software. We can't fix what we
don't know about, so please report liberally. If you're not sure if something
@ -80,6 +83,7 @@ $ RUST_BACKTRACE=1 rustc ...
```
## The Build System
[the-build-system]: #the-build-system
Rust's build system allows you to bootstrap the compiler, run tests &
benchmarks, generate documentation, install a fresh build of Rust, and more.
@ -94,6 +98,7 @@ system internals, try asking in [`#rust-internals`][pound-rust-internals].
[bootstrap]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/
### Configuration
[configuration]: #configuration
Before you can start building the compiler you need to configure the build for
your system. In most cases, that will just mean using the defaults provided
@ -125,6 +130,11 @@ file. If you still have a `config.mk` file in your directory - from
`./configure` - you may need to delete it for `config.toml` to work.
### Building
[building]: #building
Dependencies
- [build dependencies](README.md#building-from-source)
- `gdb` 6.2.0 minimum, 7.1 or later recommended for test builds
The build system uses the `x.py` script to control the build process. This script
is used to build, test, and document various parts of the compiler. You can
@ -194,6 +204,7 @@ Note: Previously `./configure` and `make` were used to build this project.
They are still available, but `x.py` is the recommended build system.
### Useful commands
[useful-commands]: #useful-commands
Some common invocations of `x.py` are:
@ -232,9 +243,38 @@ Some common invocations of `x.py` are:
guidelines as of yet, but basic rules like 4 spaces for indentation and no
more than 99 characters in a single line should be kept in mind when writing
code.
- `rustup toolchain link <name> build/<host-triple>/<stage>` - Use the custom compiler build via [rustup](https://github.com/rust-lang-nursery/rustup.rs#working-with-custom-toolchains-and-local-builds).
### Using your local build
[using-local-build]: #using-local-build
If you use Rustup to manage your rust install, it has a feature called ["custom
toolchains"][toolchain-link] that you can use to access your newly-built compiler
without having to install it to your system or user PATH. If you've run `python
x.py build`, then you can add your custom rustc to a new toolchain like this:
[toolchain-link]: https://github.com/rust-lang-nursery/rustup.rs#working-with-custom-toolchains-and-local-builds
```
rustup toolchain link <name> build/<host-triple>/stage2
```
Where `<host-triple>` is the build triple for the host (the triple of your
computer, by default), and `<name>` is the name for your custom toolchain. (If you
added `--stage 1` to your build command, the compiler will be in the `stage1`
folder instead.) You'll only need to do this once - it will automatically point
to the latest build you've done.
Once this is set up, you can use your custom toolchain just like any other. For
example, if you've named your toolchain `local`, running `cargo +local build` will
compile a project with your custom rustc, setting `rustup override set local` will
override the toolchain for your current directory, and `cargo +local doc` will use
your custom rustc and rustdoc to generate docs. (If you do this with a `--stage 1`
build, you'll need to build rustdoc specially, since it's not normally built in
stage 1. `python x.py build --stage 1 src/libstd src/tools/rustdoc` will build
rustdoc and libstd, which will allow rustdoc to be run with that toolchain.)
## Pull Requests
[pull-requests]: #pull-requests
Pull requests are the primary mechanism we use to change Rust. GitHub itself
has some [great documentation][pull-requests] on using the Pull Request feature.
@ -298,7 +338,33 @@ Speaking of tests, Rust has a comprehensive test suite. More information about
it can be found
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md).
### External Dependencies
[external-dependencies]: #external-dependencies
Currently building Rust will also build the following external projects:
* [clippy](https://github.com/rust-lang-nursery/rust-clippy)
* [miri](https://github.com/solson/miri)
If your changes break one of these projects, you need to fix them by opening
a pull request against the broken project asking to put the fix on a branch.
Then you can disable the tool building via `src/tools/toolstate.toml`.
Once the branch containing your fix is likely to be merged, you can point
the affected submodule at this branch.
Don't forget to also add your changes with
```
git add path/to/submodule
```
outside the submodule.
It can also be more convenient during development to set `submodules = false`
in the `config.toml` to prevent `x.py` from resetting to the original branch.
## Writing Documentation
[writing-documentation]: #writing-documentation
Documentation improvements are very welcome. The source of `doc.rust-lang.org`
is located in `src/doc` in the tree, and standard API documentation is generated
@ -329,6 +395,7 @@ reference to `doc/reference.html`. The CSS might be messed up, but you can
verify that the HTML is right.
## Issue Triage
[issue-triage]: #issue-triage
Sometimes, an issue will stay open, even though the bug has been fixed. And
sometimes, the original bug may go stale because something has changed in the
@ -347,32 +414,56 @@ labels to triage issues:
* Magenta, **B**-prefixed labels identify bugs which are **blockers**.
* Dark blue, **beta-** labels track changes which need to be backported into
the beta branches.
* Light purple, **C**-prefixed labels represent the **category** of an issue.
* Green, **E**-prefixed labels explain the level of **experience** necessary
to fix the issue.
* The dark blue **final-comment-period** label marks bugs that are using the
RFC signoff functionality of [rfcbot][rfcbot] and are currenty in the final
comment period.
* Red, **I**-prefixed labels indicate the **importance** of the issue. The
[I-nominated][inom] label indicates that an issue has been nominated for
prioritizing at the next triage meeting.
* The purple **metabug** label marks lists of bugs collected by other
categories.
* Purple gray, **O**-prefixed labels are the **operating system** or platform
that this issue is specific to.
* Orange, **P**-prefixed labels indicate a bug's **priority**. These labels
are only assigned during triage meetings, and replace the [I-nominated][inom]
label.
* The gray **proposed-final-comment-period** label marks bugs that are using
the RFC signoff functionality of [rfcbot][rfcbot] and are currently awaiting
signoff of all team members in order to enter the final comment period.
* Pink, **regression**-prefixed labels track regressions from stable to the
release channels.
* The light orange **relnotes** label marks issues that should be documented in
the release notes of the next release.
* Gray, **S**-prefixed labels are used for tracking the **status** of pull
requests.
* Blue, **T**-prefixed bugs denote which **team** the issue belongs to.
* Dark blue, **beta-** labels track changes which need to be backported into
the beta branches.
* The purple **metabug** label marks lists of bugs collected by other
categories.
If you're looking for somewhere to start, check out the [E-easy][eeasy] tag.
[inom]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AI-nominated
[eeasy]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy
[lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc
[rfcbot]: https://github.com/dikaiosune/rust-dashboard/blob/master/RFCBOT.md
## Out-of-tree Contributions
[out-of-tree-contributions]: #out-of-tree-contributions
There are a number of other ways to contribute to Rust that don't deal with
this repository.
@ -392,11 +483,13 @@ valuable!
[community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library
## Helpful Links and Information
[helpful-info]: #helpful-info
For people new to Rust, and just starting to contribute, or even for
more seasoned developers, some useful places to look for information
are:
* [Rust Forge][rustforge] contains additional documentation, including write-ups of how to achieve common tasks
* The [Rust Internals forum][rif], a place to ask questions and
discuss Rust's internals
* The [generated documentation for rust's compiler][gdfrustc]
@ -412,6 +505,7 @@ are:
[gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here
[rif]: http://internals.rust-lang.org
[rr]: https://doc.rust-lang.org/book/README.html
[rustforge]: https://forge.rust-lang.org/
[tlgba]: http://tomlee.co/2014/04/a-more-detailed-tour-of-the-rust-compiler/
[ro]: http://www.rustaceans.org/
[rctd]: ./src/test/COMPILER_TESTS.md

View File

@ -6,6 +6,7 @@ standard library, and documentation.
[Rust]: https://www.rust-lang.org
## Quick Start
[quick-start]: #quick-start
Read ["Installation"] from [The Book].
@ -13,6 +14,7 @@ Read ["Installation"] from [The Book].
[The Book]: https://doc.rust-lang.org/book/index.html
## Building from Source
[building-from-source]: #building-from-source
1. Make sure you have installed the dependencies:
@ -52,6 +54,7 @@ Read ["Installation"] from [The Book].
[Cargo]: https://github.com/rust-lang/cargo
### Building on Windows
[building-on-windows]: #building-on-windows
There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by
Visual Studio, and the GNU ABI used by the GCC toolchain. Which version of Rust
@ -61,6 +64,7 @@ for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU
build.
#### MinGW
[windows-mingw]: #windows-mingw
[MSYS2][msys2] can be used to easily build Rust on Windows:
@ -101,6 +105,7 @@ build.
```
#### MSVC
[windows-msvc]: #windows-msvc
MSVC builds of Rust additionally require an installation of Visual Studio 2013
(or later) so `rustc` can use its linker. Make sure to check the “C++ tools”
@ -124,6 +129,7 @@ python x.py build
```
#### Specifying an ABI
[specifying-an-abi]: #specifying-an-abi
Each specific ABI can also be used from either environment (for example, using
the GNU ABI in powershell) by using an explicit build triple. The available
@ -141,6 +147,7 @@ in Building From Source), and modifying the `build` option under the `[build]`
section.
### Configure and Make
[configure-and-make]: #configure-and-make
While it's not the recommended build system, this project also provides a
configure script and makefile (the latter of which just invokes `x.py`).
@ -155,6 +162,7 @@ When using the configure script, the generated `config.mk` file may override the
`config.mk` file.
## Building Documentation
[building-documentation]: #building-documentation
If 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`.
## Notes
[notes]: #notes
Since the Rust compiler is written in Rust, it must be built by a
precompiled "snapshot" version of itself (made in an earlier state of
@ -192,6 +201,7 @@ There is more advice about hacking on Rust in [CONTRIBUTING.md].
[CONTRIBUTING.md]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md
## Getting Help
[getting-help]: #getting-help
The Rust community congregates in a few places:
@ -204,6 +214,7 @@ The Rust community congregates in a few places:
[users.rust-lang.org]: https://users.rust-lang.org/
## Contributing
[contributing]: #contributing
To contribute to Rust, please see [CONTRIBUTING](CONTRIBUTING.md).
@ -217,6 +228,7 @@ Rust. And a good place to ask for help would be [#rust-beginners].
[#rust-beginners]: irc://irc.mozilla.org/rust-beginners
## License
[license]: #license
Rust is primarily distributed under the terms of both the MIT license
and the Apache License (Version 2.0), with portions covered by various

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)
===========================
Language
--------
- [Associated constants in traits is now stabilised.][42809]
- [Associated constants are now stabilised.][42809]
- [A lot of macro bugs are now fixed.][42913]
Compiler
@ -77,7 +175,7 @@ Stabilized APIs
- [`slice::sort_unstable_by_key`]
- [`slice::sort_unstable_by`]
- [`slice::sort_unstable`]
- [`ste::from_boxed_utf8_unchecked`]
- [`str::from_boxed_utf8_unchecked`]
- [`str::as_bytes_mut`]
- [`str::as_bytes_mut`]
- [`str::from_utf8_mut`]
@ -110,7 +208,7 @@ Compatibility Notes
- [Functions with `'static` in their return types will now not be as usable as
if they were using lifetime parameters instead.][42417]
- [The reimplementation of `{f32, f64}::is_sign_{negative, positive}` now
takes the sign of NaN into account where previously didn't.][42430]
takes the sign of NaN into account where previously didn't.][42430]
[42033]: https://github.com/rust-lang/rust/pull/42033
[42155]: https://github.com/rust-lang/rust/pull/42155
@ -170,7 +268,7 @@ Compatibility Notes
[`slice::sort_unstable_by_key`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by_key
[`slice::sort_unstable_by`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by
[`slice::sort_unstable`]: https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable
[`ste::from_boxed_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_boxed_utf8_unchecked.html
[`str::from_boxed_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_boxed_utf8_unchecked.html
[`str::as_bytes_mut`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes_mut
[`str::from_utf8_mut`]: https://doc.rust-lang.org/std/str/fn.from_utf8_mut.html
[`str::from_utf8_unchecked_mut`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked_mut.html

379
config.toml.example Normal file
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 on Solaris is not a POSIX compatible shell, but /usr/bin/bash is.
if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then
POSIX_SHELL="true"
export POSIX_SHELL
exec /usr/bin/env bash $0 "$@"
fi
unset POSIX_SHELL # clear it so if we invoke other scripts, they run as bash as well
script="$(dirname $0)"/src/bootstrap/configure.py
msg() {
echo "configure: $*"
}
step_msg() {
msg
msg "$1"
msg
}
warn() {
echo "configure: WARNING: $1"
}
err() {
echo "configure: error: $1"
exit 1
}
run() {
msg "$@"
"$@"
}
need_ok() {
if [ $? -ne 0 ]
then
err "$1"
fi
}
need_cmd() {
if command -v $1 >/dev/null 2>&1
then msg "found program '$1'"
else err "program '$1' is missing, please install it"
fi
}
make_dir() {
if [ ! -d $1 ]
then
run mkdir -p $1
fi
}
copy_if_changed() {
if cmp -s $1 $2
then
msg "leaving $2 unchanged"
else
run cp -f $1 $2
chmod u-w $2 # make copied artifact read-only
fi
}
move_if_changed() {
if cmp -s $1 $2
then
msg "leaving $2 unchanged"
else
run mv -f $1 $2
chmod u-w $2 # make moved artifact read-only
fi
}
putvar() {
local T
eval T=\$$1
eval TLEN=\${#$1}
if [ $TLEN -gt 35 ]
then
printf "configure: %-20s := %.35s ...\n" $1 "$T"
else
printf "configure: %-20s := %s %s\n" $1 "$T" "$2"
fi
printf "%-20s := %s\n" $1 "$T" >>config.tmp
}
putpathvar() {
local T
eval T=\$$1
eval TLEN=\${#$1}
if [ $TLEN -gt 35 ]
then
printf "configure: %-20s := %.35s ...\n" $1 "$T"
else
printf "configure: %-20s := %s %s\n" $1 "$T" "$2"
fi
if [ -z "$T" ]
then
printf "%-20s := \n" $1 >>config.tmp
else
printf "%-20s := \"%s\"\n" $1 "$T" >>config.tmp
fi
}
probe() {
local V=$1
try() {
cmd=$1
shift
local P
local T
for P
do
T=$(command -v $P 2>&1)
if [ $? -eq 0 ]
then
VER0=$($P --version 2>/dev/null \
| grep -o '[vV]\?[0-9][0-9.][a-z0-9.-]*' | head -1 )
if [ $? -eq 0 -a "x${VER0}" != "x" ]
then
VER="($VER0)"
else
VER=""
fi
break
else
VER=""
T=""
fi
done
eval $V=\$T
putpathvar $V "$VER"
}
probe_need() {
probe $*
local V=$1
shift
eval VV=\$$V
if [ -z "$VV" ]
then
err "$V needed, but unable to find any of: $*"
T=$($cmd --version 2>/dev/null)
if [ $? -eq 0 ]; then
exec $cmd "$script" "$@"
fi
}
validate_opt () {
for arg in $CFG_CONFIGURE_ARGS
do
isArgValid=0
for option in $BOOL_OPTIONS
do
if test --disable-$option = $arg
then
isArgValid=1
fi
if test --enable-$option = $arg
then
isArgValid=1
fi
done
for option in $VAL_OPTIONS
do
if echo "$arg" | grep -q -- "--$option="
then
isArgValid=1
fi
done
if [ "$arg" = "--help" ]
then
echo
echo "No more help available for Configure options,"
echo "check the Wiki or join our IRC channel"
break
else
if test $isArgValid -eq 0
then
err "Option '$arg' is not recognized"
fi
fi
done
}
# `valopt OPTION_NAME DEFAULT DOC` extracts a string-valued option
# from command line, using provided default value for the option if
# not present, and saves it to the generated config.mk.
#
# `valopt_nosave` is much the same, except that it does not save the
# result to config.mk (instead the script should use `putvar` itself
# later on to save it). `valopt_core` is the core upon which the
# other two are built.
valopt_core() {
VAL_OPTIONS="$VAL_OPTIONS $2"
local SAVE=$1
local OP=$2
local DEFAULT=$3
shift
shift
shift
local DOC="$*"
if [ $HELP -eq 0 ]
then
local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
local V="CFG_${UOP}"
local V_PROVIDED="${V}_PROVIDED"
eval $V="$DEFAULT"
for arg in $CFG_CONFIGURE_ARGS
do
if echo "$arg" | grep -q -- "--$OP="
then
val=$(echo "$arg" | cut -f2 -d=)
eval $V=$val
eval $V_PROVIDED=1
fi
done
if [ "$SAVE" = "save" ]
then
putvar $V
fi
else
if [ -z "$DEFAULT" ]
then
DEFAULT="<none>"
fi
OP="${OP}=[${DEFAULT}]"
printf " --%-30s %s\n" "$OP" "$DOC"
fi
}
valopt_nosave() {
valopt_core nosave "$@"
}
valopt() {
valopt_core save "$@"
}
# `opt OPTION_NAME DEFAULT DOC` extracts a boolean-valued option from
# command line, using the provided default value (0/1) for the option
# if not present, and saves it to the generated config.mk.
#
# `opt_nosave` is much the same, except that it does not save the
# result to config.mk (instead the script should use `putvar` itself
# later on to save it). `opt_core` is the core upon which the other
# two are built.
opt_core() {
BOOL_OPTIONS="$BOOL_OPTIONS $2"
local SAVE=$1
local OP=$2
local DEFAULT=$3
shift
shift
shift
local DOC="$*"
local FLAG=""
if [ $DEFAULT -eq 0 ]
then
FLAG="enable"
DEFAULT_FLAG="disable"
else
FLAG="disable"
DEFAULT_FLAG="enable"
DOC="don't $DOC"
fi
if [ $HELP -eq 0 ]
then
for arg in $CFG_CONFIGURE_ARGS
do
if [ "$arg" = "--${FLAG}-${OP}" ]
then
OP=$(echo $OP | tr 'a-z-' 'A-Z_')
FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
local V="CFG_${FLAG}_${OP}"
local V_PROVIDED="CFG_${FLAG}_${OP}_PROVIDED"
eval $V=1
eval $V_PROVIDED=1
if [ "$SAVE" = "save" ]
then
putvar $V
fi
elif [ "$arg" = "--${DEFAULT_FLAG}-${OP}" ]
then
OP=$(echo $OP | tr 'a-z-' 'A-Z_')
DEFAULT_FLAG=$(echo $DEFAULT_FLAG | tr 'a-z' 'A-Z')
local V_PROVIDED="CFG_${DEFAULT_FLAG}_${OP}_PROVIDED"
eval $V_PROVIDED=1
fi
done
else
if [ -n "$META" ]
then
OP="$OP=<$META>"
fi
printf " --%-30s %s\n" "$FLAG-$OP" "$DOC"
fi
}
opt_nosave() {
opt_core nosave "$@"
}
opt() {
opt_core save "$@"
}
envopt() {
local NAME=$1
local V="CFG_${NAME}"
eval VV=\$$V
# If configure didn't set a value already, then check environment.
#
# (It is recommended that the configure script always check the
# environment before setting any values to envopt variables; see
# e.g. how CFG_CC is handled, where it first checks `-z "$CC"`,
# and issues msg if it ends up employing that provided value.)
if [ -z "$VV" ]
then
eval $V=\$$NAME
eval VV=\$$V
fi
# If script or environment provided a value, save it.
if [ -n "$VV" ]
then
putvar $V
fi
}
enable_if_not_disabled() {
local OP=$1
local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
local ENAB_V="CFG_ENABLE_$UOP"
local EXPLICITLY_DISABLED="CFG_DISABLE_${UOP}_PROVIDED"
eval VV=\$$EXPLICITLY_DISABLED
if [ -z "$VV" ]; then
eval $ENAB_V=1
fi
}
to_gnu_triple() {
case $1 in
i686-pc-windows-gnu) echo i686-w64-mingw32 ;;
x86_64-pc-windows-gnu) echo x86_64-w64-mingw32 ;;
*) echo $1 ;;
esac
}
# Prints the absolute path of a directory to stdout
abs_path() {
local _path="$1"
# Unset CDPATH because it causes havok: it makes the destination unpredictable
# and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null
# for good measure.
(unset CDPATH && cd "$_path" > /dev/null && pwd)
}
HELP=0
for arg; do
case "$arg" in
--help) HELP=1;;
esac
done
msg "looking for configure programs"
need_cmd cmp
need_cmd mkdir
need_cmd printf
need_cmd cut
need_cmd head
need_cmd grep
need_cmd xargs
need_cmd cp
need_cmd find
need_cmd uname
need_cmd date
need_cmd tr
need_cmd sed
need_cmd file
need_cmd make
CFG_SRC_DIR="$(abs_path $(dirname $0))/"
CFG_SRC_DIR_RELATIVE="$(dirname $0)/"
CFG_BUILD_DIR="$(pwd)/"
CFG_SELF="$0"
CFG_CONFIGURE_ARGS="$@"
case "${CFG_SRC_DIR}" in
*\ * )
err "The path to the rust source directory contains spaces, which is not supported"
;;
*)
;;
esac
OPTIONS=""
if [ "$HELP" -eq 1 ]
then
echo
echo "Usage: $CFG_SELF [options]"
echo
echo "Options:"
echo
else
msg "recreating config.tmp"
echo '' >config.tmp
step_msg "processing $CFG_SELF args"
fi
BOOL_OPTIONS=""
VAL_OPTIONS=""
opt debug 0 "debug mode; disables optimization unless \`--enable-optimize\` given"
opt valgrind 0 "run tests with valgrind (memcheck by default)"
opt helgrind 0 "run tests with helgrind instead of memcheck"
opt valgrind-rpass 1 "run rpass-valgrind tests with valgrind"
opt docs 1 "build standard library documentation"
opt compiler-docs 0 "build compiler documentation"
opt optimize-tests 1 "build tests with optimizations"
opt debuginfo-tests 0 "build tests with debugger metadata"
opt quiet-tests 0 "enable quieter output when running tests"
opt libcpp 1 "build llvm with libc++ instead of libstdc++ when using clang"
opt llvm-assertions 0 "build LLVM with assertions"
opt debug-assertions 0 "build with debugging assertions"
opt fast-make 0 "use .gitmodules as timestamp for submodule deps"
opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds"
opt sccache 0 "invoke gcc/clang via sccache to reuse object files between builds"
opt local-rust 0 "use an installed rustc rather than downloading a snapshot"
opt local-rebuild 0 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version"
opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
opt llvm-link-shared 0 "prefer shared linking to LLVM (llvm-config --link-shared)"
opt rpath 1 "build rpaths into rustc itself"
opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0"
# This is used by the automation to produce single-target nightlies
opt dist-host-only 0 "only install bins for the host architecture"
opt inject-std-version 1 "inject the current compiler version of libstd into programs"
opt llvm-version-check 1 "check if the LLVM version is supported, build anyway"
opt codegen-tests 1 "run the src/test/codegen tests"
opt option-checking 1 "complain about unrecognized options in this configure script"
opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)"
opt locked-deps 0 "force Cargo.lock to be up to date"
opt vendor 0 "enable usage of vendored Rust crates"
opt sanitizers 0 "build the sanitizer runtimes (asan, lsan, msan, tsan)"
opt dist-src 1 "when building tarballs enables building a source tarball"
opt cargo-openssl-static 0 "static openssl in cargo"
opt profiler 0 "build the profiler runtime"
# Optimization and debugging options. These may be overridden by the release channel, etc.
opt_nosave optimize 1 "build optimized rust code"
opt_nosave optimize-cxx 1 "build optimized C++ code"
opt_nosave optimize-llvm 1 "build optimized LLVM"
opt_nosave llvm-assertions 0 "build LLVM with assertions"
opt_nosave debug-assertions 0 "build with debugging assertions"
opt_nosave llvm-release-debuginfo 0 "build LLVM with debugger metadata"
opt_nosave debuginfo 0 "build with debugger metadata"
opt_nosave debuginfo-lines 0 "build with line number debugger metadata"
opt_nosave debuginfo-only-std 0 "build only libstd with debugging information"
opt_nosave debug-jemalloc 0 "build jemalloc with --enable-debug --enable-fill"
valopt localstatedir "/var/lib" "local state directory"
valopt sysconfdir "/etc" "install system configuration files"
valopt datadir "${CFG_PREFIX}/share" "install data"
valopt infodir "${CFG_PREFIX}/share/info" "install additional info"
valopt llvm-root "" "set LLVM root"
valopt python "" "set path to python"
valopt jemalloc-root "" "set directory where libjemalloc_pic.a is located"
valopt build "" "GNUs ./configure syntax LLVM build triple"
valopt android-cross-path "" "Android NDK standalone path (deprecated)"
valopt i686-linux-android-ndk "" "i686-linux-android NDK standalone path"
valopt arm-linux-androideabi-ndk "" "arm-linux-androideabi NDK standalone path"
valopt armv7-linux-androideabi-ndk "" "armv7-linux-androideabi NDK standalone path"
valopt aarch64-linux-android-ndk "" "aarch64-linux-android NDK standalone path"
valopt x86_64-linux-android-ndk "" "x86_64-linux-android NDK standalone path"
valopt nacl-cross-path "" "NaCl SDK path (Pepper Canary is recommended). Must be absolute!"
valopt musl-root "/usr/local" "MUSL root installation directory (deprecated)"
valopt musl-root-x86_64 "" "x86_64-unknown-linux-musl install directory"
valopt musl-root-i686 "" "i686-unknown-linux-musl install directory"
valopt musl-root-arm "" "arm-unknown-linux-musleabi install directory"
valopt musl-root-armhf "" "arm-unknown-linux-musleabihf install directory"
valopt musl-root-armv7 "" "armv7-unknown-linux-musleabihf install directory"
valopt extra-filename "" "Additional data that is hashed and passed to the -C extra-filename flag"
valopt qemu-armhf-rootfs "" "rootfs in qemu testing, you probably don't want to use this"
valopt qemu-aarch64-rootfs "" "rootfs in qemu testing, you probably don't want to use this"
valopt experimental-targets "" "experimental LLVM targets to build"
if [ -e ${CFG_SRC_DIR}.git ]
then
valopt release-channel "dev" "the name of the release channel to build"
else
# If we have no git directory then we are probably a tarball distribution
# and should default to stable channel - Issue 28322
probe CFG_GIT git
msg "git: no git directory. Changing default release channel to stable"
valopt release-channel "stable" "the name of the release channel to build"
fi
# Used on systems where "cc" and "ar" are unavailable
valopt default-linker "cc" "the default linker"
valopt default-ar "ar" "the default ar"
# Many of these are saved below during the "writing configuration" step
# (others are conditionally saved).
opt_nosave manage-submodules 1 "let the build manage the git submodules"
opt_nosave clang 0 "prefer clang to gcc for building the runtime"
opt_nosave jemalloc 1 "build liballoc with jemalloc"
opt full-bootstrap 0 "build three compilers instead of two"
opt extended 0 "build an extended rust tool set"
valopt_nosave prefix "/usr/local" "set installation prefix"
valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary"
valopt_nosave host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples"
valopt_nosave target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples"
valopt_nosave mandir "${CFG_PREFIX}/share/man" "install man pages in PATH"
valopt_nosave docdir "${CFG_PREFIX}/share/doc/rust" "install documentation in PATH"
valopt_nosave bindir "${CFG_PREFIX}/bin" "install binaries"
# On Windows this determines root of the subtree for target libraries.
# Host runtime libs always go to 'bin'.
valopt libdir "${CFG_PREFIX}/lib" "install libraries"
case "$CFG_LIBDIR" in
"$CFG_PREFIX"/*) CAT_INC=2;;
"$CFG_PREFIX"*) CAT_INC=1;;
*)
err "libdir must begin with the prefix. Use --prefix to set it accordingly.";;
esac
CFG_LIBDIR_RELATIVE=`echo ${CFG_LIBDIR} | cut -c$((${#CFG_PREFIX}+${CAT_INC}))-`
if [ $HELP -eq 1 ]
then
echo
exit 0
fi
# Validate Options
if [ -z "$CFG_DISABLE_OPTION_CHECKING" ]
then
step_msg "validating $CFG_SELF args"
validate_opt
fi
# Validate the release channel, and configure options
case "$CFG_RELEASE_CHANNEL" in
nightly )
msg "overriding settings for $CFG_RELEASE_CHANNEL"
enable_if_not_disabled llvm-assertions
# FIXME(stage0) re-enable this on the next stage0 now that #35566 is
# fixed
case "$CFG_BUILD" in
*-pc-windows-gnu)
;;
*)
enable_if_not_disabled debuginfo-lines
enable_if_not_disabled debuginfo-only-std
;;
esac
;;
beta | stable)
msg "overriding settings for $CFG_RELEASE_CHANNEL"
case "$CFG_BUILD" in
*-pc-windows-gnu)
;;
*)
enable_if_not_disabled debuginfo-lines
enable_if_not_disabled debuginfo-only-std
;;
esac
;;
dev)
;;
*)
err "release channel must be 'dev', 'nightly', 'beta' or 'stable'"
;;
esac
# Adjust perf and debug options for debug mode
if [ -n "$CFG_ENABLE_DEBUG" ]; then
msg "debug mode enabled, setting performance options"
if [ -z "$CFG_ENABLE_OPTIMIZE_PROVIDED" ]; then
msg "optimization not explicitly enabled, disabling optimization"
CFG_DISABLE_OPTIMIZE=1
CFG_DISABLE_OPTIMIZE_CXX=1
fi
# Set following variables to 1 unless setting already provided
enable_if_not_disabled debug-assertions
enable_if_not_disabled debug-jemalloc
enable_if_not_disabled debuginfo
enable_if_not_disabled llvm-assertions
fi
# OK, now write the debugging options
if [ -n "$CFG_DISABLE_OPTIMIZE" ]; then putvar CFG_DISABLE_OPTIMIZE; fi
if [ -n "$CFG_DISABLE_OPTIMIZE_CXX" ]; then putvar CFG_DISABLE_OPTIMIZE_CXX; fi
if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then putvar CFG_DISABLE_OPTIMIZE_LLVM; fi
if [ -n "$CFG_ENABLE_LLVM_ASSERTIONS" ]; then putvar CFG_ENABLE_LLVM_ASSERTIONS; fi
if [ -n "$CFG_ENABLE_DEBUG_ASSERTIONS" ]; then putvar CFG_ENABLE_DEBUG_ASSERTIONS; fi
if [ -n "$CFG_ENABLE_LLVM_RELEASE_DEBUGINFO" ]; then putvar CFG_ENABLE_LLVM_RELEASE_DEBUGINFO; fi
if [ -n "$CFG_ENABLE_DEBUGINFO" ]; then putvar CFG_ENABLE_DEBUGINFO; fi
if [ -n "$CFG_ENABLE_DEBUGINFO_LINES" ]; then putvar CFG_ENABLE_DEBUGINFO_LINES; fi
if [ -n "$CFG_ENABLE_DEBUGINFO_ONLY_STD" ]; then putvar CFG_ENABLE_DEBUGINFO_ONLY_STD; fi
if [ -n "$CFG_ENABLE_DEBUG_JEMALLOC" ]; then putvar CFG_ENABLE_DEBUG_JEMALLOC; fi
step_msg "looking for build programs"
probe_need CFG_CURL curl
if [ -z "$CFG_PYTHON_PROVIDED" ]; then
probe_need CFG_PYTHON python2.7 python2 python
fi
python_version=$($CFG_PYTHON -V 2>&1)
if [ $(echo $python_version | grep -c '^Python 2\.7') -ne 1 ]; then
err "Found $python_version, but Python 2.7 is required"
fi
# the valgrind rpass tests will fail if you don't have a valgrind, but they're
# only disabled if you opt out.
if [ -z "$CFG_VALGRIND" ]
then
# If the user has explicitly asked for valgrind tests, then fail
if [ -n "$CFG_ENABLE_VALGRIND" ] && [ -n "$CFG_ENABLE_VALGRIND_PROVIDED" ]
then
err "No valgrind present, but valgrind tests explicitly requested"
else
CFG_DISABLE_VALGRIND_RPASS=1
putvar CFG_DISABLE_VALGRIND_RPASS
fi
fi
# Do some sanity checks if running on buildbot
# (these env vars are set by rust-buildbot)
if [ -n "$RUST_DIST_SERVER" -a -n "$ALLOW_NONZERO_RLIMIT_CORE" ]; then
# Frequently the llvm submodule directory is broken by the build
# being killed
llvm_lock="${CFG_SRC_DIR}/.git/modules/src/llvm/index.lock"
if [ -e "$llvm_lock" ]; then
step_msg "removing $llvm_lock"
rm -f "$llvm_lock"
fi
fi
BIN_SUF=
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ]
then
BIN_SUF=.exe
fi
# --enable-local-rebuild implies --enable-local-rust too
if [ -n "$CFG_ENABLE_LOCAL_REBUILD" ]
then
if [ -z "$CFG_ENABLE_LOCAL_RUST" ]
then
CFG_ENABLE_LOCAL_RUST=1
putvar CFG_ENABLE_LOCAL_RUST
fi
fi
if [ -n "$CFG_ENABLE_LOCAL_RUST" ]
then
system_rustc=$(which rustc)
if [ -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
then
: # everything already configured
elif [ -n "$system_rustc" ]
then
# we assume that rustc is in a /bin directory
CFG_LOCAL_RUST_ROOT=${system_rustc%/bin/rustc}
else
err "no local rust to use"
fi
CMD="${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF}"
LRV=`LD_LIBRARY_PATH=${CFG_LOCAL_RUST_ROOT}/lib $CMD --version`
if [ $? -ne 0 ]
then
step_msg "failure while running $CMD --version"
exit 1
fi
step_msg "using rustc at: ${CFG_LOCAL_RUST_ROOT} with version: $LRV"
putvar CFG_LOCAL_RUST_ROOT
fi
# Same with jemalloc. save the setting here.
if [ -n "$CFG_DISABLE_JEMALLOC" ]
then
putvar CFG_DISABLE_JEMALLOC
fi
# All safeguards based on $CFG_ENABLE_CLANG should occur before this
# point in the script; after this point, script logic should inspect
# $CFG_USING_CLANG rather than $CFG_ENABLE_CLANG.
# Set CFG_{CC,CXX,CPP,CFLAGS,CXXFLAGS,LDFLAGS}
envopt CC
envopt CXX
envopt CPP
envopt CFLAGS
envopt CXXFLAGS
envopt LDFLAGS
# a little post-processing of various config values
CFG_PREFIX=${CFG_PREFIX%/}
CFG_MANDIR=${CFG_MANDIR%/}
CFG_DOCDIR=${CFG_DOCDIR%/}
CFG_BINDIR=${CFG_BINDIR%/}
CFG_HOST="$(echo $CFG_HOST | tr ',' ' ')"
CFG_TARGET="$(echo $CFG_TARGET | tr ',' ' ')"
# copy build-triples to host-triples so that builds are a subset of hosts
V_TEMP=""
for i in $CFG_BUILD $CFG_HOST;
do
echo "$V_TEMP" | grep -qF $i || V_TEMP="$V_TEMP${V_TEMP:+ }$i"
done
CFG_HOST=$V_TEMP
# copy host-triples to target-triples so that hosts are a subset of targets
V_TEMP=""
for i in $CFG_HOST $CFG_TARGET;
do
echo "$V_TEMP" | grep -qF $i || V_TEMP="$V_TEMP${V_TEMP:+ }$i"
done
CFG_TARGET=$V_TEMP
step_msg "writing configuration"
putvar CFG_SRC_DIR
putvar CFG_SRC_DIR_RELATIVE
putvar CFG_BUILD_DIR
putvar CFG_OSTYPE
putvar CFG_CPUTYPE
putvar CFG_CONFIGURE_ARGS
putvar CFG_PREFIX
putvar CFG_HOST
putvar CFG_TARGET
putvar CFG_LIBDIR_RELATIVE
putvar CFG_DISABLE_MANAGE_SUBMODULES
putvar CFG_AARCH64_LINUX_ANDROID_NDK
putvar CFG_ARM_LINUX_ANDROIDEABI_NDK
putvar CFG_ARMV7_LINUX_ANDROIDEABI_NDK
putvar CFG_I686_LINUX_ANDROID_NDK
putvar CFG_X86_64_LINUX_ANDROID_NDK
putvar CFG_NACL_CROSS_PATH
putvar CFG_MANDIR
putvar CFG_DOCDIR
putvar CFG_BINDIR
putvar CFG_USING_LIBCPP
msg
copy_if_changed ${CFG_SRC_DIR}src/bootstrap/mk/Makefile.in ./Makefile
move_if_changed config.tmp config.mk
rm -f config.tmp
touch config.stamp
if [ -z "$CFG_ENABLE_DEBUG" ]; then
step_msg "configured in release mode. for development consider --enable-debug"
else
step_msg "complete"
fi
if [ "$CFG_SRC_DIR" = `pwd` ]; then
X_PY=x.py
else
X_PY=${CFG_SRC_DIR_RELATIVE}x.py
fi
msg "run \`python ${X_PY} --help\`"
msg
try python2.7 "$@"
try python27 "$@"
try python2 "$@"
exec python $script "$@"

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

View File

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

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

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)
// allow the `rustc_private` feature to link to other unstable crates
// also in the sysroot.

View File

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

View File

@ -167,6 +167,141 @@ def format_build_time(duration):
return str(datetime.timedelta(seconds=int(duration)))
def default_build_triple():
"""Build triple as in LLVM"""
default_encoding = sys.getdefaultencoding()
try:
ostype = subprocess.check_output(
['uname', '-s']).strip().decode(default_encoding)
cputype = subprocess.check_output(
['uname', '-m']).strip().decode(default_encoding)
except (subprocess.CalledProcessError, OSError):
if sys.platform == 'win32':
return 'x86_64-pc-windows-msvc'
err = "uname not found"
sys.exit(err)
# The goal here is to come up with the same triple as LLVM would,
# at least for the subset of platforms we're willing to target.
ostype_mapper = {
'Bitrig': 'unknown-bitrig',
'Darwin': 'apple-darwin',
'DragonFly': 'unknown-dragonfly',
'FreeBSD': 'unknown-freebsd',
'Haiku': 'unknown-haiku',
'NetBSD': 'unknown-netbsd',
'OpenBSD': 'unknown-openbsd'
}
# Consider the direct transformation first and then the special cases
if ostype in ostype_mapper:
ostype = ostype_mapper[ostype]
elif ostype == 'Linux':
os_from_sp = subprocess.check_output(
['uname', '-o']).strip().decode(default_encoding)
if os_from_sp == 'Android':
ostype = 'linux-android'
else:
ostype = 'unknown-linux-gnu'
elif ostype == 'SunOS':
ostype = 'sun-solaris'
# On Solaris, uname -m will return a machine classification instead
# of a cpu type, so uname -p is recommended instead. However, the
# output from that option is too generic for our purposes (it will
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
# must be used instead.
try:
cputype = subprocess.check_output(
['isainfo', '-k']).strip().decode(default_encoding)
except (subprocess.CalledProcessError, OSError):
err = "isainfo not found"
sys.exit(err)
elif ostype.startswith('MINGW'):
# msys' `uname` does not print gcc configuration, but prints msys
# configuration. so we cannot believe `uname -m`:
# msys1 is always i686 and msys2 is always x86_64.
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
# MINGW64 on x86_64.
ostype = 'pc-windows-gnu'
cputype = 'i686'
if os.environ.get('MSYSTEM') == 'MINGW64':
cputype = 'x86_64'
elif ostype.startswith('MSYS'):
ostype = 'pc-windows-gnu'
elif ostype.startswith('CYGWIN_NT'):
cputype = 'i686'
if ostype.endswith('WOW64'):
cputype = 'x86_64'
ostype = 'pc-windows-gnu'
else:
err = "unknown OS type: {}".format(ostype)
sys.exit(err)
cputype_mapper = {
'BePC': 'i686',
'aarch64': 'aarch64',
'amd64': 'x86_64',
'arm64': 'aarch64',
'i386': 'i686',
'i486': 'i686',
'i686': 'i686',
'i786': 'i686',
'powerpc': 'powerpc',
'powerpc64': 'powerpc64',
'powerpc64le': 'powerpc64le',
'ppc': 'powerpc',
'ppc64': 'powerpc64',
'ppc64le': 'powerpc64le',
's390x': 's390x',
'x64': 'x86_64',
'x86': 'i686',
'x86-64': 'x86_64',
'x86_64': 'x86_64'
}
# Consider the direct transformation first and then the special cases
if cputype in cputype_mapper:
cputype = cputype_mapper[cputype]
elif cputype in {'xscale', 'arm'}:
cputype = 'arm'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
elif cputype == 'armv6l':
cputype = 'arm'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
else:
ostype += 'eabihf'
elif cputype in {'armv7l', 'armv8l'}:
cputype = 'armv7'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
else:
ostype += 'eabihf'
elif cputype == 'mips':
if sys.byteorder == 'big':
cputype = 'mips'
elif sys.byteorder == 'little':
cputype = 'mipsel'
else:
raise ValueError("unknown byteorder: {}".format(sys.byteorder))
elif cputype == 'mips64':
if sys.byteorder == 'big':
cputype = 'mips64'
elif sys.byteorder == 'little':
cputype = 'mips64el'
else:
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
# only the n64 ABI is supported, indicate it
ostype += 'abi64'
elif cputype == 'sparcv9':
pass
else:
err = "unknown cpu type: {}".format(cputype)
sys.exit(err)
return "{}-{}".format(cputype, ostype)
class RustBuild(object):
"""Provide all the methods required to build Rust"""
def __init__(self):
@ -177,7 +312,6 @@ class RustBuild(object):
self.build = ''
self.build_dir = os.path.join(os.getcwd(), "build")
self.clean = False
self.config_mk = ''
self.config_toml = ''
self.printed = False
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
@ -374,26 +508,6 @@ class RustBuild(object):
return self.get_string(value) or value.strip()
return None
def get_mk(self, key):
"""Returns the value of the given key in config.mk, otherwise returns None
>>> rb = RustBuild()
>>> rb.config_mk = 'key := value\\n'
>>> rb.get_mk('key')
'value'
If the key does not exists, the result is None:
>>> rb.get_mk('does_not_exists') == None
True
"""
for line in iter(self.config_mk.splitlines()):
if line.startswith(key + ' '):
var = line[line.find(':=') + 2:].strip()
if var != '':
return var
return None
def cargo(self):
"""Return config path for cargo"""
return self.program_config('cargo')
@ -407,15 +521,9 @@ class RustBuild(object):
>>> rb = RustBuild()
>>> rb.config_toml = 'rustc = "rustc"\\n'
>>> rb.config_mk = 'CFG_LOCAL_RUST_ROOT := /tmp/rust\\n'
>>> rb.program_config('rustc')
'rustc'
>>> cargo_path = rb.program_config('cargo')
>>> cargo_path.rstrip(".exe") == os.path.join("/tmp/rust",
... "bin", "cargo")
True
>>> rb.config_toml = ''
>>> rb.config_mk = ''
>>> cargo_path = rb.program_config('cargo')
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
... "bin", "cargo")
@ -424,10 +532,6 @@ class RustBuild(object):
config = self.get_toml(program)
if config:
return config
config = self.get_mk('CFG_LOCAL_RUST_ROOT')
if config:
return os.path.join(config, "bin", "{}{}".format(
program, self.exe_suffix()))
return os.path.join(self.bin_root(), "bin", "{}{}".format(
program, self.exe_suffix()))
@ -439,10 +543,14 @@ class RustBuild(object):
'devel'
"""
start = line.find('"')
if start == -1:
return None
end = start + 1 + line[start + 1:].find('"')
return line[start + 1:end]
if start != -1:
end = start + 1 + line[start + 1:].find('"')
return line[start + 1:end]
start = line.find('\'')
if start != -1:
end = start + 1 + line[start + 1:].find('\'')
return line[start + 1:end]
return None
@staticmethod
def exe_suffix():
@ -517,162 +625,19 @@ class RustBuild(object):
def build_triple(self):
"""Build triple as in LLVM"""
default_encoding = sys.getdefaultencoding()
config = self.get_toml('build')
if config:
return config
config = self.get_mk('CFG_BUILD')
if config:
return config
try:
ostype = subprocess.check_output(
['uname', '-s']).strip().decode(default_encoding)
cputype = subprocess.check_output(
['uname', '-m']).strip().decode(default_encoding)
except (subprocess.CalledProcessError, OSError):
if sys.platform == 'win32':
return 'x86_64-pc-windows-msvc'
err = "uname not found"
if self.verbose:
raise Exception(err)
sys.exit(err)
# The goal here is to come up with the same triple as LLVM would,
# at least for the subset of platforms we're willing to target.
ostype_mapper = {
'Bitrig': 'unknown-bitrig',
'Darwin': 'apple-darwin',
'DragonFly': 'unknown-dragonfly',
'FreeBSD': 'unknown-freebsd',
'Haiku': 'unknown-haiku',
'NetBSD': 'unknown-netbsd',
'OpenBSD': 'unknown-openbsd'
}
# Consider the direct transformation first and then the special cases
if ostype in ostype_mapper:
ostype = ostype_mapper[ostype]
elif ostype == 'Linux':
os_from_sp = subprocess.check_output(
['uname', '-o']).strip().decode(default_encoding)
if os_from_sp == 'Android':
ostype = 'linux-android'
else:
ostype = 'unknown-linux-gnu'
elif ostype == 'SunOS':
ostype = 'sun-solaris'
# On Solaris, uname -m will return a machine classification instead
# of a cpu type, so uname -p is recommended instead. However, the
# output from that option is too generic for our purposes (it will
# always emit 'i386' on x86/amd64 systems). As such, isainfo -k
# must be used instead.
try:
cputype = subprocess.check_output(
['isainfo', '-k']).strip().decode(default_encoding)
except (subprocess.CalledProcessError, OSError):
err = "isainfo not found"
if self.verbose:
raise Exception(err)
sys.exit(err)
elif ostype.startswith('MINGW'):
# msys' `uname` does not print gcc configuration, but prints msys
# configuration. so we cannot believe `uname -m`:
# msys1 is always i686 and msys2 is always x86_64.
# instead, msys defines $MSYSTEM which is MINGW32 on i686 and
# MINGW64 on x86_64.
ostype = 'pc-windows-gnu'
cputype = 'i686'
if os.environ.get('MSYSTEM') == 'MINGW64':
cputype = 'x86_64'
elif ostype.startswith('MSYS'):
ostype = 'pc-windows-gnu'
elif ostype.startswith('CYGWIN_NT'):
cputype = 'i686'
if ostype.endswith('WOW64'):
cputype = 'x86_64'
ostype = 'pc-windows-gnu'
else:
err = "unknown OS type: {}".format(ostype)
if self.verbose:
raise ValueError(err)
sys.exit(err)
cputype_mapper = {
'BePC': 'i686',
'aarch64': 'aarch64',
'amd64': 'x86_64',
'arm64': 'aarch64',
'i386': 'i686',
'i486': 'i686',
'i686': 'i686',
'i786': 'i686',
'powerpc': 'powerpc',
'powerpc64': 'powerpc64',
'powerpc64le': 'powerpc64le',
'ppc': 'powerpc',
'ppc64': 'powerpc64',
'ppc64le': 'powerpc64le',
's390x': 's390x',
'x64': 'x86_64',
'x86': 'i686',
'x86-64': 'x86_64',
'x86_64': 'x86_64'
}
# Consider the direct transformation first and then the special cases
if cputype in cputype_mapper:
cputype = cputype_mapper[cputype]
elif cputype in {'xscale', 'arm'}:
cputype = 'arm'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
elif cputype == 'armv6l':
cputype = 'arm'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
else:
ostype += 'eabihf'
elif cputype in {'armv7l', 'armv8l'}:
cputype = 'armv7'
if ostype == 'linux-android':
ostype = 'linux-androideabi'
else:
ostype += 'eabihf'
elif cputype == 'mips':
if sys.byteorder == 'big':
cputype = 'mips'
elif sys.byteorder == 'little':
cputype = 'mipsel'
else:
raise ValueError("unknown byteorder: {}".format(sys.byteorder))
elif cputype == 'mips64':
if sys.byteorder == 'big':
cputype = 'mips64'
elif sys.byteorder == 'little':
cputype = 'mips64el'
else:
raise ValueError('unknown byteorder: {}'.format(sys.byteorder))
# only the n64 ABI is supported, indicate it
ostype += 'abi64'
elif cputype == 'sparcv9':
pass
else:
err = "unknown cpu type: {}".format(cputype)
if self.verbose:
raise ValueError(err)
sys.exit(err)
return "{}-{}".format(cputype, ostype)
return default_build_triple()
def update_submodules(self):
"""Update submodules"""
if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \
self.get_toml('submodules') == "false" or \
self.get_mk('CFG_DISABLE_MANAGE_SUBMODULES') == "1":
self.get_toml('submodules') == "false":
return
print('Updating submodules')
default_encoding = sys.getdefaultencoding()
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root)
run(["git", "submodule", "-q", "sync"], cwd=self.rust_root, verbose=self.verbose)
submodules = [s.split(' ', 1)[1] for s in subprocess.check_output(
["git", "config", "--file",
os.path.join(self.rust_root, ".gitmodules"),
@ -680,11 +645,9 @@ class RustBuild(object):
).decode(default_encoding).splitlines()]
submodules = [module for module in submodules
if not ((module.endswith("llvm") and
(self.get_toml('llvm-config') or
self.get_mk('CFG_LLVM_ROOT'))) or
self.get_toml('llvm-config')) or
(module.endswith("jemalloc") and
(self.get_toml('jemalloc') or
self.get_mk('CFG_JEMALLOC_ROOT'))))]
self.get_toml('jemalloc')))]
run(["git", "submodule", "update",
"--init", "--recursive"] + submodules,
cwd=self.rust_root, verbose=self.verbose)
@ -719,11 +682,7 @@ def bootstrap():
try:
with open(args.config or 'config.toml') as config:
build.config_toml = config.read()
except:
pass
try:
build.config_mk = open('config.mk').read()
except:
except (OSError, IOError):
pass
if '\nverbose = 2' in build.config_toml:
@ -731,11 +690,9 @@ def bootstrap():
elif '\nverbose = 1' in build.config_toml:
build.verbose = 1
build.use_vendored_sources = '\nvendor = true' in build.config_toml or \
'CFG_ENABLE_VENDOR' in build.config_mk
build.use_vendored_sources = '\nvendor = true' in build.config_toml
build.use_locked_deps = '\nlocked-deps = true' in build.config_toml or \
'CFG_ENABLE_LOCKED_DEPS' in build.config_mk
build.use_locked_deps = '\nlocked-deps = true' in build.config_toml
if 'SUDO_USER' in os.environ and not build.use_vendored_sources:
if os.environ.get('USER') != os.environ['SUDO_USER']:

View File

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

View File

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

View File

@ -23,7 +23,7 @@
//! 6. "cc"
//!
//! Some of this logic is implemented here, but much of it is farmed out to the
//! `gcc` crate itself, so we end up having the same fallbacks as there.
//! `cc` crate itself, so we end up having the same fallbacks as there.
//! Similar logic is then used to find a C++ compiler, just some s/cc/c++/ is
//! used.
//!
@ -35,7 +35,7 @@ use std::process::Command;
use std::iter;
use build_helper::{cc2ar, output};
use gcc;
use cc;
use Build;
use config::Target;
@ -45,15 +45,15 @@ pub fn find(build: &mut Build) {
// For all targets we're going to need a C compiler for building some shims
// and such as well as for being a linker for Rust code.
for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
let mut cfg = gcc::Config::new();
cfg.cargo_metadata(false).opt_level(0).debug(false)
let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
.target(&target).host(&build.build);
let config = build.config.target_config.get(&target);
if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
cfg.compiler(cc);
} else {
set_compiler(&mut cfg, "gcc", target, config, build);
set_compiler(&mut cfg, Language::C, target, config, build);
}
let compiler = cfg.get_compiler();
@ -67,14 +67,14 @@ pub fn find(build: &mut Build) {
// For all host triples we need to find a C++ compiler as well
for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
let mut cfg = gcc::Config::new();
cfg.cargo_metadata(false).opt_level(0).debug(false).cpp(true)
let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
.target(&host).host(&build.build);
let config = build.config.target_config.get(&host);
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
cfg.compiler(cxx);
} else {
set_compiler(&mut cfg, "g++", host, config, build);
set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
}
let compiler = cfg.get_compiler();
build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
@ -82,8 +82,8 @@ pub fn find(build: &mut Build) {
}
}
fn set_compiler(cfg: &mut gcc::Config,
gnu_compiler: &str,
fn set_compiler(cfg: &mut cc::Build,
compiler: Language,
target: Interned<String>,
config: Option<&Target>,
build: &Build) {
@ -94,7 +94,7 @@ fn set_compiler(cfg: &mut gcc::Config,
t if t.contains("android") => {
if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
let target = target.replace("armv7", "arm");
let compiler = format!("{}-{}", target, gnu_compiler);
let compiler = format!("{}-{}", target, compiler.clang());
cfg.compiler(ndk.join("bin").join(compiler));
}
}
@ -103,6 +103,7 @@ fn set_compiler(cfg: &mut gcc::Config,
// which is a gcc version from ports, if this is the case.
t if t.contains("openbsd") => {
let c = cfg.get_compiler();
let gnu_compiler = compiler.gcc();
if !c.path().ends_with(gnu_compiler) {
return
}
@ -145,3 +146,29 @@ fn set_compiler(cfg: &mut gcc::Config,
_ => {}
}
}
/// The target programming language for a native compiler.
enum Language {
/// The compiler is targeting C.
C,
/// The compiler is targeting C++.
CPlusPlus,
}
impl Language {
/// Obtains the name of a compiler in the GCC collection.
fn gcc(self) -> &'static str {
match self {
Language::C => "gcc",
Language::CPlusPlus => "g++",
}
}
/// Obtains the name of a compiler in the clang suite.
fn clang(self) -> &'static str {
match self {
Language::C => "clang",
Language::CPlusPlus => "clang++",
}
}
}

View File

@ -24,12 +24,12 @@ use Build;
use config::Config;
// The version number
pub const CFG_RELEASE_NUM: &str = "1.21.0";
pub const CFG_RELEASE_NUM: &str = "1.22.1";
// An optional number to put after the label, e.g. '.2' -> '-beta.2'
// Be sure to make this starts with a dot to conform to semver pre-release
// versions (section 9)
pub const CFG_PRERELEASE_VERSION: &str = ".4";
pub const CFG_PRERELEASE_VERSION: &str = ".3";
pub struct GitInfo {
inner: Option<Info>,

View File

@ -23,7 +23,7 @@ use std::path::{PathBuf, Path};
use std::process::Command;
use std::io::Read;
use build_helper::{self, output};
use build_helper::{self, output, BuildExpectation};
use builder::{Kind, RunConfig, ShouldRun, Builder, Compiler, Step};
use cache::{INTERNER, Interned};
@ -33,6 +33,7 @@ use native;
use tool::{self, Tool};
use util::{self, dylib_path, dylib_path_var};
use {Build, Mode};
use toolstate::ToolState;
const ADB_TEST_DIR: &str = "/data/tmp/work";
@ -64,22 +65,26 @@ impl fmt::Display for TestKind {
}
}
fn try_run(build: &Build, cmd: &mut Command) {
fn try_run_expecting(build: &Build, cmd: &mut Command, expect: BuildExpectation) {
if !build.fail_fast {
if !build.try_run(cmd) {
let failures = build.delayed_failures.get();
build.delayed_failures.set(failures + 1);
if !build.try_run(cmd, expect) {
let mut failures = build.delayed_failures.borrow_mut();
failures.push(format!("{:?}", cmd));
}
} else {
build.run(cmd);
build.run_expecting(cmd, expect);
}
}
fn try_run(build: &Build, cmd: &mut Command) {
try_run_expecting(build, cmd, BuildExpectation::None)
}
fn try_run_quiet(build: &Build, cmd: &mut Command) {
if !build.fail_fast {
if !build.try_run_quiet(cmd) {
let failures = build.delayed_failures.get();
build.delayed_failures.set(failures + 1);
let mut failures = build.delayed_failures.borrow_mut();
failures.push(format!("{:?}", cmd));
}
} else {
build.run_quiet(cmd);
@ -241,15 +246,162 @@ impl Step for Rls {
let compiler = builder.compiler(stage, host);
builder.ensure(tool::Rls { compiler, target: self.host });
let mut cargo = tool::prepare_tool_cargo(builder,
compiler,
host,
"test",
"src/tools/rls");
// Don't build tests dynamically, just a pain to work with
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
builder.add_rustc_lib_path(compiler, &mut cargo);
cargo.arg("--").args(&build.config.cmd.test_args());
try_run_expecting(
build,
&mut cargo,
builder.build.config.toolstate.rls.passes(ToolState::Testing),
);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustfmt {
stage: u32,
host: Interned<String>,
}
impl Step for Rustfmt {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/tools/rustfmt")
}
fn make_run(run: RunConfig) {
run.builder.ensure(Rustfmt {
stage: run.builder.top_stage,
host: run.target,
});
}
/// Runs `cargo test` for rustfmt.
fn run(self, builder: &Builder) {
let build = builder.build;
let stage = self.stage;
let host = self.host;
let compiler = builder.compiler(stage, host);
builder.ensure(tool::Rustfmt { compiler, target: self.host });
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
cargo.arg("--manifest-path").arg(build.src.join("src/tools/rls/Cargo.toml"));
cargo.arg("--manifest-path").arg(build.src.join("src/tools/rustfmt/Cargo.toml"));
// Don't build tests dynamically, just a pain to work with
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
builder.add_rustc_lib_path(compiler, &mut cargo);
try_run(build, &mut cargo);
try_run_expecting(
build,
&mut cargo,
builder.build.config.toolstate.rustfmt.passes(ToolState::Testing),
);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Miri {
host: Interned<String>,
}
impl Step for Miri {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let test_miri = run.builder.build.config.test_miri;
run.path("src/tools/miri").default_condition(test_miri)
}
fn make_run(run: RunConfig) {
run.builder.ensure(Miri {
host: run.target,
});
}
/// Runs `cargo test` for miri.
fn run(self, builder: &Builder) {
let build = builder.build;
let host = self.host;
let compiler = builder.compiler(1, host);
let miri = builder.ensure(tool::Miri { compiler, target: self.host });
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
cargo.arg("--manifest-path").arg(build.src.join("src/tools/miri/Cargo.toml"));
// Don't build tests dynamically, just a pain to work with
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
// miri tests need to know about the stage sysroot
cargo.env("MIRI_SYSROOT", builder.sysroot(compiler));
cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler));
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
cargo.env("MIRI_PATH", miri);
builder.add_rustc_lib_path(compiler, &mut cargo);
try_run_expecting(
build,
&mut cargo,
builder.build.config.toolstate.miri.passes(ToolState::Testing),
);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Clippy {
host: Interned<String>,
}
impl Step for Clippy {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = false;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/tools/clippy")
}
fn make_run(run: RunConfig) {
run.builder.ensure(Clippy {
host: run.target,
});
}
/// Runs `cargo test` for clippy.
fn run(self, builder: &Builder) {
let build = builder.build;
let host = self.host;
let compiler = builder.compiler(1, host);
let _clippy = builder.ensure(tool::Clippy { compiler, target: self.host });
let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml"));
// Don't build tests dynamically, just a pain to work with
cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
// clippy tests need to know about the stage sysroot
cargo.env("SYSROOT", builder.sysroot(compiler));
builder.add_rustc_lib_path(compiler, &mut cargo);
try_run_expecting(
build,
&mut cargo,
builder.build.config.toolstate.clippy.passes(ToolState::Testing),
);
}
}
@ -900,7 +1052,6 @@ impl Step for CrateLibrustc {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Crate {
compiler: Compiler,
@ -1080,6 +1231,75 @@ impl Step for Crate {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustdoc {
host: Interned<String>,
test_kind: TestKind,
}
impl Step for Rustdoc {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/librustdoc").path("src/tools/rustdoc")
}
fn make_run(run: RunConfig) {
let builder = run.builder;
let test_kind = if builder.kind == Kind::Test {
TestKind::Test
} else if builder.kind == Kind::Bench {
TestKind::Bench
} else {
panic!("unexpected builder.kind in crate: {:?}", builder.kind);
};
builder.ensure(Rustdoc {
host: run.host,
test_kind,
});
}
fn run(self, builder: &Builder) {
let build = builder.build;
let test_kind = self.test_kind;
let compiler = builder.compiler(builder.top_stage, self.host);
let target = compiler.host;
let mut cargo = tool::prepare_tool_cargo(builder,
compiler,
target,
test_kind.subcommand(),
"src/tools/rustdoc");
let _folder = build.fold_output(|| {
format!("{}_stage{}-rustdoc", test_kind.subcommand(), compiler.stage)
});
println!("{} rustdoc stage{} ({} -> {})", test_kind, compiler.stage,
&compiler.host, target);
if test_kind.subcommand() == "test" && !build.fail_fast {
cargo.arg("--no-fail-fast");
}
cargo.arg("-p").arg("rustdoc:0.0.0");
cargo.arg("--");
cargo.args(&build.config.cmd.test_args());
if build.config.quiet_tests {
cargo.arg("--quiet");
}
let _time = util::timeit();
try_run(build, &mut cargo);
}
}
fn envify(s: &str) -> String {
s.chars().map(|c| {
match c {

View File

@ -13,7 +13,7 @@
//! Responsible for cleaning out a build directory of all old and stale
//! artifacts to prepare for a fresh build. Currently doesn't remove the
//! `build/cache` directory (download cache) or the `build/$target/llvm`
//! directory as we want that cached between builds.
//! directory unless the --all flag is present.
use std::fs;
use std::io::{self, ErrorKind};
@ -21,24 +21,29 @@ use std::path::Path;
use Build;
pub fn clean(build: &Build) {
pub fn clean(build: &Build, all: bool) {
rm_rf("tmp".as_ref());
rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist"));
for host in &build.hosts {
let entries = match build.out.join(host).read_dir() {
Ok(iter) => iter,
Err(_) => continue,
};
if all {
rm_rf(&build.out);
} else {
rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist"));
for entry in entries {
let entry = t!(entry);
if entry.file_name().to_str() == Some("llvm") {
continue
for host in &build.hosts {
let entries = match build.out.join(host).read_dir() {
Ok(iter) => iter,
Err(_) => continue,
};
for entry in entries {
let entry = t!(entry);
if entry.file_name().to_str() == Some("llvm") {
continue
}
let path = t!(entry.path().canonicalize());
rm_rf(&path);
}
let path = t!(entry.path().canonicalize());
rm_rf(&path);
}
}
}

View File

@ -10,12 +10,12 @@
//! Serialized configuration of a build.
//!
//! This module implements parsing `config.mk` and `config.toml` configuration
//! files to tweak how the build runs.
//! This module implements parsing `config.toml` configuration files to tweak
//! how the build runs.
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process;
@ -23,10 +23,11 @@ use std::cmp;
use num_cpus;
use toml;
use util::{exe, push_exe_path};
use util::exe;
use cache::{INTERNER, Interned};
use flags::Flags;
pub use flags::Subcommand;
use toolstate::ToolStates;
/// Global configuration for the entire build and/or bootstrap.
///
@ -111,6 +112,7 @@ pub struct Config {
pub low_priority: bool,
pub channel: String,
pub quiet_tests: bool,
pub test_miri: bool,
// Fallback musl-root for all targets
pub musl_root: Option<PathBuf>,
pub prefix: Option<PathBuf>,
@ -124,14 +126,14 @@ pub struct Config {
pub nodejs: Option<PathBuf>,
pub gdb: Option<PathBuf>,
pub python: Option<PathBuf>,
pub configure_args: Vec<String>,
pub openssl_static: bool,
pub configure_args: Vec<String>,
// These are either the stage0 downloaded binaries or the locally installed ones.
pub initial_cargo: PathBuf,
pub initial_rustc: PathBuf,
pub toolstate: ToolStates,
}
/// Per-target configuration stored in the global configuration structure.
@ -190,6 +192,8 @@ struct Build {
sanitizers: Option<bool>,
profiler: Option<bool>,
openssl_static: Option<bool>,
configure_args: Option<Vec<String>>,
local_rebuild: Option<bool>,
}
/// TOML representation of various global install decisions.
@ -219,6 +223,7 @@ struct Llvm {
targets: Option<String>,
experimental_targets: Option<String>,
link_jobs: Option<u32>,
link_shared: Option<bool>,
}
#[derive(Deserialize, Default, Clone)]
@ -265,6 +270,10 @@ struct Rust {
debuginfo_tests: Option<bool>,
codegen_tests: Option<bool>,
ignore_git: Option<bool>,
debug: Option<bool>,
dist_src: Option<bool>,
quiet_tests: Option<bool>,
test_miri: Option<bool>,
}
/// TOML representation of how each build target is configured.
@ -300,6 +309,7 @@ impl Config {
config.codegen_tests = true;
config.ignore_git = false;
config.rust_dist_src = true;
config.test_miri = false;
config.on_fail = flags.on_fail;
config.stage = flags.stage;
@ -326,6 +336,18 @@ impl Config {
}
}).unwrap_or_else(|| TomlConfig::default());
let toolstate_toml_path = config.src.join("src/tools/toolstate.toml");
let parse_toolstate = || -> Result<_, Box<::std::error::Error>> {
let mut f = File::open(toolstate_toml_path)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
Ok(toml::from_str(&contents)?)
};
config.toolstate = parse_toolstate().unwrap_or_else(|err| {
println!("failed to parse TOML configuration 'toolstate.toml': {}", err);
process::exit(2);
});
let build = toml.build.clone().unwrap_or(Build::default());
set(&mut config.build, build.build.clone().map(|x| INTERNER.intern_string(x)));
set(&mut config.build, flags.build);
@ -374,6 +396,8 @@ impl Config {
set(&mut config.sanitizers, build.sanitizers);
set(&mut config.profiler, build.profiler);
set(&mut config.openssl_static, build.openssl_static);
set(&mut config.configure_args, build.configure_args);
set(&mut config.local_rebuild, build.local_rebuild);
config.verbose = cmp::max(config.verbose, flags.verbose);
if let Some(ref install) = toml.install {
@ -385,6 +409,18 @@ impl Config {
config.mandir = install.mandir.clone().map(PathBuf::from);
}
// Store off these values as options because if they're not provided
// we'll infer default values for them later
let mut llvm_assertions = None;
let mut debuginfo_lines = None;
let mut debuginfo_only_std = None;
let mut debug = None;
let mut debug_jemalloc = None;
let mut debuginfo = None;
let mut debug_assertions = None;
let mut optimize = None;
let mut ignore_git = None;
if let Some(ref llvm) = toml.llvm {
match llvm.ccache {
Some(StringOrBool::String(ref s)) => {
@ -397,31 +433,36 @@ impl Config {
}
set(&mut config.ninja, llvm.ninja);
set(&mut config.llvm_enabled, llvm.enabled);
set(&mut config.llvm_assertions, llvm.assertions);
llvm_assertions = llvm.assertions;
set(&mut config.llvm_optimize, llvm.optimize);
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
set(&mut config.llvm_link_shared, llvm.link_shared);
config.llvm_targets = llvm.targets.clone();
config.llvm_experimental_targets = llvm.experimental_targets.clone();
config.llvm_link_jobs = llvm.link_jobs;
}
if let Some(ref rust) = toml.rust {
set(&mut config.rust_debug_assertions, rust.debug_assertions);
set(&mut config.rust_debuginfo, rust.debuginfo);
set(&mut config.rust_debuginfo_lines, rust.debuginfo_lines);
set(&mut config.rust_debuginfo_only_std, rust.debuginfo_only_std);
set(&mut config.rust_optimize, rust.optimize);
debug = rust.debug;
debug_assertions = rust.debug_assertions;
debuginfo = rust.debuginfo;
debuginfo_lines = rust.debuginfo_lines;
debuginfo_only_std = rust.debuginfo_only_std;
optimize = rust.optimize;
ignore_git = rust.ignore_git;
debug_jemalloc = rust.debug_jemalloc;
set(&mut config.rust_optimize_tests, rust.optimize_tests);
set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
set(&mut config.codegen_tests, rust.codegen_tests);
set(&mut config.rust_rpath, rust.rpath);
set(&mut config.debug_jemalloc, rust.debug_jemalloc);
set(&mut config.use_jemalloc, rust.use_jemalloc);
set(&mut config.backtrace, rust.backtrace);
set(&mut config.channel, rust.channel.clone());
set(&mut config.ignore_git, rust.ignore_git);
set(&mut config.rust_dist_src, rust.dist_src);
set(&mut config.quiet_tests, rust.quiet_tests);
set(&mut config.test_miri, rust.test_miri);
config.rustc_default_linker = rust.default_linker.clone();
config.rustc_default_ar = rust.default_ar.clone();
config.musl_root = rust.musl_root.clone().map(PathBuf::from);
@ -476,226 +517,31 @@ impl Config {
None => stage0_root.join(exe("cargo", &config.build)),
};
// compat with `./configure` while we're still using that
if fs::metadata("config.mk").is_ok() {
config.update_with_config_mk();
}
// Now that we've reached the end of our configuration, infer the
// default values for all options that we haven't otherwise stored yet.
let default = config.channel == "nightly";
config.llvm_assertions = llvm_assertions.unwrap_or(default);
let default = match &config.channel[..] {
"stable" | "beta" | "nightly" => true,
_ => false,
};
config.rust_debuginfo_lines = debuginfo_lines.unwrap_or(default);
config.rust_debuginfo_only_std = debuginfo_only_std.unwrap_or(default);
let default = debug == Some(true);
config.debug_jemalloc = debug_jemalloc.unwrap_or(default);
config.rust_debuginfo = debuginfo.unwrap_or(default);
config.rust_debug_assertions = debug_assertions.unwrap_or(default);
config.rust_optimize = optimize.unwrap_or(!default);
let default = config.channel == "dev";
config.ignore_git = ignore_git.unwrap_or(default);
config
}
/// "Temporary" routine to parse `config.mk` into this configuration.
///
/// While we still have `./configure` this implements the ability to decode
/// that configuration into this. This isn't exactly a full-blown makefile
/// parser, but hey it gets the job done!
fn update_with_config_mk(&mut self) {
let mut config = String::new();
File::open("config.mk").unwrap().read_to_string(&mut config).unwrap();
for line in config.lines() {
let mut parts = line.splitn(2, ":=").map(|s| s.trim());
let key = parts.next().unwrap();
let value = match parts.next() {
Some(n) if n.starts_with('\"') => &n[1..n.len() - 1],
Some(n) => n,
None => continue
};
macro_rules! check {
($(($name:expr, $val:expr),)*) => {
if value == "1" {
$(
if key == concat!("CFG_ENABLE_", $name) {
$val = true;
continue
}
if key == concat!("CFG_DISABLE_", $name) {
$val = false;
continue
}
)*
}
}
}
check! {
("MANAGE_SUBMODULES", self.submodules),
("COMPILER_DOCS", self.compiler_docs),
("DOCS", self.docs),
("LLVM_ASSERTIONS", self.llvm_assertions),
("LLVM_RELEASE_DEBUGINFO", self.llvm_release_debuginfo),
("OPTIMIZE_LLVM", self.llvm_optimize),
("LLVM_VERSION_CHECK", self.llvm_version_check),
("LLVM_STATIC_STDCPP", self.llvm_static_stdcpp),
("LLVM_LINK_SHARED", self.llvm_link_shared),
("OPTIMIZE", self.rust_optimize),
("DEBUG_ASSERTIONS", self.rust_debug_assertions),
("DEBUGINFO", self.rust_debuginfo),
("DEBUGINFO_LINES", self.rust_debuginfo_lines),
("DEBUGINFO_ONLY_STD", self.rust_debuginfo_only_std),
("JEMALLOC", self.use_jemalloc),
("DEBUG_JEMALLOC", self.debug_jemalloc),
("RPATH", self.rust_rpath),
("OPTIMIZE_TESTS", self.rust_optimize_tests),
("DEBUGINFO_TESTS", self.rust_debuginfo_tests),
("QUIET_TESTS", self.quiet_tests),
("LOCAL_REBUILD", self.local_rebuild),
("NINJA", self.ninja),
("CODEGEN_TESTS", self.codegen_tests),
("LOCKED_DEPS", self.locked_deps),
("VENDOR", self.vendor),
("FULL_BOOTSTRAP", self.full_bootstrap),
("EXTENDED", self.extended),
("SANITIZERS", self.sanitizers),
("PROFILER", self.profiler),
("DIST_SRC", self.rust_dist_src),
("CARGO_OPENSSL_STATIC", self.openssl_static),
}
match key {
"CFG_BUILD" if value.len() > 0 => self.build = INTERNER.intern_str(value),
"CFG_HOST" if value.len() > 0 => {
self.hosts.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
}
"CFG_TARGET" if value.len() > 0 => {
self.targets.extend(value.split(" ").map(|s| INTERNER.intern_str(s)));
}
"CFG_EXPERIMENTAL_TARGETS" if value.len() > 0 => {
self.llvm_experimental_targets = Some(value.to_string());
}
"CFG_MUSL_ROOT" if value.len() > 0 => {
self.musl_root = Some(parse_configure_path(value));
}
"CFG_MUSL_ROOT_X86_64" if value.len() > 0 => {
let target = INTERNER.intern_str("x86_64-unknown-linux-musl");
let target = self.target_config.entry(target).or_insert(Target::default());
target.musl_root = Some(parse_configure_path(value));
}
"CFG_MUSL_ROOT_I686" if value.len() > 0 => {
let target = INTERNER.intern_str("i686-unknown-linux-musl");
let target = self.target_config.entry(target).or_insert(Target::default());
target.musl_root = Some(parse_configure_path(value));
}
"CFG_MUSL_ROOT_ARM" if value.len() > 0 => {
let target = INTERNER.intern_str("arm-unknown-linux-musleabi");
let target = self.target_config.entry(target).or_insert(Target::default());
target.musl_root = Some(parse_configure_path(value));
}
"CFG_MUSL_ROOT_ARMHF" if value.len() > 0 => {
let target = INTERNER.intern_str("arm-unknown-linux-musleabihf");
let target = self.target_config.entry(target).or_insert(Target::default());
target.musl_root = Some(parse_configure_path(value));
}
"CFG_MUSL_ROOT_ARMV7" if value.len() > 0 => {
let target = INTERNER.intern_str("armv7-unknown-linux-musleabihf");
let target = self.target_config.entry(target).or_insert(Target::default());
target.musl_root = Some(parse_configure_path(value));
}
"CFG_DEFAULT_AR" if value.len() > 0 => {
self.rustc_default_ar = Some(value.to_string());
}
"CFG_DEFAULT_LINKER" if value.len() > 0 => {
self.rustc_default_linker = Some(value.to_string());
}
"CFG_GDB" if value.len() > 0 => {
self.gdb = Some(parse_configure_path(value));
}
"CFG_RELEASE_CHANNEL" => {
self.channel = value.to_string();
}
"CFG_PREFIX" => {
self.prefix = Some(PathBuf::from(value));
}
"CFG_SYSCONFDIR" => {
self.sysconfdir = Some(PathBuf::from(value));
}
"CFG_DOCDIR" => {
self.docdir = Some(PathBuf::from(value));
}
"CFG_BINDIR" => {
self.bindir = Some(PathBuf::from(value));
}
"CFG_LIBDIR" => {
self.libdir = Some(PathBuf::from(value));
}
"CFG_LIBDIR_RELATIVE" => {
self.libdir_relative = Some(PathBuf::from(value));
}
"CFG_MANDIR" => {
self.mandir = Some(PathBuf::from(value));
}
"CFG_LLVM_ROOT" if value.len() > 0 => {
let target = self.target_config.entry(self.build.clone())
.or_insert(Target::default());
let root = parse_configure_path(value);
target.llvm_config = Some(push_exe_path(root, &["bin", "llvm-config"]));
}
"CFG_JEMALLOC_ROOT" if value.len() > 0 => {
let target = self.target_config.entry(self.build.clone())
.or_insert(Target::default());
target.jemalloc = Some(parse_configure_path(value).join("libjemalloc_pic.a"));
}
"CFG_ARM_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
let target = INTERNER.intern_str("arm-linux-androideabi");
let target = self.target_config.entry(target).or_insert(Target::default());
target.ndk = Some(parse_configure_path(value));
}
"CFG_ARMV7_LINUX_ANDROIDEABI_NDK" if value.len() > 0 => {
let target = INTERNER.intern_str("armv7-linux-androideabi");
let target = self.target_config.entry(target).or_insert(Target::default());
target.ndk = Some(parse_configure_path(value));
}
"CFG_I686_LINUX_ANDROID_NDK" if value.len() > 0 => {
let target = INTERNER.intern_str("i686-linux-android");
let target = self.target_config.entry(target).or_insert(Target::default());
target.ndk = Some(parse_configure_path(value));
}
"CFG_AARCH64_LINUX_ANDROID_NDK" if value.len() > 0 => {
let target = INTERNER.intern_str("aarch64-linux-android");
let target = self.target_config.entry(target).or_insert(Target::default());
target.ndk = Some(parse_configure_path(value));
}
"CFG_X86_64_LINUX_ANDROID_NDK" if value.len() > 0 => {
let target = INTERNER.intern_str("x86_64-linux-android");
let target = self.target_config.entry(target).or_insert(Target::default());
target.ndk = Some(parse_configure_path(value));
}
"CFG_LOCAL_RUST_ROOT" if value.len() > 0 => {
let path = parse_configure_path(value);
self.initial_rustc = push_exe_path(path.clone(), &["bin", "rustc"]);
self.initial_cargo = push_exe_path(path, &["bin", "cargo"]);
}
"CFG_PYTHON" if value.len() > 0 => {
let path = parse_configure_path(value);
self.python = Some(path);
}
"CFG_ENABLE_CCACHE" if value == "1" => {
self.ccache = Some(exe("ccache", &self.build));
}
"CFG_ENABLE_SCCACHE" if value == "1" => {
self.ccache = Some(exe("sccache", &self.build));
}
"CFG_CONFIGURE_ARGS" if value.len() > 0 => {
self.configure_args = value.split_whitespace()
.map(|s| s.to_string())
.collect();
}
"CFG_QEMU_ARMHF_ROOTFS" if value.len() > 0 => {
let target = INTERNER.intern_str("arm-unknown-linux-gnueabihf");
let target = self.target_config.entry(target).or_insert(Target::default());
target.qemu_rootfs = Some(parse_configure_path(value));
}
"CFG_QEMU_AARCH64_ROOTFS" if value.len() > 0 => {
let target = INTERNER.intern_str("aarch64-unknown-linux-gnu");
let target = self.target_config.entry(target).or_insert(Target::default());
target.qemu_rootfs = Some(parse_configure_path(value));
}
_ => {}
}
}
}
pub fn verbose(&self) -> bool {
self.verbose > 0
}
@ -705,30 +551,6 @@ impl Config {
}
}
#[cfg(not(windows))]
fn parse_configure_path(path: &str) -> PathBuf {
path.into()
}
#[cfg(windows)]
fn parse_configure_path(path: &str) -> PathBuf {
// on windows, configure produces unix style paths e.g. /c/some/path but we
// only want real windows paths
use std::process::Command;
use build_helper;
// '/' is invalid in windows paths, so we can detect unix paths by the presence of it
if !path.contains('/') {
return path.into();
}
let win_path = build_helper::output(Command::new("cygpath").arg("-w").arg(path));
let win_path = win_path.trim();
win_path.into()
}
fn set<T>(field: &mut T, val: Option<T>) {
if let Some(v) = val {
*field = v;

417
src/bootstrap/configure.py Executable file
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::fs::{self, File};
use std::io::{Read, Write};
use std::io::{self, Read, Write};
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
@ -365,6 +365,9 @@ impl Step for Rustc {
// tiny morsel of metadata is used by rust-packaging
let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
if let Some(sha) = build.rust_sha() {
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
}
// On MinGW we've got a few runtime DLL dependencies that we need to
// include. The first argument to this script is where to put these DLLs
@ -429,7 +432,7 @@ impl Step for Rustc {
// Man pages
t!(fs::create_dir_all(image.join("share/man/man1")));
cp_r(&build.src.join("man"), &image.join("share/man/man1"));
cp_r(&build.src.join("src/doc/man"), &image.join("share/man/man1"));
// Debugger scripts
builder.ensure(DebuggerScripts {
@ -724,6 +727,9 @@ impl Step for Src {
let dst_src = dst.join("rust");
t!(fs::create_dir_all(&dst_src));
let src_files = [
"src/Cargo.lock",
];
// This is the reduced set of paths which will become the rust-src component
// (essentially libstd and all of its path dependencies)
let std_src_dirs = [
@ -754,11 +760,14 @@ impl Step for Src {
"src/libprofiler_builtins",
];
let std_src_dirs_exclude = [
"src/compiler-rt/test",
"src/libcompiler_builtins/compiler-rt/test",
"src/jemalloc/test/unit",
];
copy_src_dirs(build, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src);
for file in src_files.iter() {
copy(&build.src.join(file), &dst_src.join(file));
}
// Create source tarball in rust-installer format
let mut cmd = rust_installer(builder);
@ -822,9 +831,9 @@ impl Step for PlainSourceTarball {
"RELEASES.md",
"configure",
"x.py",
"config.toml.example",
];
let src_dirs = [
"man",
"src",
];
@ -837,6 +846,9 @@ impl Step for PlainSourceTarball {
// Create the version file
write_file(&plain_dst_src.join("version"), build.rust_version().as_bytes());
if let Some(sha) = build.rust_sha() {
write_file(&plain_dst_src.join("git-commit-hash"), sha.as_bytes());
}
// If we're building from git sources, we need to vendor a complete distribution.
if build.rust_info.is_git() {
@ -887,7 +899,12 @@ impl Step for PlainSourceTarball {
fn install(src: &Path, dstdir: &Path, perms: u32) {
let dst = dstdir.join(src.file_name().unwrap());
t!(fs::create_dir_all(dstdir));
t!(fs::copy(src, &dst));
drop(fs::remove_file(&dst));
{
let mut s = t!(fs::File::open(&src));
let mut d = t!(fs::File::create(&dst));
io::copy(&mut s, &mut d).expect("failed to copy");
}
chmod(&dst, perms);
}
@ -1081,19 +1098,39 @@ impl Step for Rls {
.arg("--output-dir").arg(&distdir(build))
.arg("--non-installed-overlay").arg(&overlay)
.arg(format!("--package-name={}-{}", name, target))
.arg("--legacy-manifest-dirs=rustlib,cargo");
if build.config.channel == "nightly" {
cmd.arg("--component-name=rls");
} else {
cmd.arg("--component-name=rls-preview");
}
.arg("--legacy-manifest-dirs=rustlib,cargo")
.arg("--component-name=rls-preview");
build.run(&mut cmd);
distdir(build).join(format!("{}-{}.tar.gz", name, target))
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct DontDistWithMiriEnabled;
impl Step for DontDistWithMiriEnabled {
type Output = PathBuf;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let build_miri = run.builder.build.config.test_miri;
run.default_condition(build_miri)
}
fn make_run(run: RunConfig) {
run.builder.ensure(DontDistWithMiriEnabled);
}
fn run(self, _: &Builder) -> PathBuf {
panic!("Do not distribute with miri enabled.\n\
The distributed libraries would include all MIR (increasing binary size).
The distributed MIR would include validation statements.");
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Extended {
stage: u32,
@ -1156,6 +1193,9 @@ impl Step for Extended {
install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
let version = build.rust_version();
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
if let Some(sha) = build.rust_sha() {
t!(t!(File::create(overlay.join("git-commit-hash"))).write_all(sha.as_bytes()));
}
install(&etc.join("README.md"), &overlay, 0o644);
// When rust-std package split from rustc, we needed to ensure that during
@ -1163,7 +1203,10 @@ impl Step for Extended {
// the std files during uninstall. To do this ensure that rustc comes
// before rust-std in the list below.
let mut tarballs = vec![rustc_installer, cargo_installer, rls_installer,
analysis_installer, docs_installer, std_installer];
analysis_installer, std_installer];
if build.config.docs {
tarballs.push(docs_installer);
}
if target.contains("pc-windows-gnu") {
tarballs.push(mingw_installer.unwrap());
}
@ -1285,12 +1328,8 @@ impl Step for Extended {
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target))
.join(format!("rust-std-{}", target)),
&exe.join("rust-std"));
let rls_path = if build.config.channel == "nightly" {
work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls")
} else {
work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview")
};
cp_r(&rls_path, &exe.join("rls"));
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rls"), target)).join("rls-preview"),
&exe.join("rls"));
cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-analysis"), target))
.join(format!("rust-analysis-{}", target)),
&exe.join("rust-analysis"));

View File

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

View File

@ -60,7 +60,9 @@ pub enum Subcommand {
paths: Vec<PathBuf>,
test_args: Vec<String>,
},
Clean,
Clean {
all: bool,
},
Dist {
paths: Vec<PathBuf>,
},
@ -136,7 +138,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
None => {
// No subcommand -- show the general usage and subcommand help
println!("{}\n", subcommand_help);
process::exit(0);
process::exit(1);
}
};
@ -147,6 +149,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`");
opts.optmulti("", "test-args", "extra arguments", "ARGS");
},
"bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
"clean" => { opts.optflag("", "all", "clean all build artifacts"); },
_ => { },
};
@ -250,7 +253,7 @@ Arguments:
}
});
// All subcommands can have an optional "Available paths" section
// All subcommands except `clean` can have an optional "Available paths" section
if matches.opt_present("verbose") {
let config = Config::parse(&["build".to_string()]);
let mut build = Build::new(config);
@ -258,9 +261,10 @@ Arguments:
let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
} else {
extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
subcommand).as_str());
} else if subcommand.as_str() != "clean" {
extra_help.push_str(format!(
"Run `./x.py {} -h -v` to see a list of available paths.",
subcommand).as_str());
}
// User passed in -h/--help?
@ -290,10 +294,13 @@ Arguments:
}
"clean" => {
if paths.len() > 0 {
println!("\nclean takes no arguments\n");
println!("\nclean does not take a path argument\n");
usage(1, &opts, &subcommand_help, &extra_help);
}
Subcommand::Clean
Subcommand::Clean {
all: matches.opt_present("all"),
}
}
"dist" => {
Subcommand::Dist {

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,8 @@ use compile::{self, libtest_stamp, libstd_stamp, librustc_stamp};
use native;
use channel::GitInfo;
use cache::Interned;
use toolstate::ToolState;
use build_helper::BuildExpectation;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct CleanTools {
@ -62,7 +64,9 @@ struct ToolBuild {
compiler: Compiler,
target: Interned<String>,
tool: &'static str,
path: &'static str,
mode: Mode,
expectation: BuildExpectation,
}
impl Step for ToolBuild {
@ -81,6 +85,8 @@ impl Step for ToolBuild {
let compiler = self.compiler;
let target = self.target;
let tool = self.tool;
let path = self.path;
let expectation = self.expectation;
match self.mode {
Mode::Libstd => builder.ensure(compile::Std { compiler, target }),
@ -92,21 +98,22 @@ impl Step for ToolBuild {
let _folder = build.fold_output(|| format!("stage{}-{}", compiler.stage, tool));
println!("Building stage{} tool {} ({})", compiler.stage, tool, target);
let mut cargo = prepare_tool_cargo(builder, compiler, target, tool);
build.run(&mut cargo);
let mut cargo = prepare_tool_cargo(builder, compiler, target, "build", path);
build.run_expecting(&mut cargo, expectation);
build.cargo_out(compiler, Mode::Tool, target).join(exe(tool, &compiler.host))
}
}
fn prepare_tool_cargo(
pub fn prepare_tool_cargo(
builder: &Builder,
compiler: Compiler,
target: Interned<String>,
tool: &'static str,
command: &'static str,
path: &'static str,
) -> Command {
let build = builder.build;
let mut cargo = builder.cargo(compiler, Mode::Tool, target, "build");
let dir = build.src.join("src/tools").join(tool);
let mut cargo = builder.cargo(compiler, Mode::Tool, target, command);
let dir = build.src.join(path);
cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
// We don't want to build tools dynamically as they'll be running across
@ -119,7 +126,12 @@ fn prepare_tool_cargo(
cargo.env("LIBZ_SYS_STATIC", "1");
}
// if tools are using lzma we want to force the build script to build its
// own copy
cargo.env("LZMA_API_STATIC", "1");
cargo.env("CFG_RELEASE_CHANNEL", &build.config.channel);
cargo.env("CFG_VERSION", build.rust_version());
let info = GitInfo::new(&build.config, &dir);
if let Some(sha) = info.sha() {
@ -145,15 +157,27 @@ macro_rules! tool {
impl<'a> Builder<'a> {
pub fn tool_exe(&self, tool: Tool) -> PathBuf {
let stage = self.tool_default_stage(tool);
match tool {
$(Tool::$name =>
self.ensure($name {
compiler: self.compiler(0, self.build.build),
compiler: self.compiler(stage, self.build.build),
target: self.build.build,
}),
)+
}
}
pub fn tool_default_stage(&self, tool: Tool) -> u32 {
// Compile the error-index in the top stage as it depends on
// rustdoc, so we want to avoid recompiling rustdoc twice if we
// can. Otherwise compile everything else in stage0 as there's
// no need to rebootstrap everything
match tool {
Tool::ErrorIndex => self.top_stage,
_ => 0,
}
}
}
$(
@ -183,6 +207,8 @@ macro_rules! tool {
target: self.target,
tool: $tool_name,
mode: $mode,
path: $path,
expectation: BuildExpectation::None,
})
}
}
@ -200,7 +226,7 @@ tool!(
Compiletest, "src/tools/compiletest", "compiletest", Mode::Libtest;
BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd;
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::Libstd;
RustInstaller, "src/tools/rust-installer", "rust-installer", Mode::Libstd;
RustInstaller, "src/tools/rust-installer", "fabricate", Mode::Libstd;
);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
@ -229,6 +255,8 @@ impl Step for RemoteTestServer {
target: self.target,
tool: "remote-test-server",
mode: Mode::Libstd,
path: "src/tools/remote-test-server",
expectation: BuildExpectation::None,
})
}
}
@ -275,7 +303,16 @@ impl Step for Rustdoc {
let _folder = build.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
println!("Building rustdoc for stage{} ({})", target_compiler.stage, target_compiler.host);
let mut cargo = prepare_tool_cargo(builder, build_compiler, target, "rustdoc");
let mut cargo = prepare_tool_cargo(builder,
build_compiler,
target,
"build",
"src/tools/rustdoc");
// Most tools don't get debuginfo, but rustdoc should.
cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
.env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
build.run(&mut cargo);
// Cargo adds a number of paths to the dylib search path on windows, which results in
// the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@ -336,6 +373,48 @@ impl Step for Cargo {
target: self.target,
tool: "cargo",
mode: Mode::Librustc,
path: "src/tools/cargo",
expectation: BuildExpectation::None,
})
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Clippy {
pub compiler: Compiler,
pub target: Interned<String>,
}
impl Step for Clippy {
type Output = PathBuf;
const DEFAULT: bool = false;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
run.path("src/tools/clippy")
}
fn make_run(run: RunConfig) {
run.builder.ensure(Clippy {
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
target: run.target,
});
}
fn run(self, builder: &Builder) -> PathBuf {
// Clippy depends on procedural macros (serde), which requires a full host
// compiler to be available, so we need to depend on that.
builder.ensure(compile::Rustc {
compiler: self.compiler,
target: builder.build.build,
});
builder.ensure(ToolBuild {
compiler: self.compiler,
target: self.target,
tool: "clippy",
mode: Mode::Librustc,
path: "src/tools/clippy",
expectation: builder.build.config.toolstate.clippy.passes(ToolState::Compiling),
})
}
}
@ -378,6 +457,79 @@ impl Step for Rls {
target: self.target,
tool: "rls",
mode: Mode::Librustc,
path: "src/tools/rls",
expectation: builder.build.config.toolstate.rls.passes(ToolState::Compiling),
})
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Rustfmt {
pub compiler: Compiler,
pub target: Interned<String>,
}
impl Step for Rustfmt {
type Output = PathBuf;
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let builder = run.builder;
run.path("src/tools/rustfmt").default_condition(builder.build.config.extended)
}
fn make_run(run: RunConfig) {
run.builder.ensure(Rustfmt {
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
target: run.target,
});
}
fn run(self, builder: &Builder) -> PathBuf {
builder.ensure(ToolBuild {
compiler: self.compiler,
target: self.target,
tool: "rustfmt",
mode: Mode::Librustc,
path: "src/tools/rustfmt",
expectation: builder.build.config.toolstate.rustfmt.passes(ToolState::Compiling),
})
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Miri {
pub compiler: Compiler,
pub target: Interned<String>,
}
impl Step for Miri {
type Output = PathBuf;
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun) -> ShouldRun {
let build_miri = run.builder.build.config.test_miri;
run.path("src/tools/miri").default_condition(build_miri)
}
fn make_run(run: RunConfig) {
run.builder.ensure(Miri {
compiler: run.builder.compiler(run.builder.top_stage, run.builder.build.build),
target: run.target,
});
}
fn run(self, builder: &Builder) -> PathBuf {
builder.ensure(ToolBuild {
compiler: self.compiler,
target: self.target,
tool: "miri",
mode: Mode::Librustc,
path: "src/tools/miri",
expectation: builder.build.config.toolstate.miri.passes(ToolState::Compiling),
})
}
}
@ -387,7 +539,7 @@ impl<'a> Builder<'a> {
/// `host`.
pub fn tool_cmd(&self, tool: Tool) -> Command {
let mut cmd = Command::new(self.tool_exe(tool));
let compiler = self.compiler(0, self.build.build);
let compiler = self.compiler(self.tool_default_stage(tool), self.build.build);
self.prepare_tool_cmd(compiler, &mut cmd);
cmd
}

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`
pub fn copy(src: &Path, dst: &Path) {
let _ = fs::remove_file(&dst);
let res = fs::copy(src, dst);
if let Err(e) = res {
// Attempt to "easy copy" by creating a hard link (symlinks don't work on
// windows), but if that fails just fall back to a slow `copy` operation.
if let Ok(()) = fs::hard_link(src, dst) {
return
}
if let Err(e) = fs::copy(src, dst) {
panic!("failed to copy `{}` to `{}`: {}", src.display(),
dst.display(), e)
}
@ -44,7 +48,6 @@ pub fn copy(src: &Path, dst: &Path) {
let atime = FileTime::from_last_access_time(&metadata);
let mtime = FileTime::from_last_modification_time(&metadata);
t!(filetime::set_file_times(dst, atime, mtime));
}
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
@ -279,7 +282,7 @@ pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
ptr::null_mut());
let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
let mut db = data.as_mut_ptr()
let db = data.as_mut_ptr()
as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
let buf = &mut (*db).ReparseTarget as *mut _;
let mut i = 0;

View File

@ -35,55 +35,97 @@ macro_rules! t {
})
}
pub fn run(cmd: &mut Command) {
println!("running: {:?}", cmd);
run_silent(cmd);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum BuildExpectation {
Succeeding,
Failing,
None,
}
pub fn run_silent(cmd: &mut Command) {
if !try_run_silent(cmd) {
pub fn run(cmd: &mut Command, expect: BuildExpectation) {
println!("running: {:?}", cmd);
run_silent(cmd, expect);
}
pub fn run_silent(cmd: &mut Command, expect: BuildExpectation) {
if !try_run_silent(cmd, expect) {
std::process::exit(1);
}
}
pub fn try_run_silent(cmd: &mut Command) -> bool {
pub fn try_run_silent(cmd: &mut Command, expect: BuildExpectation) -> bool {
let status = match cmd.status() {
Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
cmd, e)),
};
if !status.success() {
println!("\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n",
cmd,
status);
}
status.success()
process_status(
cmd,
status.success(),
expect,
|| println!("\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n",
cmd,
status))
}
pub fn run_suppressed(cmd: &mut Command) {
if !try_run_suppressed(cmd) {
fn process_status<F: FnOnce()>(
cmd: &Command,
success: bool,
expect: BuildExpectation,
f: F,
) -> bool {
use BuildExpectation::*;
match (expect, success) {
(None, false) => { f(); false },
// Non-tool build succeeds, everything is good
(None, true) => true,
// Tool expected to work and is working
(Succeeding, true) => true,
// Tool expected to fail and is failing
(Failing, false) => {
println!("This failure is expected (see `src/tools/toolstate.toml`)");
true
},
// Tool expected to work, but is failing
(Succeeding, false) => {
f();
println!("You can disable the tool in `src/tools/toolstate.toml`");
false
},
// Tool expected to fail, but is working
(Failing, true) => {
println!("Expected `{:?}` to fail, but it succeeded.\n\
Please adjust `src/tools/toolstate.toml` accordingly", cmd);
false
}
}
}
pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) {
if !try_run_suppressed(cmd, expect) {
std::process::exit(1);
}
}
pub fn try_run_suppressed(cmd: &mut Command) -> bool {
pub fn try_run_suppressed(cmd: &mut Command, expect: BuildExpectation) -> bool {
let output = match cmd.output() {
Ok(status) => status,
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}",
cmd, e)),
};
if !output.status.success() {
println!("\n\ncommand did not execute successfully: {:?}\n\
process_status(
cmd,
output.status.success(),
expect,
|| println!("\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n\
stdout ----\n{}\n\
stderr ----\n{}\n\n",
cmd,
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr));
}
output.status.success()
String::from_utf8_lossy(&output.stderr)))
}
pub fn gnu_target(target: &str) -> String {

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

View File

@ -5,21 +5,27 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm 9
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm 14
# Note:
# Do not upgrade to `openjdk-9-jre-headless`, as it will cause certificate error
# when installing the Android SDK (see PR #45193). This is unfortunate, but
# every search result suggested either disabling HTTPS or replacing JDK 9 by
# JDK 8 as the solution (e.g. https://stackoverflow.com/q/41421340). :|
RUN dpkg --add-architecture i386 && \
apt-get update && \
apt-get install -y --no-install-recommends \
libgl1-mesa-glx \
libpulse0 \
libstdc++6:i386 \
openjdk-9-jre-headless \
openjdk-8-jre-headless \
tzdata
COPY scripts/android-sdk.sh /scripts/
RUN . /scripts/android-sdk.sh && \
download_and_create_avd tools_r25.2.5-linux.zip armeabi-v7a 18
download_and_create_avd 4333796 armeabi-v7a 18
ENV PATH=$PATH:/android/sdk/emulator
ENV PATH=$PATH:/android/sdk/tools
ENV PATH=$PATH:/android/sdk/platform-tools
@ -27,7 +33,7 @@ ENV TARGETS=arm-linux-androideabi
ENV RUST_CONFIGURE_ARGS \
--target=$TARGETS \
--arm-linux-androideabi-ndk=/android/ndk/arm-9
--arm-linux-androideabi-ndk=/android/ndk/arm-14
ENV SCRIPT python2.7 ../x.py test --target $TARGETS

View File

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

View File

@ -65,11 +65,24 @@ CFLAGS="-march=armv7-a" \
hide_output make -j$(nproc)
hide_output make install
cd ..
rm -rf musl-$MUSL
tar xf musl-$MUSL.tar.gz
cd musl-$MUSL
CC=aarch64-linux-gnu-gcc \
CFLAGS="" \
hide_output ./configure \
--prefix=/usr/local/aarch64-linux-musl \
--enable-wrapper=gcc
hide_output make -j$(nproc)
hide_output make install
cd ..
rm -rf musl-$MUSL*
ln -nsf ../arm-linux-musleabi/bin/musl-gcc /usr/local/bin/arm-linux-musleabi-gcc
ln -nsf ../arm-linux-musleabihf/bin/musl-gcc /usr/local/bin/arm-linux-musleabihf-gcc
ln -nsf ../armv7-linux-musleabihf/bin/musl-gcc /usr/local/bin/armv7-linux-musleabihf-gcc
ln -nsf ../aarch64-linux-musl/bin/musl-gcc /usr/local/bin/aarch64-unknown-linux-musl-gcc
curl -L https://github.com/llvm-mirror/llvm/archive/release_39.tar.gz | tar xzf -
curl -L https://github.com/llvm-mirror/libunwind/archive/release_39.tar.gz | tar xzf -
@ -116,5 +129,19 @@ cp lib/libunwind.a /usr/local/armv7-linux-musleabihf/lib
cd ..
rm -rf libunwind-build
mkdir libunwind-build
cd libunwind-build
cmake ../libunwind-release_39 \
-DLLVM_PATH=/tmp/llvm-release_39 \
-DLIBUNWIND_ENABLE_SHARED=0 \
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
-DCMAKE_C_FLAGS="" \
-DCMAKE_CXX_FLAGS=""
make -j$(nproc)
cp lib/libunwind.a /usr/local/aarch64-linux-musl/lib
cd ..
rm -rf libunwind-build
rm -rf libunwind-release_39
rm -rf llvm-release_39

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 kernel. This file was generated by running `make defconfig`
# followed by `make menuconfig` and then enabling the IPv6 protocol page.
COPY disabled/aarch64-gnu/config /build/.config
COPY aarch64-gnu/config /build/.config
RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.42.tar.xz | \
tar xJf - && \
cd /build/linux-4.4.42 && \

View File

@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip arm64 21
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm64 21
ENV PATH=$PATH:/android/ndk/arm64-21/bin

View File

@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
download_ndk android-ndk-r13b-linux-x86_64.zip && \
make_standalone_toolchain arm 9 && \
download_ndk android-ndk-r15c-linux-x86_64.zip && \
make_standalone_toolchain arm 14 && \
make_standalone_toolchain arm 21 && \
remove_ndk
RUN chmod 777 /android/ndk && \
ln -s /android/ndk/arm-21 /android/ndk/arm
ENV PATH=$PATH:/android/ndk/arm-9/bin
ENV PATH=$PATH:/android/ndk/arm-14/bin
ENV DEP_Z_ROOT=/android/ndk/arm-9/sysroot/usr/
ENV DEP_Z_ROOT=/android/ndk/arm-14/sysroot/usr/
ENV HOSTS=armv7-linux-androideabi
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
--enable-extended \
--enable-cargo-openssl-static
# We support api level 9, but api level 21 is required to build llvm. To
# We support api level 14, but api level 21 is required to build llvm. To
# overcome this problem we use a ndk with api level 21 to build llvm and then
# switch to a ndk with api level 9 to complete the build. When the linker is
# switch to a ndk with api level 14 to complete the build. When the linker is
# invoked there are missing symbols (like sigsetempty, not available with api
# level 9), the default linker behavior is to generate an error, to allow the
# level 14), the default linker behavior is to generate an error, to allow the
# build to finish we use --warn-unresolved-symbols. Note that the missing
# symbols does not affect std, only the compiler (llvm) and cargo (openssl).
ENV SCRIPT \
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
rm /android/ndk/arm && \
ln -s /android/ndk/arm-9 /android/ndk/arm && \
ln -s /android/ndk/arm-14 /android/ndk/arm && \
python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
COPY scripts/sccache.sh /scripts/

View File

@ -5,17 +5,17 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
download_ndk android-ndk-r13b-linux-x86_64.zip && \
make_standalone_toolchain x86 9 && \
download_ndk android-ndk-r15c-linux-x86_64.zip && \
make_standalone_toolchain x86 14 && \
make_standalone_toolchain x86 21 && \
remove_ndk
RUN chmod 777 /android/ndk && \
ln -s /android/ndk/x86-21 /android/ndk/x86
ENV PATH=$PATH:/android/ndk/x86-9/bin
ENV PATH=$PATH:/android/ndk/x86-14/bin
ENV DEP_Z_ROOT=/android/ndk/x86-9/sysroot/usr/
ENV DEP_Z_ROOT=/android/ndk/x86-14/sysroot/usr/
ENV HOSTS=i686-linux-android
@ -27,18 +27,18 @@ ENV RUST_CONFIGURE_ARGS \
--enable-extended \
--enable-cargo-openssl-static
# We support api level 9, but api level 21 is required to build llvm. To
# We support api level 14, but api level 21 is required to build llvm. To
# overcome this problem we use a ndk with api level 21 to build llvm and then
# switch to a ndk with api level 9 to complete the build. When the linker is
# switch to a ndk with api level 14 to complete the build. When the linker is
# invoked there are missing symbols (like sigsetempty, not available with api
# level 9), the default linker behavior is to generate an error, to allow the
# level 14), the default linker behavior is to generate an error, to allow the
# build to finish we use --warn-unresolved-symbols. Note that the missing
# symbols does not affect std, only the compiler (llvm) and cargo (openssl).
ENV SCRIPT \
python2.7 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \
(export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \
rm /android/ndk/x86 && \
ln -s /android/ndk/x86-9 /android/ndk/x86 && \
ln -s /android/ndk/x86-14 /android/ndk/x86 && \
python2.7 ../x.py dist --host $HOSTS --target $HOSTS)
COPY scripts/sccache.sh /scripts/

View File

@ -5,7 +5,7 @@ RUN sh /scripts/android-base-apt-get.sh
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
download_and_make_toolchain android-ndk-r13b-linux-x86_64.zip x86_64 21
download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip x86_64 21
ENV PATH=$PATH:/android/ndk/x86_64-21/bin

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
COPY scripts/emscripten-wasm.sh /scripts/
COPY disabled/wasm32-exp/node.sh /usr/local/bin/node
COPY wasm32-exp/node.sh /usr/local/bin/node
RUN bash /scripts/emscripten-wasm.sh
# cache

View File

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

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
pushd $SYSROOT
centos_base=http://mirror.centos.org/altarch/7.3.1611/os/ppc64le/Packages
centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages
glibc_v=2.17-157.el7
kernel_v=3.10.0-514.el7
for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do

View File

@ -36,12 +36,14 @@ elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
echo Cannot run disabled images on travis!
exit 1
fi
retry docker \
# retry messes with the pipe from tar to docker. Not needed on non-travis
# Transform changes the context of disabled Dockerfiles to match the enabled ones
tar --transform 's#^./disabled/#./#' -C $docker_dir -c . | docker \
build \
--rm \
-t rust-ci \
-f "$docker_dir/disabled/$image/Dockerfile" \
"$docker_dir"
-f "$image/Dockerfile" \
-
else
echo Invalid image: $image
exit 1

View File

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

View File

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

View File

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

View File

@ -52,7 +52,11 @@ if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
fi
else
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
# We almost always want debug assertions enabled, but sometimes this takes too
# long for too little benefit, so we just turn them off.
if [ "$NO_DEBUG_ASSERTIONS" = "" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions"
fi
# In general we always want to run tests with LLVM assertions enabled, but not
# all platforms currently support that, so we have an option to disable.

View File

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

View File

@ -19,12 +19,14 @@ starting with the second edition.
## Requirements
Building the book requires [mdBook] >= v0.0.13. To get it:
Building the book requires [mdBook], ideally the same version that
[rust-lang/rust uses in this file][rust-mdbook]. To get it:
[mdBook]: https://github.com/azerupi/mdBook
[rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml
```bash
$ cargo install mdbook
$ cargo install mdbook --vers [version-num]
```
## Building

View File

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

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
Chapter 1, and make a new project using Cargo, like so:
```bash
```
$ cargo new guessing_game --bin
$ cd guessing_game
```
@ -34,7 +34,7 @@ Look at the generated *Cargo.toml* file:
Filename: Cargo.toml
```toml
```
[package]
name = "guessing_game"
version = "0.1.0"
@ -51,7 +51,7 @@ you. Check out the *src/main.rs* file:
Filename: src/main.rs
```rust
```
fn main() {
println!("Hello, world!");
}
@ -60,9 +60,10 @@ fn main() {
Now lets compile this “Hello, world!” program and run it in the same step
using the `cargo run` command:
```bash
```
$ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs
Running `target/debug/guessing_game`
Hello, world!
```
@ -81,7 +82,7 @@ to input a guess. Enter the code in Listing 2-1 into *src/main.rs*.
Filename: src/main.rs
```rust,ignore
```
use std::io;
fn main() {
@ -98,51 +99,51 @@ fn main() {
}
```
<caption>
Listing 2-1: Code to get a guess from the user and print it out
</caption>
Listing 2-1: Code to get a guess from the user and print
it out
This code contains a lot of information, so lets go over it bit by bit. To
obtain user input and then print the result as output, we need to import the
`io` (input/output) library from the standard library (which is known as `std`):
obtain user input and then print the result as output, we need to bring the
`io` (input/output) library into scope. The `io` library comes from the
standard library (which is known as `std`):
```rust,ignore
```
use std::io;
```
By default, Rust imports only a few types into every program in the
*prelude*. If a type you want to use isnt in the
prelude, you have to import that type into your program explicitly with a `use`
By default, Rust brings only a few types into the scope of every program in
the *prelude*. If a type you want to use isnt in the
prelude, you have to bring that type into scope explicitly with a `use`
statement. Using the `std::io` library provides you with a number of useful
`io`-related features, including the functionality to accept user input.
As you saw in Chapter 1, the `main` function is the entry point into the
program:
```rust,ignore
```
fn main() {
```
The `fn` syntax declares a new function, the `()` indicate there are no
arguments, and `{` starts the body of the function.
parameters, and `{` starts the body of the function.
As you also learned in Chapter 1, `println!` is a macro that prints a string to
the screen:
```rust,ignore
```
println!("Guess the number!");
println!("Please input your guess.");
```
This code is just printing a prompt stating what the game is and requesting
input from the user.
This code is printing a prompt stating what the game is and requesting input
from the user.
### Storing Values with Variables
Next, well create a place to store the user input, like this:
```rust,ignore
```
let mut guess = String::new();
```
@ -150,7 +151,7 @@ Now the program is getting interesting! Theres a lot going on in this little
line. Notice that this is a `let` statement, which is used to create
*variables*. Heres another example:
```rust,ignore
```
let foo = bar;
```
@ -158,7 +159,7 @@ This line will create a new variable named `foo` and bind it to the value
`bar`. In Rust, variables are immutable by default. The following example shows
how to use `mut` before the variable name to make a variable mutable:
```rust
```
let foo = 5; // immutable
let mut bar = 5; // mutable
```
@ -189,7 +190,7 @@ Recall that we included the input/output functionality from the standard
library with `use std::io;` on the first line of the program. Now well call an
associated function, `stdin`, on `io`:
```rust,ignore
```
io::stdin().read_line(&mut guess)
.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
part is this method:
```rust,ignore
```
.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
written this code as:
```rust,ignore
```
io::stdin().read_line(&mut guess).expect("Failed to read line");
```
@ -264,32 +265,35 @@ argument to `expect`. If the `read_line` method returns an `Err`, it would
likely be the result of an error coming from the underlying operating system.
If this instance of `io::Result` is an `Ok` value, `expect` will take the
return value that `Ok` is holding and return just that value to you so you
could use it. In this case, that value is the number of characters the user
could use it. In this case, that value is the number of bytes in what the user
entered into standard input.
If we dont call `expect`, the program will compile, but well get a warning:
```bash
```
$ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used,
#[warn(unused_must_use)] on by default
src/main.rs:10 io::stdin().read_line(&mut guess);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
warning: unused `std::result::Result` which must be used
--> src/main.rs:10:5
|
10 | io::stdin().read_line(&mut guess);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_must_use)] on by default
```
Rust warns that we havent used the `Result` value returned from `read_line`,
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
want to crash this program when a problem occurs, we can use `expect`. Youll
learn about recovering from errors in Chapter 9.
suppress the warning is to actually write error handling, but since we want to
crash this program when a problem occurs, we can use `expect`. Youll learn
about recovering from errors in Chapter 9.
### 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:
```rust,ignore
```
println!("You guessed: {}", guess);
```
@ -299,7 +303,7 @@ using `{}`: the first set of `{}` holds the first value listed after the format
string, the second set holds the second value, and so on. Printing out multiple
values in one call to `println!` would look like this:
```rust
```
let x = 5;
let y = 10;
@ -310,11 +314,13 @@ This code would print out `x = 5 and y = 10`.
### Testing the First Part
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
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
@ -348,7 +354,7 @@ you:
Filename: Cargo.toml
```toml
```
[dependencies]
rand = "0.3.14"
@ -367,7 +373,7 @@ version 0.3.14.”
Now, without changing any of the code, lets build the project, as shown in
Listing 2-2:
```bash
```
$ cargo build
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.3.14
@ -375,12 +381,11 @@ $ cargo build
Compiling libc v0.2.14
Compiling rand v0.3.14
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
```
<caption>
Listing 2-2: The output from running `cargo build` after adding the rand crate
as a dependency
</caption>
Listing 2-2: The output from running `cargo build` after
adding the rand crate as a dependency
You may see different version numbers (but they will all be compatible with
the code, thanks to SemVer!), and the lines may be in a different order.
@ -405,7 +410,7 @@ 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,
youll only see one line of output:
```bash
```
$ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
```
@ -448,7 +453,7 @@ But by default, Cargo will only look for versions larger than `0.3.0` and
smaller than `0.4.0`. If the `rand` crate has released two new versions,
`0.3.15` and `0.4.0`, you would see the following if you ran `cargo update`:
```bash
```
$ cargo update
Updating registry `https://github.com/rust-lang/crates.io-index`
Updating rand v0.3.14 -> v0.3.15
@ -460,7 +465,7 @@ that the version of the `rand` crate you are now using is `0.3.15`.
If you wanted to use `rand` version `0.4.0` or any version in the `0.4.x`
series, youd have to update the *Cargo.toml* file to look like this instead:
```toml
```
[dependencies]
rand = "0.4.0"
@ -471,7 +476,7 @@ available and reevaluate your `rand` requirements according to the new version
you specified.
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,
so Rustaceans are able to write smaller projects that are assembled from a
number of packages.
@ -483,7 +488,7 @@ in Listing 2-3:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -507,9 +512,8 @@ fn main() {
}
```
<caption>
Listing 2-3: Code changes needed in order to generate a random number
</caption>
Listing 2-3: Code changes needed in order to generate a
random number
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
@ -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`
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
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
@ -545,9 +549,10 @@ the answer as soon as it starts!
Try running the program a few times:
```bash
```
$ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 7
@ -573,7 +578,7 @@ step is shown in Listing 2-4:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -604,9 +609,8 @@ fn main() {
}
```
<caption>
Listing 2-4: Handling the possible return values of comparing two numbers
</caption>
Listing 2-4: Handling the possible return values of
comparing two numbers
The first new bit here is another `use`, bringing a type called
`std::cmp::Ordering` into scope from the standard library. `Ordering` is
@ -616,7 +620,7 @@ compare two values.
Then we add five new lines at the bottom that use the `Ordering` type:
```rust,ignore
```
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
@ -627,7 +631,7 @@ match guess.cmp(&secret_number) {
The `cmp` method compares two values and can be called on anything that can be
compared. It takes a reference to whatever you want to compare with: here its
comparing the `guess` to the `secret_number`. `cmp` returns a variant of the
`Ordering` enum we imported with the `use` statement. We use a
`Ordering` enum we brought into scope with the `use` statement. We use a
`match` expression to decide what to do next based on
which variant of `Ordering` was returned from the call to `cmp` with the values
in `guess` and `secret_number`.
@ -638,7 +642,7 @@ expression fits that arms pattern. Rust takes the value given to `match` and
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
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
used here. Say that the user has guessed 50, and the randomly generated secret
@ -646,7 +650,7 @@ number this time is 38. When the code compares 50 to 38, the `cmp` method will
return `Ordering::Greater`, because 50 is greater than 38. `Ordering::Greater`
is the value that the `match` expression gets. It looks at the first arms
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
`Ordering::Greater`! The associated code in that arm will execute and print
`Too big!` to the screen. The `match` expression ends because it has no need to
@ -654,7 +658,7 @@ look at the last arm in this particular scenario.
However, the code in Listing 2-4 wont compile yet. Lets try it:
```bash
```
$ cargo build
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
error[E0308]: mismatched types
@ -687,7 +691,7 @@ that by adding the following two lines to the `main` function body:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -723,7 +727,7 @@ fn main() {
The two new lines are:
```rust,ignore
```
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
```
@ -740,11 +744,12 @@ We bind `guess` to the expression `guess.trim().parse()`. The `guess` in the
expression refers to the original `guess` that was a `String` with the input in
it. The `trim` method on a `String` instance will eliminate any whitespace at
the beginning and end. `u32` can only contain numerical characters, but the
user must press the Return key to satisfy `read_line`. When the user presses
Return, a newline character is added to the string. For example, if the user
types 5 and presses return, `guess` looks like this: `5\n`. The `\n` represents
“newline,” the return key. The `trim` method eliminates `\n`, resulting in just
`5`.
user must press the <span class="keystroke">enter</span> key to satisfy
`read_line`. When the user presses <span class="keystroke">enter</span>, a
newline character is added to the string. For example, if the user types <span
class="keystroke">5</span> and presses <span class="keystroke"> enter</span>,
`guess` looks like this: `5\n`. The `\n` represents “newline,” the enter key.
The `trim` method eliminates `\n`, resulting in just `5`.
The `parse` method on strings parses a string into some
kind of number. Because this method can parse a variety of number types, we
@ -761,7 +766,7 @@ The call to `parse` could easily cause an error. If, for example, the string
contained `A👍%`, there would be no way to convert that to a number. Because it
might fail, the `parse` method returns a `Result` type, much like the
`read_line` method does as discussed earlier in “Handling Potential Failure
with the Result Type” on page XX. 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
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
@ -770,9 +775,10 @@ and `expect` will return the number that we want from the `Ok` value.
Lets run the program now!
```bash
```
$ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
Running `target/guessing_game`
Guess the number!
The secret number is: 58
@ -797,7 +803,7 @@ chances at guessing the number:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -839,13 +845,13 @@ program again. Notice that there is a new problem because the program is doing
exactly what we told it to do: ask for another guess forever! It doesnt seem
like the user can quit!
The user could always halt the program by using the keyboard shortcut `Ctrl-C`.
But theres another way to escape this insatiable monster that we mentioned in
the `parse` discussion in “Comparing the Guesses” on page XX: if the user
enters a non-number answer, the program will crash. The user can take advantage
of that in order to quit, as shown here:
The user could always halt the program by using the keyboard shortcut
<span class="keystroke">ctrl-C</span>. But theres another way to escape this
insatiable monster that we mentioned in the `parse` discussion in “Comparing the
Guess to the Secret Number”: if the user enters a non-number answer, the program
will crash. The user can take advantage of that in order to quit, as shown here:
```bash
```
$ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Running `target/guessing_game`
@ -880,7 +886,7 @@ Lets program the game to quit when the user wins by adding a `break`:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -930,7 +936,7 @@ the user inputs a non-number, 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
converted from a `String` to a `u32`:
```rust,ignore
```
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
@ -962,7 +968,7 @@ might encounter!
Now everything in the program should work as expected. Lets try it by running
`cargo run`:
```bash
```
$ cargo run
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
Running `target/guessing_game`
@ -991,7 +997,7 @@ secret number. Listing 2-5 shows the final code:
Filename: src/main.rs
```rust,ignore
```
extern crate rand;
use std::io;
@ -1030,9 +1036,7 @@ fn main() {
}
```
<caption>
Listing 2-5: Complete code of the guessing game
</caption>
## Summary

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
them early will give you a strong core to start from.
PROD: START BOX
### Keywords
The Rust language has a set of *keywords* that have been reserved for use by
the language only, much like other languages do. Keep in mind that you cannot
use these words as names of variables or functions. Most of the keywords have
special meanings, and youll be using them to do various tasks in your Rust
programs; a few have no current functionality associated with them but have
been reserved for functionality that might be added to Rust in the future. You
can find a list of the keywords in Appendix A.
PROD: END BOX
> ### Keywords
>
> The Rust language has a set of *keywords* that have been reserved for use by
> the language only, much like other languages do. Keep in mind that you cannot
> use these words as names of variables or functions. Most of the keywords have
> special meanings, and youll be using them to do various tasks in your Rust
> programs; a few have no current functionality associated with them but have
> been reserved for functionality that might be added to Rust in the future. You
> can find a list of the keywords in Appendix A.
## Variables and Mutability
@ -43,7 +39,7 @@ code with the following:
Filename: src/main.rs
```rust,ignore
```
fn main() {
let x = 5;
println!("The value of x is: {}", x);
@ -97,7 +93,7 @@ For example, change *src/main.rs* to the following:
Filename: src/main.rs
```rust
```
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
@ -108,9 +104,10 @@ fn main() {
When we run this program, we get the following:
```bash
```
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
@ -161,7 +158,7 @@ const MAX_POINTS: u32 = 100_000;
Constants are valid for the entire time a program runs, within the scope they
were declared in, making them a useful choice for values in your application
domain that multiple part of the program might need to know about, such as the
domain that multiple parts of the program might need to know about, such as the
maximum number of points any player of a game is allowed to earn or the speed
of light.
@ -172,8 +169,8 @@ hardcoded value needed to be updated in the future.
### Shadowing
As we saw in the guessing game tutorial in Chapter 2, we can declare new
variables with the same name as a previous variables, and the new variable
As we saw in the guessing game tutorial in Chapter 2, we can declare a new
variable with the same name as a previous variable, and the new variable
*shadows* the previous variable. Rustaceans say that the first variable is
*shadowed* by the second, which means that the second variables value is what
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
```rust
```
fn main() {
let x = 5;
@ -199,9 +196,10 @@ repeating `let x =`, taking the original value and adding `1` so the value of
previous value and multiplying it by `2` to give `x` a final value of `12`.
When you run this program, it will output the following:
```bash
```
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/variables`
The value of x is: 12
```
@ -217,7 +215,7 @@ change the type of the value, but reuse the same name. For example, say our
program asks a user to show how many spaces they want between some text by
inputting space characters, but we really want to store that input as a number:
```rust
```
let spaces = " ";
let spaces = spaces.len();
```
@ -229,7 +227,7 @@ from having to come up with different names, like `spaces_str` and
`spaces_num`; instead, we can reuse the simpler `spaces` name. However, if we
try to use `mut` for this, as shown here:
```rust,ignore
```
let mut spaces = " ";
spaces = spaces.len();
```
@ -237,7 +235,7 @@ spaces = spaces.len();
well get a compile-time error because were not allowed to mutate a variables
type:
```bash
```
error[E0308]: mismatched types
--> 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
possible type we want to use:
```bash
error[E0282]: unable to infer enough type information about `_`
```
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^ cannot infer type for `_`
|
= note: type annotations or generic parameter binding required
| ^^^^^
| |
| cannot infer type for `_`
| consider giving `guess` a type
```
Youll see different type annotations as we discuss the various data types.
@ -295,16 +294,14 @@ work in Rust.
#### Integer Types
An *integer* is a number without a fractional component. We used one integer
type earlier in this chapter, the `i32` type. This type declaration indicates
that the value its associated with should be a signed integer (hence the `i`,
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1
shows the built-in integer types in Rust. Each variant in the Signed and
Unsigned columns (for example, *i32*) can be used to declare the type of an
type earlier in this chapter, the `u32` type. This type declaration indicates
that the value its associated with should be an unsigned integer (signed
integer types start with `i` instead of `u`) that takes up 32 bits of space.
Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
and Unsigned columns (for example, *i16*) can be used to declare the type of an
integer value.
<caption>
Table 3-1: Integer Types in Rust
</caption>
| Length | Signed | Unsigned |
|--------|--------|----------|
@ -325,11 +322,11 @@ Signed numbers are stored using twos complement representation (if youre
unsure what this is, you can search for it online; an explanation is outside
the scope of this book).
Each signed variant can store numbers from -2<sup>n - 1</sup> to 2<sup>n - 1</sup> - 1 inclusive,
where `n` is the number of bits that variant uses. So an `i8` can store numbers
from -2<sup>7</sup> to 2<sup>7</sup> - 1, which equals -128 to 127. Unsigned variants can store
numbers from 0 to 2<sup>n</sup> - 1, so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which
equals 0 to 255.
Each signed variant can store numbers from -(2<sup>n - 1</sup>) to 2<sup>n -
1</sup> - 1 inclusive, where `n` is the number of bits that variant uses. So an
`i8` can store numbers from -(2<sup>7</sup>) to 2<sup>7</sup> - 1, which equals
-128 to 127. Unsigned variants can store numbers from 0 to 2<sup>n</sup> - 1,
so a `u8` can store numbers from 0 to 2<sup>8</sup> - 1, which equals 0 to 255.
Additionally, the `isize` and `usize` types depend on the kind of computer your
program is running on: 64-bits if 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
`57u8`, and `_` as a visual separator, such as `1_000`.
<caption>
Table 3-2: Integer Literals in Rust
</caption>
| Number literals | Example |
|------------------|---------------|
@ -361,18 +356,14 @@ youd use `isize` or `usize` is when indexing some sort of collection.
Rust also has two primitive types for *floating-point numbers*, which are
numbers with decimal points. Rusts floating-point types are `f32` and `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.
Its possible to use an `f64` type on 32-bit systems, but it will be slower
than using an `f32` type on those systems. Most of the time, trading potential
worse performance for better precision is a reasonable initial choice, and you
should benchmark your code if you suspect floating-point size is a problem in
your situation.
because on modern CPUs its roughly the same speed as `f32` but is capable of
more precision.
Heres an example that shows floating-point numbers in action:
Filename: src/main.rs
```rust
```
fn main() {
let x = 2.0; // f64
@ -385,13 +376,13 @@ Floating-point numbers are represented according to the IEEE-754 standard. The
#### Numeric Operations
Rust supports the usual basic mathematic operations 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.
The following code shows how youd use each one in a `let` statement:
Filename: src/main.rs
```rust
```
fn main() {
// addition
let sum = 5 + 10;
@ -422,7 +413,7 @@ For example:
Filename: src/main.rs
```rust
```
fn main() {
let t = true;
@ -431,18 +422,19 @@ fn main() {
```
The main way to consume boolean values is through conditionals, such as an `if`
statement. 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.
#### The Character Type
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
code shows one way to use it:
code shows one way to use it. Note that the `char` type is specified with
single quotes, as opposed to strings that use double quotes:
Filename: src/main.rs
```rust
```
fn main() {
let c = 'z';
let z = '';
@ -476,7 +468,7 @@ type annotations in this example:
Filename: src/main.rs
```rust
```
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
@ -488,7 +480,7 @@ use pattern matching to destructure a tuple value, like this:
Filename: src/main.rs
```rust
```
fn main() {
let tup = (500, 6.4, 1);
@ -510,7 +502,7 @@ value we want to access. For example:
Filename: src/main.rs
```rust
```
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
@ -538,7 +530,7 @@ inside square brackets:
Filename: src/main.rs
```rust
```
fn main() {
let a = [1, 2, 3, 4, 5];
}
@ -557,7 +549,7 @@ program that needs to know the names of the months of the year. Its very
unlikely that such a program will need to add or remove months, so you can use
an array because you know it will always contain 12 items:
```rust
```
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
```
@ -569,7 +561,7 @@ elements of an array using indexing, like this:
Filename: src/main.rs
```rust
```
fn main() {
let a = [1, 2, 3, 4, 5];
@ -589,7 +581,7 @@ the array? Say we change the example to the following:
Filename: src/main.rs
```rust,ignore
```
fn main() {
let a = [1, 2, 3, 4, 5];
let index = 10;
@ -602,9 +594,10 @@ fn main() {
Running this code using `cargo run` produces the following result:
```bash
```
$ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6
@ -636,7 +629,7 @@ Heres a program that contains an example function definition:
Filename: src/main.rs
```rust
```
fn main() {
println!("Hello, world!");
@ -649,8 +642,8 @@ fn another_function() {
```
Function definitions in Rust start with `fn` and have a set of parentheses
after the function name. The curly braces tell the compiler where the function
body begins and ends.
after the function name. The curly brackets tell the compiler where the
function body begins and ends.
We can call any function weve defined by entering its name followed by a set
of parentheses. Because `another_function` is defined in the program, it can be
@ -663,9 +656,10 @@ 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
should see the following output:
```bash
```
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs
Running `target/debug/functions`
Hello, world!
Another function.
@ -690,7 +684,7 @@ look like in Rust:
Filename: src/main.rs
```rust
```
fn main() {
another_function(5);
}
@ -702,16 +696,17 @@ fn another_function(x: i32) {
Try running this program; you should get the following output:
```bash
```
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs
Running `target/debug/functions`
The value of x is: 5
```
The declaration of `another_function` has one parameter named `x`. The type of
`x` is specified as `i32`. When `5` is passed to `another_function`, the
`println!` macro puts `5` where the pair of curly braces were in the format
`println!` macro puts `5` where the pair of curly brackets were in the format
string.
In function signatures, you *must* declare the type of each parameter. This is
@ -724,7 +719,7 @@ declarations with commas, like this:
Filename: src/main.rs
```rust
```
fn main() {
another_function(5, 6);
}
@ -744,9 +739,10 @@ 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
`cargo run`:
```bash
```
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/functions`
The value of x is: 5
The value of y is: 6
@ -776,15 +772,13 @@ statement. In Listing 3-3, `let y = 6;` is a statement:
Filename: src/main.rs
```rust
```
fn main() {
let y = 6;
}
```
<caption>
Listing 3-3: A `main` function declaration containing one statement.
</caption>
Function definitions are also statements; the entire preceding example is a
statement in itself.
@ -794,7 +788,7 @@ to another variable, as the following code tries to do:
Filename: src/main.rs
```rust,ignore
```
fn main() {
let x = (let y = 6);
}
@ -802,7 +796,7 @@ fn main() {
When you run this program, youll get an error like this:
```bash
```
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
error: expected expression, found statement (`let`)
@ -830,7 +824,7 @@ new scopes, `{}`, is an expression, for example:
Filename: src/main.rs
```rust
```
fn main() {
let x = 5;
@ -845,7 +839,7 @@ fn main() {
This expression:
```rust,ignore
```
{
let x = 3;
x + 1
@ -853,23 +847,25 @@ This expression:
```
is a block that, in this case, evaluates to `4`. That value gets bound to `y`
as part of the `let` statement. Note the line without a semicolon at the end,
unlike most of the lines youve seen so far. Expressions do not include ending
semicolons. If you add a semicolon to the end of an expression, you turn it
into a statement, which will then not return a value. Keep this in mind as you
explore function return values and expressions next.
as part of the `let` statement. Note the `x + 1` line without a semicolon at
the end, unlike most of the lines youve seen so far. Expressions do not
include ending semicolons. If you add a semicolon to the end of an expression,
you turn it into a statement, which will then not return a value. Keep this in
mind as you explore function return values and expressions next.
### Functions with Return Values
Functions can return values to the code that calls them. We dont name return
values, but we do declare their type after an arrow (`->`). In Rust, the return
value of the function is synonymous with the value of the final expression in
the block of the body of a function. Heres an example of a function that
returns a value:
the block of the body of a function. You can return early from a function by
using the `return` keyword and specifying a value, but most functions return
the last expression implicitly. Heres an example of a function that returns a
value:
Filename: src/main.rs
```rust
```
fn five() -> i32 {
5
}
@ -886,9 +882,10 @@ function—just the number `5` by itself. Thats a perfectly valid function in
Rust. Note that the functions return type is specified, too, as `-> i32`. Try
running this code; the output should look like this:
```bash
```
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/functions`
The value of x is: 5
```
@ -899,7 +896,7 @@ first, the line `let x = five();` shows that were using the return value of a
function to initialize a variable. Because the function `five` returns a `5`,
that line is the same as the following:
```rust
```
let x = 5;
```
@ -910,7 +907,7 @@ example:
Filename: src/main.rs
```rust
```
fn main() {
let x = plus_one(5);
@ -928,7 +925,7 @@ expression to a statement?
Filename: src/main.rs
```rust,ignore
```
fn main() {
let x = plus_one(5);
@ -947,18 +944,14 @@ error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | fn plus_one(x: i32) -> i32 {
| ____________________________^ starting here...
| ____________________________^
8 | | x + 1;
| | - help: consider removing this semicolon
9 | | }
| |_^ ...ending here: expected i32, found ()
| |_^ expected i32, found ()
|
= note: expected type `i32`
found type `()`
help: consider removing this semicolon:
--> src/main.rs:8:10
|
8 | x + 1;
| ^
```
The main error message, “mismatched types,” reveals the core issue with this
@ -978,7 +971,7 @@ reading the source code may find useful.
Heres a simple comment:
```rust
```
// Hello, world.
```
@ -986,7 +979,7 @@ In Rust, comments must start with two slashes and continue until the end of the
line. For comments that extend beyond a single line, youll need to include
`//` on each line, like this:
```rust
```
// So were doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain whats going on.
@ -996,7 +989,7 @@ Comments can also be placed at the end of lines containing code:
Filename: src/main.rs
```rust
```
fn main() {
let lucky_number = 7; // Im feeling lucky today.
}
@ -1007,14 +1000,15 @@ separate line above the code its annotating:
Filename: src/main.rs
```rust
```
fn main() {
// Im feeling lucky today.
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
@ -1035,7 +1029,7 @@ the `if` expression. In the *src/main.rs* file, input the following:
Filename: src/main.rs
```rust
```
fn main() {
let number = 3;
@ -1047,24 +1041,27 @@ fn main() {
}
```
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
All `if` expressions start with the keyword `if`, which is followed by a
condition. In this case, the condition checks whether or not the variable
`number` has a value less than 5. The block of code we want to execute if the
condition is true is placed immediately after the condition inside curly
braces. Blocks of code associated with the conditions in `if` expressions are
brackets. Blocks of code associated with the conditions in `if` expressions are
sometimes called *arms*, just like the arms in `match` expressions that we
discussed in the “Comparing the Guess to the Secret Number” section of Chapter
2. Optionally, we can also include an `else` expression, which we chose to do
here, to give the program an alternative block of code to execute should the
condition evaluate to false. If you dont provide an `else` expression and the
condition is false, the program will just skip the `if` block and move on to
the next bit of code.
discussed in the “Comparing the Guess to the Secret Number” section of
Chapter 2. Optionally, we can also include an `else` expression, which we chose
to do here, to give the program an alternative block of code to execute should
the condition evaluate to false. If you dont provide an `else` expression and
the condition is false, the program will just skip the `if` block and move on
to the next bit of code.
Try running this code; you should see the following output:
```bash
```
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
condition was true
```
@ -1072,15 +1069,16 @@ condition was true
Lets try changing the value of `number` to a value that makes the condition
`false` to see what happens:
```rust,ignore
```
let number = 7;
```
Run the program again, and look at the output:
```bash
```
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
condition was false
```
@ -1091,7 +1089,7 @@ code:
Filename: src/main.rs
```rust,ignore
```
fn main() {
let number = 3;
@ -1124,7 +1122,7 @@ expression to the following:
Filename: src/main.rs
```rust
```
fn main() {
let number = 3;
@ -1143,7 +1141,7 @@ expression. For example:
Filename: src/main.rs
```rust
```
fn main() {
let number = 6;
@ -1162,9 +1160,10 @@ fn main() {
This program has four possible paths it can take. After running it, you should
see the following output:
```bash
```
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
number is divisible by 3
```
@ -1187,7 +1186,7 @@ statement, for instance in Listing 3-4:
Filename: src/main.rs
```rust
```
fn main() {
let condition = true;
let number = if condition {
@ -1200,16 +1199,16 @@ fn main() {
}
```
<caption>
Listing 3-4: Assigning the result of an `if` expression to a variable
</caption>
Listing 3-4: Assigning the result of an `if` expression
to a variable
The `number` variable will be bound to a value based on the outcome of the `if`
expression. Run this code to see what happens:
```bash
```
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/branches`
The value of number is: 5
```
@ -1224,7 +1223,7 @@ the following example?
Filename: src/main.rs
```rust,ignore
```
fn main() {
let condition = true;
@ -1247,15 +1246,15 @@ error[E0308]: if and else have incompatible types
--> src/main.rs:4:18
|
4 | let number = if condition {
| __________________^ starting here...
| __________________^
5 | | 5
6 | | } else {
7 | | "six"
8 | | };
| |_____^ ...ending here: expected integral variable, found reference
| |_____^ expected integral variable, found reference
|
= note: expected type `{integer}`
found type `&'static str`
found type `&str`
```
The expression in the `if` block evaluates to an integer, and the expression in
@ -1286,7 +1285,7 @@ like this:
Filename: src/main.rs
```rust,ignore
```
fn main() {
loop {
println!("again!");
@ -1296,11 +1295,13 @@ fn main() {
When we run this program, well see `again!` printed over and over continuously
until we stop the program manually. Most terminals support a keyboard shortcut,
ctrl-C, to halt a program that is stuck in a continual loop. Give it a try:
<span class="keystroke">ctrl-C</span>, to halt a program that is stuck in a
continual loop. Give it a try:
```bash
```
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
Running `target/debug/loops`
again!
again!
@ -1309,9 +1310,9 @@ again!
^Cagain!
```
The symbol `^C` represents where you pressed ctrl-C. You may or may not see the
word `again!` printed after the `^C`, depending on where the code was in the
loop when it received the halt signal.
The symbol `^C` represents where you pressed <span class="keystroke">ctrl-C
</span>. You may or may not see the word `again!` printed after the `^C`,
depending on where the code was in the loop when it received the halt signal.
Fortunately, Rust provides another, more reliable way to break out of a loop.
You can place the `break` keyword within the loop to tell the program when to
@ -1334,11 +1335,11 @@ prints another message and exits:
Filename: src/main.rs
```rust
```
fn main() {
let mut number = 3;
while number != 0 {
while number != 0 {
println!("{}!", number);
number = number - 1;
@ -1355,11 +1356,11 @@ true, the code runs; otherwise, it exits the loop.
#### Looping Through a Collection with `for`
You could use the `while` construct to loop over the elements of a collection,
such as an array. For example:
such as an array. For example, lets look at Listing 3-5:
Filename: src/main.rs
```rust
```
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
@ -1372,18 +1373,18 @@ fn main() {
}
```
<caption>
Listing 3-5: Looping through each element of a collection using a `while` loop
</caption>
Listing 3-5: Looping through each element of a collection
using a `while` loop
Here, the code counts up through the elements in the array. It starts at index
`0`, and then loops until it reaches the final index in the array (that is,
when `index < 5` is no longer true). Running this code will print out every
element in the array:
```bash
```
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
Running `target/debug/loops`
the value is: 10
the value is: 20
@ -1402,11 +1403,11 @@ code to perform the conditional check on every element on every iteration
through the loop.
As a more efficient alternative, you can use a `for` loop and execute some code
for each item in a collection. A `for` loop looks like this:
for each item in a collection. A `for` loop looks like this code in Listing 3-6:
Filename: src/main.rs
```rust
```
fn main() {
let a = [10, 20, 30, 40, 50];
@ -1416,9 +1417,8 @@ fn main() {
}
```
<caption>
Listing 3-6: Looping through each element of a collection using a `for` loop
</caption>
Listing 3-6: Looping through each element of a collection
using a `for` loop
When we run this code, well see the same output as in Listing 3-5. More
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
```rust
```
fn main() {
for number in (1..4).rev() {
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
attributes. In this chapter, well compare and contrast tuples with structs,
demonstrate how to use structs, and discuss how to define methods and
associated functions on structs to specify behavior associated with a structs
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the
building blocks for creating new types in your programs domain to take full
advantage of Rusts compile time type checking.
associated functions to specify behavior associated with a structs data. The
struct and *enum* (which is discussed in Chapter 6) concepts are the building
blocks for creating new types in your programs domain to take full advantage
of Rusts compile time type checking.
## Defining and Instantiating Structs
@ -61,9 +61,9 @@ Listing 5-2: Creating an instance of the `User` struct
To get a specific value from a struct, we can use dot notation. If we wanted
just this 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
can use the dot notation and assign into a particular field. Listing 5-3 shows
how to change the value in the `email` field of a mutable `User` instance:
use this value. If the instance is mutable, we can change a value by using the
dot notation and assigning into a particular field. Listing 5-3 shows how to
change the value in the `email` field of a mutable `User` instance:
```
let mut user1 = User {
@ -78,11 +78,14 @@ user1.email = String::from("anotheremail@example.com");
Listing 5-3: Changing the value in the `email` field of a `User` instance
Like any expression, we can implicitly return a new instance of a struct from a
function by constructing the new instance as the last expression in the
function body. Listing 5-4 shows a `build_user` function that returns a `User`
instance with the given `email` and `username`. The `active` field gets the
value of `true`, and the `sign_in_count` gets a value of `1`.
Note that the entire instance must be mutable; Rust doesnt allow us to mark
only certain fields as mutable. Also note that with any expression, we can
construct a new instance of the struct as the last expression in the function
body to implicitly return that new instance.
Listing 5-4 shows a `build_user` function that returns a `User` instance with
the given email and username. The `active` field gets the value of `true`, and
the `sign_in_count` gets a value of `1`.
```
fn build_user(email: String, username: String) -> User {
@ -98,24 +101,17 @@ fn build_user(email: String, username: String) -> User {
Listing 5-4: A `build_user` function that takes an email and username and
returns a `User` instance
Repeating the `email` field name and `email` variable, and the same for
`username`, is a bit tedious, though. It makes sense to name the function
arguments with the same name as the struct fields, but if the struct had more
fields, repeating each name would get even more annoying. Luckily, there's a
convenient shorthand!
It makes sense to name the function arguments with the same name as the struct
fields, but having to repeat the `email` and `username` field names and
variables is a bit tedious. If the struct had more fields, repeating each name
would get even more annoying. Luckily, there's a convenient shorthand!
### Field Init Shorthand when Variables Have the Same Name as Fields
### Using the Field Init Shorthand when Variables and Fields Have the Same Name
If you have variables with the same names as struct fields, you can use *field
init shorthand*. This can make functions that create new instances of structs
more concise.
In Listing 5-4, the parameter names `email` and `username` are the same as the
`User` 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.
Because the parameter names and the struct field names are exactly the same in
Listing 5-4, we can use the *field init shorthand* syntax to rewrite
`build_user` so that it behaves exactly the same but doesnt have the
repetition of `email` and `username` in the way shown in Listing 5-5.
```
fn build_user(email: String, username: String) -> User {
@ -128,16 +124,23 @@ fn build_user(email: String, username: String) -> User {
}
```
Listing 5-5: A `build_user` function that uses field init syntax since the
Listing 5-5: A `build_user` function that uses field init shorthand since the
`email` and `username` parameters have the same name as struct fields
Here, 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
Its often useful to create a new instance from an old instance, using most of
the old instances values but changing some. Listing 5-6 shows an example of
creating a new `User` instance in `user2` by setting the values of `email` and
`username` but using the same values for the rest of the fields from the
`user1` instance we created in Listing 5-2:
Its often useful to create a new instance of a struct that uses most of an old
instances values, but changes some. We do this using *struct update syntax*.
First, Listing 5-6 shows how we create a new `User` instance in `user2` without
the update syntax. We set new values for `email` and `username`, but otherwise
use the same values from `user1` that we created in Listing 5-2:
```
let user2 = User {
@ -148,15 +151,12 @@ let user2 = User {
};
```
Listing 5-6: Creating a new `User` instance, `user2`, and setting some fields
to the values of the same fields from `user1`
Listing 5-6: Creating a new `User` instance using some of the values from
`user1`
The *struct update syntax* achieves the same effect as the code in Listing 5-6
using less code. The struct update syntax uses `..` to specify that the
remaining fields not set explicitly should have the same value as the fields in
the given instance. The code in Listing 5-7 also creates an instance in `user2`
that has a different value for `email` and `username` but has the same values
for the `active` and `sign_in_count` fields that `user1` has:
Using struct update syntax, we can achieve the same effect with less code,
shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
explicitly set should have the same value as the fields in the given instance.
```
let user2 = User {
@ -170,14 +170,22 @@ Listing 5-7: Using struct update syntax to set a new `email` and `username`
values for a `User` instance but use the rest of the values from the fields of
the instance in the `user1` variable
The code in Listing 5-7 also creates an instance in `user2` that has a
different value for `email` and `username` but has the same values for the
`active` and `sign_in_count` fields from `user1`.
### Tuple Structs without Named Fields to Create Different Types
We can also define structs that look similar to tuples, called *tuple structs*,
that have the added meaning the struct name provides, but dont have names
associated with their fields, just the types of the fields. The definition of a
tuple struct still starts with the `struct` keyword and the struct name, which
are followed by the types in the tuple. For example, here are definitions and
usages of tuple structs named `Color` and `Point`:
associated with their fields, just the types of the fields. Tuple structs are
useful when you want to give the whole tuple a name and make the tuple be a
different type than other tuples, but naming each field as in a regular struct
would be verbose or redundant.
To define a tuple struct you start with the `struct` keyword and the struct
name followed by the types in the tuple. For example, here are definitions and
usages of two tuple structs named `Color` and `Point`:
```
struct Color(i32, i32, i32);
@ -189,8 +197,12 @@ let origin = Point(0, 0, 0);
Note that the `black` and `origin` values are different types, since theyre
instances of different tuple structs. Each struct we define is its own type,
even though the fields within the struct have the same types. Otherwise, tuple
struct instances behave like tuples, which we covered in Chapter 3.
even though the fields within the struct have the same types. For example, a
function that takes a parameter of type `Color` cannot take a `Point` as an
argument, even though both types are made up of three `i32` values. Otherwise,
tuple struct instances behave like tuples, which we covered in Chapter 3: you
can destructure them into their individual pieces, you can use a `.` followed
by the index to access an individual value, and so on.
### Unit-Like Structs without Any Fields
@ -204,10 +216,10 @@ PROD: START BOX
### Ownership of Struct Data
In the `User` struct definition in Listing 5-1, we used the owned `String`
type rather than the `&str` string slice type. This is a deliberate choice
because we want instances of this struct to own all of its data and for that
data to be valid for as long as the entire struct is valid.
In the `User` struct definition in Listing 5-1, we used the owned `String` type
rather than the `&str` string slice type. This is a deliberate choice because
we want instances of this struct to own all of its data and for that data to be
valid for as long as the entire struct is valid.
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
@ -251,8 +263,8 @@ error[E0106]: missing lifetime specifier
| ^ expected lifetime parameter
```
Well discuss how to fix these errors so you can store references in structs
in Chapter 10, but for now, well fix errors like these using owned types like
Well discuss how to fix these errors so you can store references in structs in
Chapter 10, but for now, well fix errors like these using owned types like
`String` instead of references like `&str`.
PROD: END BOX
@ -264,7 +276,7 @@ calculates the area of a rectangle. Well start with single variables, and the
refactor the program until were using structs instead.
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
just that in our projects *src/main.rs*:
@ -272,22 +284,22 @@ Filename: src/main.rs
```
fn main() {
let length1 = 50;
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(length1, width1)
area(width1, height1)
);
}
fn area(length: u32, width: u32) -> u32 {
length * width
fn area(width: u32, height: u32) -> u32 {
width * height
}
```
Listing 5-8: Calculating the area of a rectangle specified by its length and
width in separate variables
Listing 5-8: Calculating the area of a rectangle specified by its width and
height in separate variables
Now, run this program using `cargo run`:
@ -298,20 +310,20 @@ The area of the rectangle is 1500 square pixels.
### Refactoring with Tuples
Even though Listing 5-8 works and figures out the area of the rectangle by
calling the `area` function with each dimension, we can do better. The length
and the width are related to each other because together they describe one
calling the `area` function with each dimension, we can do better. The width
and the height are related to each other because together they describe one
rectangle.
The issue with this method is evident in the signature of `area`:
```
fn area(length: u32, width: u32) -> u32 {
fn area(width: u32, height: u32) -> u32 {
```
The `area` function is supposed to calculate the area of one rectangle, but the
function we wrote has two parameters. The parameters are related, but thats
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
page XX: by using tuples. Listing 5-9 shows another version of our program that
uses tuples:
@ -320,7 +332,7 @@ Filename: src/main.rs
```
fn main() {
let rect1 = (50, 30);
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
@ -333,16 +345,16 @@ fn area(dimensions: (u32, u32)) -> u32 {
}
```
Listing 5-8: Specifying the length and width of the rectangle with a tuple
Listing 5-8: Specifying the width and height of the rectangle with a tuple
In one way, this program is better. Tuples let us add a bit of structure, and
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
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
to keep in mind that `length` is the tuple index `0` and `width` is the tuple
to keep in mind that `width` is the tuple index `0` and `height` is the tuple
index `1`. If someone else worked on this code, they would have to figure this
out and keep it in mind as well. It would be easy to forget or mix up these
values and cause errors, because we havent conveyed the meaning of our data in
@ -358,12 +370,12 @@ Filename: src/main.rs
```
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
@ -372,16 +384,16 @@ fn main() {
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.length * rectangle.width
rectangle.width * rectangle.height
}
```
Listing 5-10: Defining a `Rectangle` struct
Here 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
in `main` we create a particular instance of a `Rectangle` that has a length of
50 and a width of 30.
defined the fields as `width` and `height`, both of which have type `u32`. Then
in `main` we create a particular instance of a `Rectangle` that has a width of
30 and a height of 50.
Our `area` function is now defined with one parameter, which weve named
`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
@ -390,10 +402,10 @@ take ownership of it. This way, `main` retains its ownership and can continue
using `rect1`, which is the reason we use the `&` in the function signature and
where we call the function.
The `area` function accesses the `length` and `width` fields of the `Rectangle`
The `area` function accesses the `width` and `height` fields of the `Rectangle`
instance. Our function signature for `area` now indicates exactly what we mean:
calculate the area of a `Rectangle` using its `length` and `width` fields. This
conveys that the length and width are related to each other, and gives
calculate the area of a `Rectangle` using its `width` and `height` fields. This
conveys that the width and height are related to each other, and gives
descriptive names to the values rather than using the tuple index values of `0`
and `1`—a win for clarity.
@ -408,12 +420,12 @@ Filename: src/main.rs
```
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {}", rect1);
}
@ -473,12 +485,12 @@ Filename: src/main.rs
```
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
@ -491,7 +503,7 @@ Now when we run the program, we wont get any errors and well see the
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
@ -502,8 +514,8 @@ When we use the `{:#?}` style in the example, the output will look like this:
```
rect1 is Rectangle {
length: 50,
width: 30
width: 30,
height: 50
}
```
@ -539,18 +551,18 @@ Filename: src/main.rs
```
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
@ -638,9 +650,9 @@ Filename: src/main.rs
```
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect2 = Rectangle { length: 40, width: 10 };
let rect3 = Rectangle { length: 45, width: 60 };
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
@ -667,8 +679,8 @@ parameter will be by looking at the code that calls the method:
read `rect2` (rather than write, which would mean wed need a mutable borrow),
and we want `main` to retain ownership of `rect2` so we can use it again after
calling the `can_hold` method. The return value of `can_hold` will be a
boolean, and the implementation will check whether the length and width of
`self` are both greater than the length and width of the other `Rectangle`,
boolean, and the implementation will check whether the width and height of
`self` are both greater than the width and height of the other `Rectangle`,
respectively. Lets add the new `can_hold` method to the `impl` block from
Listing 5-13, shown in Listing 5-15:
@ -677,11 +689,11 @@ Filename: src/main.rs
```
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width
self.width > other.width && self.height > other.height
}
}
```
@ -705,7 +717,7 @@ function.
Associated functions are often used for constructors that will return a new
instance of the struct. For example, we could provide an associated function
that would have one dimension parameter and use that as both length and width,
that would have one dimension parameter and use that as both width and height,
thus making it easier to create a square `Rectangle` rather than having to
specify the same value twice:
@ -714,7 +726,7 @@ Filename: src/main.rs
```
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { length: size, width: size }
Rectangle { width: size, height: size }
}
}
```
@ -733,13 +745,13 @@ in its own `impl` block:
```
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width
self.width > other.width && self.height > other.height
}
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,10 +14,7 @@ advanced features to show you how to:
* Extend Cargo with your own custom commands
Cargo can do even more than what we can cover in this chapter too, so for a
full explanation, see its documentation at *http://doc.rust-lang.org/cargo/*.
<!--can you give a link to the documentation?-->
<!-- done /Carol -->
full explanation, see its documentation at *https://doc.rust-lang.org/cargo/*.
## Customizing Builds with Release Profiles
@ -26,36 +23,14 @@ different configurations, to allow the programmer more control over various
options for compiling your code. Each profile is configured independently of
the others.
<!-- To be clear, are these release profiles pre-defined profiles that you use
for different things? Can you lay that out more explicitly, give a more
detailed definition? That seems super useful, but I'm not sure I'm following
what they actually are. -->
<!-- They are pre-defined, we've tried to clarify /Carol -->
Cargo has two main profiles you should know about: the `dev` profile Cargo uses
when you run `cargo build`, and the `release` profile Cargo uses when you run
`cargo build --release`. The `dev` profile is defined with good defaults for
developing, and likewise the `release` profile has good defaults for release
builds.
Cargo has four profiles defined with good default configurations for each use
case. Cargo uses the different profiles based on which command youre running.
The commands correspond to the profiles as shown in Table 14-1:
<!-- Hm, so these profiles aren't built-in, just supported? and used for what
for cargo build? How do you use a particular profile in a build, is it chosen
by default? Do you have to specify? -->
<!-- They are built in with defaults. We've tried to clarify by changing this
to a table and adding some more explanation, is this better? /Carol -->
| Command | Profile |
|-------------------------|-----------|
| `cargo build` | `dev` |
| `cargo build --release` | `release` |
| `cargo test` | `test` |
| `cargo doc` | `doc` |
Table 14-1: Which profile is used when you run different Cargo commands
This may be familiar from the output of your builds, which shows the profile
used in the build:
<!-- Above-is that what you meant here? -->
<!-- Yep! /Carol -->
These names may be familiar from the output of your builds, which shows the
profile used in the build:
```
$ cargo build
@ -64,31 +39,19 @@ $ cargo build --release
Finished release [optimized] target(s) in 0.0 secs
```
The “dev” and “release” notifications here indicate that the compiler is
using different profiles.
<!-- which profile is "debug" associated with? As you can probably tell, I'm
not confident in my interpretation here, I think we need more info -->
<!-- Sorry, this was an inconsistency in cargo that we actually noticed while
writing this section and has since been fixed, but then I think we missed
updating this spot. `debug` should be gone. /Carol -->
The “dev” and “release” notifications here indicate that the compiler is using
different profiles.
### Customizing Release Profiles
<!-- Do we mean that the profiles are all already stored in Cargo.toml, or you
have to add the entire code to cargo.toml? It seems like the former from the
writing, but looking through toml files I've made the latter seems to be true.
If you have multiple profiles in the toml, how do you choose which one to use?
-->
<!-- We've tried to clarify below. Please let me know if this is still unclear,
I'm confused about how you're drawing your conclusions. /Carol -->
Cargo has default settings for each of the profiles that apply when there
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
override any subset of the default settings. For example, here are the default
values for the `opt-level` setting for the `dev` and `release` profiles:
Filename: Cargo.toml
```
[profile.dev]
opt-level = 0
@ -112,15 +75,6 @@ them in *Cargo.toml*. If we wanted to use optimization level 1 in the
development profile, for example, we can add these two lines to our projects
*Cargo.toml*:
<!-- So do we choose which profile to use when? How do we do that? Or is that
determined automatically by Rust, and if so, how? I think we need to show that
somewhere around here -->
<!-- Which profile is used is determined by which command you're running, which
we tried to show above. I hope the table added above has clarified this, if
not, please suggest further wording above, but the reader should understand
which profile gets used when by this point and I don't think we should repeat
it again here. /Carol -->
Filename: Cargo.toml
```
@ -134,7 +88,7 @@ will use the defaults for the `dev` profile plus our customization to
optimizations than the default, but not as many as a release build.
For the full list of configuration options and defaults for each profile, see
Cargos documentation at *http://doc.rust-lang.org/cargo/*.
Cargos documentation at *https://doc.rust-lang.org/cargo/*.
## Publishing a Crate to Crates.io
@ -158,13 +112,9 @@ contents of documentation comments for public API items, intended for
programmers interested in knowing how to *use* your crate, as opposed to how
your crate is *implemented*.
<!-- Doc comments support markdown but dont require markdown, is that right?
Just wanted to make that distinction -->
<!-- yes -->
Documentation comments use `///` instead of `//` and support Markdown notation
for formatting the text if 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`:
Filename: src/lib.rs
@ -184,11 +134,7 @@ pub fn add_one(x: i32) -> i32 {
}
```
Listing 14-2: A documentation comment for a function
<!-- At some point, a screenshot of how this is rendered in HTML could be really
useful here, what you do think? -->
<!-- Yup! /Carol -->
Listing 14-1: A documentation comment for a function
Here, we give a description of what the `add_one` function does, then start a
section with the heading “Examples”, and code that demonstrates how to use the
@ -201,36 +147,28 @@ For convenience, running `cargo doc --open` will build the HTML for your
current crates documentation (as well as the documentation for all of your
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
gets rendered, shown here in Figure 14-3:
gets rendered, shown here in Figure 14-2:
<img alt="Rendered HTML documentation for the `add_one` function of `my_crate`" src="img/trpl14-03.png" class="center" />
Figure 14-3: HTML documentation for the `add_one` function
<!--Above - I added this line to describe what we're doing, encourage good
practice, can you add/edit where necessary? These will generate as HTML when
the code is run, is that how it works? -->
<!-- Not when the code is run, when the programmer runs `cargo doc`. That
doesn't run the programmer's code, really, not in the way `cargo run` runs it
anyway. We've tried clarifying as well as adding a screenshot. /Carol -->
Figure 14-2: HTML documentation for the `add_one` function
#### Commonly Used Sections
We used the `# Examples` markdown heading in Listing 14-2 to create a section
We used the `# Examples` markdown heading in Listing 14-1 to create a section
in the HTML with the title “Examples”. Some other sections that crate authors
commonly use in their documentation include:
- Panics: The scenarios in which this function could `panic!`. Callers of this
function who dont want their programs to panic should make sure that they
dont call this function in these situations.
- Errors: If this function returns a `Result`, describing the kinds of errors
that might occur and what conditions might cause those errors to be returned
can be helpful to callers so that they can write code to handle the different
kinds of errors in different ways.
- Safety: If this function uses `unsafe` code (which we will discuss in Chapter
19), there should be a section covering the invariants that this function
expects callers to uphold in order for the code in `unsafe` blocks to
function correctly.
* **Panics**: The scenarios in which this function could `panic!`. Callers of
this function who dont want their programs to panic should make sure that
they dont call this function in these situations.
* **Errors**: If this function returns a `Result`, describing the kinds of
errors that might occur and what conditions might cause those errors to be
returned can be helpful to callers so that they can write code to handle the
different kinds of errors in different ways.
* **Safety**: If this function is `unsafe` to call (we will discuss unsafety in
Chapter 19), there should be a section explaining why the function is unsafe
and covering the invariants that this function expects callers to uphold.
Most documentation comment sections dont need all of these sections, but this
is a good list to check to remind you of the kinds of things that people
@ -244,7 +182,7 @@ running `cargo test` will run the code examples in your documentation as tests!
Nothing is better than documentation with examples. Nothing is worse than
examples that dont actually work because the code has changed since the
documentation has been written. Try running `cargo test` with the documentation
for the `add_one` function like in Listing 14-2; you should see a section in
for the `add_one` function like in Listing 14-1; you should see a section in
the test results like this:
```
@ -262,21 +200,16 @@ tests catch that the example and the code are out of sync from one another!
#### Commenting Contained Items
<!-- I'm not clear what this comment does that's different, what do you mean by
"comment containing items"? The lingo might just be going over my head here -->
<!-- we've tried to reword and we've changed the example, is this clearer?
/Carol -->
Theres another style of doc comment, `//!`, that adds documentation to the
item that contains the comments, rather than adding documentation to the items
following the comments. These are typically used inside the crate root file
(*src/lib.rs*) or inside a modules root (*mod.rs*) to document the crate or
the module as a whole.
(*src/lib.rs* by convention) or inside a module to document the crate or the
module as a whole.
For example, if we wanted to add documentation that described the purpose of
the `my_crate` crate that contains the `add_one` function, we can add
documentation comments that start with `//!` to the beginning of *src/lib.rs*
as shown in Listing 14-4:
as shown in Listing 14-3:
Filename: src/lib.rs
@ -290,7 +223,7 @@ Filename: src/lib.rs
// ...snip...
```
Listing 14-4: Documentation for the `my_crate` crate as a whole
Listing 14-3: Documentation for the `my_crate` crate as a whole
Notice there isnt any code after the last line that begins with `//!`. Because
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
page of the documentation for `my_crate` above the list of public items in the
crate, as shown in Figure 14-5:
crate, as shown in Figure 14-4:
<img alt="Rendered HTML documentation with a comment for the crate as a whole" src="img/trpl14-05.png" class="center" />
Figure 14-5: Rendered documentation for `my_crate` including the comment
Figure 14-4: Rendered documentation for `my_crate` including the comment
describing the crate as a whole
<!-- I'm not sure what we're looking at here, that's different from just using
///, can you point it out, talk about it? -->
<!-- Does the screenshot help? /Carol -->
Documentation comments within items are useful for describing crates and
modules especially. Use them to talk about the purpose of the container overall
to help users of your crate understand your organization.
### Exporting a Convenient Public API with `pub use`
#### Exporting a Convenient Public API with `pub use`
In Chapter 7, we covered how to organize our code into modules with the `mod`
keyword, how to make items public with the `pub` keyword, and how to bring
@ -328,11 +257,6 @@ also be annoyed at having to type `use
my_crate::some_module::another_module::UsefulType;` rather than `use
my_crate::UsefulType;`.
<!-- Can you outline why, briefly, here? Reading on, is it something like:
because some useful functions might be buried within modules that the user is
unaware of -->
<!-- Yes, that's pretty much it. We've clarified above. /Carol -->
The structure of your public API is a major consideration when publishing a
crate. People who use your crate are less familiar with the structure than you
are, and might have trouble finding the pieces they want to use if the module
@ -345,13 +269,10 @@ to your private structure, using `pub use`. Re-exporting takes a public item in
one location and makes it public in another location as if it was defined in
the other location instead.
<!-- Can you give a quick definition of "re-export" here? -->
<!-- Yup! /Carol -->
For example, say we made a library named `art` for modeling artistic concepts.
Within this library is a `kinds` module containing two enums named
`PrimaryColor` and `SecondaryColor` and a `utils` module containing a function
named `mix` as shown in Listing 14-6:
named `mix` as shown in Listing 14-5:
Filename: src/lib.rs
@ -387,16 +308,16 @@ pub mod utils {
}
```
Listing 14-6: An `art` library with items organized into `kinds` and `utils`
Listing 14-5: An `art` library with items organized into `kinds` and `utils`
modules
The front page of the documentation for this crate generated by `cargo doc`
would look like Figure 14-7:
would look like Figure 14-6:
<img alt="Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules" src="img/trpl14-07.png" class="center" />
Figure 14-7: Front page of the documentation for `art`
that lists the `kinds` and `utils` modules
Figure 14-6: Front page of the documentation for `art` that lists the `kinds`
and `utils` modules
Note that the `PrimaryColor` and `SecondaryColor` types arent listed on the
front page, nor is the `mix` function. We have to click on `kinds` and `utils`
@ -404,7 +325,7 @@ in order to see them.
Another crate depending on this library would need `use` statements that import
the items from `art` including specifying the module structure 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:
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
<!--Below -- just to clarify, the "users of this crate" refers to people using
the crate in 14-8 that `uses` art, is that right? I want to make sure I'm
following accurately! -->
<!-- No, it refers to the users of the `art` crate. I've tried to clarify
/Carol -->
The author of the code in Listing 14-8 that uses the `art` crate had to figure
The author of the code in Listing 14-7 that uses the `art` crate had to figure
out that `PrimaryColor` is in the `kinds` module and `mix` is in the `utils`
module. The module structure of the `art` crate is more relevant to developers
working on the `art` crate than developers using the `art` crate. The internal
@ -442,8 +357,8 @@ confusion in having to figure out where to look and inconvenience in having to
specify the module names in the `use` statements.
To remove the internal organization from the public API, we can take the `art`
crate code from Listing 14-6 and add `pub use` statements to re-export the
items at the top level, as shown in Listing 14-9:
crate code from Listing 14-5 and add `pub use` statements to re-export the
items at the top level, as shown in Listing 14-8:
Filename: src/lib.rs
@ -465,22 +380,20 @@ pub mod utils {
}
```
Listing 14-9: Adding `pub use` statements to re-export items
<!-- Will add ghosting in libreoffice /Carol -->
Listing 14-8: Adding `pub use` statements to re-export items
The API documentation generated with `cargo doc` for this crate will now list
and link re-exports on the front page as shown in Figure 14-10, which makes
and link re-exports on the front page as shown in Figure 14-9, which makes
these types easier to find.
<img alt="Rendered documentation for the `art` crate with the re-exports on the front page" src="img/trpl14-10.png" class="center" />
Figure 14-10: Front page of the documentation for `art` that lists the
Figure 14-9: Front page of the documentation for `art` that lists the
re-exports
Users of the `art` crate can still see and choose to use the internal structure
as in Listing 14-8, or they can use the more convenient structure from Listing
14-9, as shown in Listing 14-11:
as in Listing 14-7, or they can use the more convenient structure from Listing
14-8, as shown in Listing 14-10:
Filename: src/main.rs
@ -495,9 +408,7 @@ fn main() {
}
```
Listing 14-11: A program using the re-exported items from the `art` crate
<!-- Will add ghosting in libreoffice /Carol -->
Listing 14-10: A program using the re-exported items from the `art` crate
In cases where there are many nested modules, re-exporting the types at the top
level with `pub use` can make a big difference in the experience of people who
@ -514,7 +425,7 @@ structure differs from their public API.
Before you can publish any crates, you need to create an account on crates.io
and get an API token. To do so, visit the home page at *https://crates.io* and
log in via a GitHub account---the GitHub account is a requirement for now, but
log in via a GitHub accountthe GitHub account is a requirement for now, but
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
retrieve your API key. Then run the `cargo login` command with your API key,
@ -525,7 +436,7 @@ $ cargo login abcdefghijklmnopqrstuvwxyz012345
```
This command will inform Cargo of your API token and store it locally in
*~/.cargo/credentials*. Note that this token is a **secret** and should not be
*~/.cargo/credentials*. Note that this token is a *secret* and should not be
shared with anyone else. If it is shared with anyone for any reason, you should
revoke it and generate a new token on Crates.io.
@ -535,9 +446,6 @@ Now you have an account, and lets say you already have a crate you want to
publish. Before publishing, youll need to add some metadata to your crate by
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
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
@ -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
use for publishing like so:
Filename: Cargo.toml
```
[package]
name = "guessing_game"
@ -575,16 +485,14 @@ Package Data Exchange (SPDX) at *http://spdx.org/licenses/* lists the
identifiers you can use for this value. For example, to specify that youve
licensed your crate using the MIT License, add the `MIT` identifier:
Filename: Cargo.toml
```
[package]
name = "guessing_game"
license = "MIT"
```
<!-- Can you give an example of what a license identifier value looks like? It
is a alphanumerical code? -->
<!-- Mostly, yeah. /Carol -->
If you want to use a license that 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
`license-file` to specify the name of that file instead of using the `license`
@ -592,7 +500,7 @@ key.
Guidance on which license is right for your project is out of scope for this
book. Many people in the Rust community choose to license their projects in the
same way as Rust itself, with a dual license of `MIT/Apache-2.0`---this
same way as Rust itself, with a dual license of `MIT/Apache-2.0`this
demonstrates that you can also specify multiple license identifiers separated
by a slash.
@ -600,6 +508,8 @@ So, with a unique name, the version, and author details that `cargo new` added
when you created the crate, your description, and the license you chose added,
the *Cargo.toml* for a project thats ready to publish might look like this:
Filename: Cargo.toml
```
[package]
name = "guessing_game"
@ -611,7 +521,7 @@ license = "MIT/Apache-2.0"
[dependencies]
```
Cargo's documentation at *http://doc.rust-lang.org/cargo/* describes other
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
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,
you change the `version` value specified in your *Cargo.toml* and republish.
Use the Semantic Versioning rules at *http://semver.org/* to decide what an appropriate next
version number is based on the kinds of changes youve made. Then run `cargo
publish` to upload the new version.
Use the Semantic Versioning rules at *http://semver.org/* to decide what an
appropriate next version number is based on the kinds of changes youve made.
Then run `cargo publish` to upload the new version.
### Removing Versions from Crates.io with `cargo yank`
@ -660,11 +569,11 @@ projects from adding them as a new dependency. This is useful when a version of
a crate ends up being broken for one reason or another. For situations such as
this, Cargo supports *yanking* a version of a crate.
Yanking a version prevents new projects from starting to depend on that
version while allowing all existing projects that depend on it to continue to
download and depend on that version. Essentially, a yank means that all
projects with a *Cargo.lock* will not break, while any future *Cargo.lock*
files generated will not use the yanked version.
Yanking a version prevents new projects from starting to depend on that version
while allowing all existing projects that depend on it to continue to download
and depend on that version. Essentially, a yank means that all projects with a
*Cargo.lock* will not break, while any future *Cargo.lock* files generated will
not use the yanked version.
To yank a version of a crate, run `cargo yank` and specify which version you
want to yank:
@ -711,6 +620,8 @@ We need to modify the binary packages *Cargo.toml* and add a `[workspace]`
section to tell Cargo the `adder` package is a workspace. Add this at the
bottom of the file:
Filename: Cargo.toml
```
[workspace]
```
@ -719,26 +630,20 @@ Like many Cargo features, workspaces support convention over configuration: we
dont need to add anything more than this to *Cargo.toml* to define our
workspace as long as we follow the convention.
<!-- Below -- any crates what depends on, specifically? The program? -->
<!-- They're all programs. We mean the top-level crate in the workspace here,
I've tried to clarify. /Carol -->
### Specifying Workspace Dependencies
The workspace convention says any crates in any subdirectories that the
top-level crate depends on are part of the workspace. Any crate, whether in a
workspace or not, can specify that it has a dependency on a crate in a local
directory by using the `path` attribute on the dependency specification in
*Cargo.toml*. If a crate has the `[workspace]` key and we specify path
dependencies where the paths are subdirectories of the crates directory, those
dependent crates will be considered part of the workspace. Lets specify in the
*Cargo.toml* for the top-level `adder` crate that it will have a dependency on
an `add-one` crate that will be in the `add-one` subdirectory, by changing
*Cargo.toml* to look like this:
By default, Cargo will include all transitive path dependencies. A *path
dependency* is when any crate, whether in a workspace or not, specifies that it
has a dependency on a crate in a local directory by using the `path` attribute
on the dependency specification in *Cargo.toml*. If a crate has the
`[workspace]` key, or if the crate is itself part of a workspace, and we
specify path dependencies where the paths are subdirectories of the crates
directory, those dependent crates will be considered part of the workspace.
Lets specify in the *Cargo.toml* for the top-level `adder` crate that it will
have a dependency on an `add-one` crate that will be in the `add-one`
subdirectory, by changing *Cargo.toml* to look like this:
<!-- Above, what is the path dependency actually doing here, can you fill out
the paragraph above? -->
<!-- done /Carol -->
Filename: Cargo.toml
```
[dependencies]
@ -751,10 +656,6 @@ and are assumed to come from Crates.io.
### Creating the Second Crate in the Workspace
<!-- You can see I'm adding headings, here, trying to add some more navigable
structure -- can you improve these? I'm not sure mine are accurate -->
<!-- Yep! /Carol -->
Next, while in the `adder` directory, generate an `add-one` crate:
```
@ -784,12 +685,11 @@ pub fn add_one(x: i32) -> i32 {
}
```
<!-- below -- Where are we adding the extern crate line? -->
<!-- at the top, where all the extern crate lines go and as illustrated by the listing /Carol -->
Open up *src/main.rs* for `adder` and add an `extern crate` line at the top of
the file to bring the new `add-one` library crate into scope. Then change the
`main` function to call the `add_one` function, as in Listing 14-12:
`main` function to call the `add_one` function, as in Listing 14-11:
Filename: src/main.rs
```
extern crate add_one;
@ -800,7 +700,7 @@ fn main() {
}
```
Listing 14-12: Using the `add-one` library crate from the `adder` crate
Listing 14-11: Using the `add-one` library crate from the `adder` crate
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
necessary.
<!-- Above -- I have no idea what this means for our project here, can you put
it in more practical terms, or otherwise maybe just explain what this means for
the user? -->
<!-- I added more explanation for the target directory in this section and
added more explanation for the Cargo.lock in the next section, since the
Cargo.lock advantages aren't as visible until you start adding dependencies on
external crates. What do you think? /Carol -->
#### Depending on an External Crate in a Workspace
Also notice the workspace only has one *Cargo.lock*, rather than having a
@ -1001,9 +893,6 @@ does not have an `--all` flag or a `-p` flag, so it is necessary to change to
each crates directory and run `cargo publish` on each crate in the workspace
in order to publish them.
<!-- What does that mean, we have to publish them all one at a time?-->
<!-- Yep, we've tried to clarify /Carol -->
Now try adding an `add-two` crate to this workspace in a similar way as the
`add-one` crate for some more practice!
@ -1024,13 +913,10 @@ target that isnt runnable on its own but is suitable for including within
other programs. Usually, crates have information in the *README* file about
whether a crate is a library, has a binary target, or both.
<!-- What is a binary target, and how do you know if a package has one? -->
<!-- Added /Carol -->
All binaries from `cargo install` are put into the installation roots *bin*
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
`$PATH` to be able to run programs youve gotten through `cargo install`.
configurations, this will be `$HOME/.cargo/bin`. Ensure that directory is in
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
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
The precedence of Rust binary operators is ordered as follows, going from
strong to weak:
The precedence of Rust operators is ordered as follows, going from strong to
weak. Binary Operators at the same precedence level are evaluated in the order
given by their associativity.
```text
as :
* / %
+ -
<< >>
&
^
|
== != < > <= >=
&&
||
.. ...
<-
=
```
Operators at the same precedence level are evaluated left-to-right. Unary
operators have the same precedence level and are stronger than any of the
binary operators.
| Operator | Associativity |
|-----------------------------|---------------------|
| `?` | |
| Unary `-` `*` `!` `&` `&mut` | |
| `as` `:` | left to right |
| `*` `/` `%` | left to right |
| `+` `-` | left to right |
| `<<` `>>` | left to right |
| `&` | left to right |
| `^` | left to right |
| <code>&#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
parentheses, `(` and `)`.
Also note that the function body is wrapped in curly braces, `{` and `}`. Rust
requires these around all function bodies. Its considered good style to put
the opening curly brace on the same line as the function declaration, with one
space in between.
Also note that the function body is wrapped in curly brackets, `{` and `}`.
Rust requires these around all function bodies. Its considered good style to
put the opening curly bracket on the same line as the function declaration,
with one space in between.
Inside the `main` function:
@ -319,7 +319,7 @@ $ cargo build
```
This should have created an executable file in *target/debug/hello_cargo* (or
*target\debug\hello_cargo.exe* on Windows), which you can run with this command:
*target\\debug\\hello_cargo.exe* on Windows), which you can run with this command:
```text
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows

View File

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

View File

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

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:
```text
error[E0282]: unable to infer enough type information about `_`
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^ cannot infer type for `_`
|
= note: type annotations or generic parameter binding required
| ^^^^^
| |
| cannot infer type for `_`
| consider giving `guess` a type
```
Youll see different type annotations as we discuss the various data types.
@ -42,11 +43,11 @@ work in Rust.
#### Integer Types
An *integer* is a number without a fractional component. We used one integer
type earlier in this chapter, the `i32` type. This type declaration indicates
that the value its associated with should be a signed integer (hence the `i`,
as opposed to a `u` for unsigned) that takes up 32 bits of space. Table 3-1
shows the built-in integer types in Rust. Each variant in the Signed and
Unsigned columns (for example, *i32*) can be used to declare the type of an
type earlier in this chapter, the `u32` type. This type declaration indicates
that the value its associated with should be an unsigned integer (signed
integer types start with `i` instead of `u`) that takes up 32 bits of space.
Table 3-1 shows the built-in integer types in Rust. Each variant in the Signed
and Unsigned columns (for example, *i16*) can be used to declare the type of an
integer value.
<span class="caption">Table 3-1: Integer Types in Rust</span>
@ -104,12 +105,8 @@ youd use `isize` or `usize` is when indexing some sort of collection.
Rust also has two primitive types for *floating-point numbers*, which are
numbers with decimal points. Rusts floating-point types are `f32` and `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.
Its possible to use an `f64` type on 32-bit systems, but it will be slower
than using an `f32` type on those systems. Most of the time, trading potential
worse performance for better precision is a reasonable initial choice, and you
should benchmark your code if you suspect floating-point size is a problem in
your situation.
because on modern CPUs its roughly the same speed as `f32` but is capable of
more precision.
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
`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>
@ -348,6 +346,7 @@ Running this code using `cargo run` produces the following result:
```text
$ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays`
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6

View File

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

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
`number` has a value less than 5. The block of code we want to execute if the
condition is true is placed immediately after the condition inside curly
braces. Blocks of code associated with the conditions in `if` expressions are
brackets. Blocks of code associated with the conditions in `if` expressions are
sometimes called *arms*, just like the arms in `match` expressions that we
discussed in the “Comparing the Guess to the Secret Number” section of
Chapter 2. Optionally, we can also include an `else` expression, which we chose
@ -49,6 +49,7 @@ Try running this code; you should see the following output:
```text
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
condition was true
```
@ -65,6 +66,7 @@ Run the program again, and look at the output:
```text
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
condition was false
```
@ -149,6 +151,7 @@ see the following output:
```text
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
number is divisible by 3
```
@ -193,6 +196,7 @@ expression. Run this code to see what happens:
```text
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/branches`
The value of number is: 5
```
@ -238,7 +242,7 @@ error[E0308]: if and else have incompatible types
| |_____^ expected integral variable, found reference
|
= note: expected type `{integer}`
found type `&'static str`
found type `&str`
```
The expression in the `if` block evaluates to an integer, and the expression in
@ -285,6 +289,7 @@ continual loop. Give it a try:
```text
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
Running `target/debug/loops`
again!
again!
@ -339,7 +344,7 @@ true, the code runs; otherwise, it exits the loop.
#### Looping Through a Collection with `for`
You could use the `while` construct to loop over the elements of a collection,
such as an array. For example:
such as an array. For example, lets look at Listing 3-5:
<span class="filename">Filename: src/main.rs</span>
@ -367,6 +372,7 @@ element in the array:
```text
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
Running `target/debug/loops`
the value is: 10
the value is: 20
@ -385,7 +391,7 @@ code to perform the conditional check on every element on every iteration
through the loop.
As a more efficient alternative, you can use a `for` loop and execute some code
for each item in a collection. A `for` loop looks like this:
for each item in a collection. A `for` loop looks like this code in Listing 3-6:
<span class="filename">Filename: src/main.rs</span>

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
attributes. In this chapter, well compare and contrast tuples with structs,
demonstrate how to use structs, and discuss how to define methods and
associated functions on structs to specify behavior associated with a structs
data. The struct and *enum* (which is discussed in Chapter 6) concepts are the
building blocks for creating new types in your programs domain to take full
advantage of Rusts compile time type checking.
associated functions to specify behavior associated with a structs data. The
struct and *enum* (which is discussed in Chapter 6) concepts are the building
blocks for creating new types in your programs domain to take full advantage
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
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
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
by specifying concrete values for each of the fields. We create an instance by
stating the name of the struct, and then add curly braces containing `key:
stating the name of the struct, and then add curly brackets containing `key:
value` pairs where the keys are the names of the fields and the values are the
data we want to store in those fields. We dont have to specify the fields in
the same order in which we declared them in the struct. In other words, the
@ -54,9 +54,9 @@ struct</span>
To get a specific value from a struct, we can use dot notation. If we wanted
just this 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
can use the dot notation and assign into a particular field. Listing 5-3 shows
how to change the value in the `email` field of a mutable `User` instance:
use this value. If the instance is mutable, we can change a value by using the
dot notation and assigning into a particular field. Listing 5-3 shows how to
change the value in the `email` field of a mutable `User` instance:
```rust
# struct User {
@ -79,11 +79,14 @@ user1.email = String::from("anotheremail@example.com");
<span class="caption">Listing 5-3: Changing the value in the `email` field of a
`User` instance</span>
Like any expression, we can implicitly return a new instance of a struct from a
function by constructing the new instance as the last expression in the
function body. Listing 5-4 shows a `build_user` function that returns a `User`
instance with the given `email` and `username`. The `active` field gets the
value of `true`, and the `sign_in_count` gets a value of `1`.
Note that the entire instance must be mutable; Rust doesnt allow us to mark
only certain fields as mutable. Also note that as with any expression, we can
construct a new instance of the struct as the last expression in the function
body to implicitly return that new instance.
Listing 5-4 shows a `build_user` function that returns a `User` instance with
the given email and username. The `active` field gets the value of `true`, and
the `sign_in_count` gets a value of `1`.
```rust
# struct User {
@ -106,24 +109,17 @@ fn build_user(email: String, username: String) -> User {
<span class="caption">Listing 5-4: A `build_user` function that takes an email
and username and returns a `User` instance</span>
Repeating the `email` field name and `email` variable, and the same for
`username`, is a bit tedious, though. It makes sense to name the function
arguments with the same name as the struct fields, but if the struct had more
fields, repeating each name would get even more annoying. Luckily, theres a
convenient shorthand!
It makes sense to name the function arguments with the same name as the struct
fields, but having to repeat the `email` and `username` field names and
variables is a bit tedious. If the struct had more fields, repeating each name
would get even more annoying. Luckily, there's a convenient shorthand!
### Field Init Shorthand when Variables Have the Same Name as Fields
### Using the Field Init Shorthand when Variables and Fields Have the Same Name
If you have variables with the same names as struct fields, you can use *field
init shorthand*. This can make functions that create new instances of structs
more concise.
In Listing 5-4, the parameter names `email` and `username` are the same as the
`User` 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.
Because the parameter names and the struct field names are exactly the same in
Listing 5-4, we can use the *field init shorthand* syntax to rewrite
`build_user` so that it behaves exactly the same but doesnt have the
repetition of `email` and `username` in the way shown in Listing 5-5.
```rust
# struct User {
@ -144,16 +140,23 @@ fn build_user(email: String, username: String) -> User {
```
<span class="caption">Listing 5-5: A `build_user` function that uses field init
syntax since the `email` and `username` parameters have the same name as struct
fields</span>
shorthand since the `email` and `username` parameters have the same name as
struct fields</span>
Here, 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
Its often useful to create a new instance from an old instance, using most of
the old instances values but changing some. Listing 5-6 shows an example of
creating a new `User` instance in `user2` by setting the values of `email` and
`username` but using the same values for the rest of the fields from the
`user1` instance we created in Listing 5-2:
Its often useful to create a new instance of a struct that uses most of an old
instances values, but changes some. We do this using *struct update syntax*.
First, Listing 5-6 shows how we create a new `User` instance in `user2` without
the update syntax. We set new values for `email` and `username`, but otherwise
use the same values from `user1` that we created in Listing 5-2:
```rust
# struct User {
@ -178,15 +181,12 @@ let user2 = User {
};
```
<span class="caption">Listing 5-6: Creating a new `User` instance, `user2`, and
setting some fields to the values of the same fields from `user1`</span>
<span class="caption">Listing 5-6: Creating a new `User` instance using some of
the values from `user1`</span>
The *struct update syntax* achieves the same effect as the code in Listing 5-6
using less code. The struct update syntax uses `..` to specify that the
remaining fields not set explicitly should have the same value as the fields in
the given instance. The code in Listing 5-7 also creates an instance in `user2`
that has a different value for `email` and `username` but has the same values
for the `active` and `sign_in_count` fields that `user1` has:
Using struct update syntax, we can achieve the same effect with less code,
shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
explicitly set should have the same value as the fields in the given instance.
```rust
# struct User {
@ -214,14 +214,22 @@ let user2 = User {
`email` and `username` values for a `User` instance but use the rest of the
values from the fields of the instance in the `user1` variable</span>
The code in Listing 5-7 also creates an instance in `user2` that has a
different value for `email` and `username` but has the same values for the
`active` and `sign_in_count` fields from `user1`.
### Tuple Structs without Named Fields to Create Different Types
We can also define structs that look similar to tuples, called *tuple structs*,
that have the added meaning the struct name provides, but dont have names
associated with their fields, just the types of the fields. The definition of a
tuple struct still starts with the `struct` keyword and the struct name, which
are followed by the types in the tuple. For example, here are definitions and
usages of tuple structs named `Color` and `Point`:
associated with their fields, just the types of the fields. Tuple structs are
useful when you want to give the whole tuple a name and make the tuple be a
different type than other tuples, but naming each field as in a regular struct
would be verbose or redundant.
To define a tuple struct you start with the `struct` keyword and the struct
name followed by the types in the tuple. For example, here are definitions and
usages of two tuple structs named `Color` and `Point`:
```rust
struct Color(i32, i32, i32);
@ -233,8 +241,12 @@ let origin = Point(0, 0, 0);
Note that the `black` and `origin` values are different types, since theyre
instances of different tuple structs. Each struct we define is its own type,
even though the fields within the struct have the same types. Otherwise, tuple
struct instances behave like tuples, which we covered in Chapter 3.
even though the fields within the struct have the same types. For example, a
function that takes a parameter of type `Color` cannot take a `Point` as an
argument, even though both types are made up of three `i32` values. Otherwise,
tuple struct instances behave like tuples, which we covered in Chapter 3: you
can destructure them into their individual pieces, you can use a `.` followed
by the index to access an individual value, and so on.
### Unit-Like Structs without Any Fields

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.
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
just that in our projects *src/main.rs*:
@ -13,22 +13,22 @@ just that in our projects *src/main.rs*:
```rust
fn main() {
let length1 = 50;
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(length1, width1)
area(width1, height1)
);
}
fn area(length: u32, width: u32) -> u32 {
length * width
fn area(width: u32, height: u32) -> u32 {
width * height
}
```
<span class="caption">Listing 5-8: Calculating the area of a rectangle
specified by its length and width in separate variables</span>
specified by its width and height in separate variables</span>
Now, run this program using `cargo run`:
@ -39,20 +39,20 @@ The area of the rectangle is 1500 square pixels.
### Refactoring with Tuples
Even though Listing 5-8 works and figures out the area of the rectangle by
calling the `area` function with each dimension, we can do better. The length
and the width are related to each other because together they describe one
calling the `area` function with each dimension, we can do better. The width
and the height are related to each other because together they describe one
rectangle.
The issue with this method is evident in the signature of `area`:
```rust,ignore
fn area(length: u32, width: u32) -> u32 {
fn area(width: u32, height: u32) -> u32 {
```
The `area` function is supposed to calculate the area of one rectangle, but the
function we wrote has two parameters. The parameters are related, but thats
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
page XX: by using tuples. Listing 5-9 shows another version of our program that
uses tuples:
@ -61,7 +61,7 @@ uses tuples:
```rust
fn main() {
let rect1 = (50, 30);
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
@ -74,7 +74,7 @@ fn area(dimensions: (u32, u32)) -> u32 {
}
```
<span class="caption">Listing 5-8: Specifying the length and width of the
<span class="caption">Listing 5-8: Specifying the width and height of the
rectangle with a tuple</span>
In one way, this program is better. Tuples let us add a bit of structure, and
@ -82,9 +82,9 @@ 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
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
to keep in mind that `length` is the tuple index `0` and `width` is the tuple
to keep in mind that `width` is the tuple index `0` and `height` is the tuple
index `1`. If someone else worked on this code, they would have to figure this
out and keep it in mind as well. It would be easy to forget or mix up these
values and cause errors, because we havent conveyed the meaning of our data in
@ -100,12 +100,12 @@ parts, as shown in Listing 5-10:
```rust
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
@ -114,16 +114,16 @@ fn main() {
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.length * rectangle.width
rectangle.width * rectangle.height
}
```
<span class="caption">Listing 5-10: Defining a `Rectangle` struct</span>
Here 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
in `main` we create a particular instance of a `Rectangle` that has a length of
50 and a width of 30.
defined the fields as `width` and `height`, both of which have type `u32`. Then
in `main` we create a particular instance of a `Rectangle` that has a width of
30 and a height of 50.
Our `area` function is now defined with one parameter, which weve named
`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
@ -132,10 +132,10 @@ take ownership of it. This way, `main` retains its ownership and can continue
using `rect1`, which is the reason we use the `&` in the function signature and
where we call the function.
The `area` function accesses the `length` and `width` fields of the `Rectangle`
The `area` function accesses the `width` and `height` fields of the `Rectangle`
instance. Our function signature for `area` now indicates exactly what we mean:
calculate the area of a `Rectangle` using its `length` and `width` fields. This
conveys that the length and width are related to each other, and gives
calculate the area of a `Rectangle` using its `width` and `height` fields. This
conveys that the width and height are related to each other, and gives
descriptive names to the values rather than using the tuple index values of `0`
and `1`—a win for clarity.
@ -150,12 +150,12 @@ chapters:
```rust,ignore
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {}", rect1);
}
@ -176,7 +176,7 @@ direct end user consumption. The primitive types weve seen so far implement
`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
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
want and structs dont have a provided implementation of `Display`.
@ -216,12 +216,12 @@ definition, as shown in Listing 5-12:
```rust
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
@ -234,7 +234,7 @@ Now when we run the program, we wont get any errors and well see the
following output:
```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
@ -245,8 +245,8 @@ When we use the `{:#?}` style in the example, the output will look like this:
```text
rect1 is Rectangle {
length: 50,
width: 30
width: 30,
height: 50
}
```

View File

@ -19,18 +19,18 @@ in Listing 5-13:
```rust
#[derive(Debug)]
struct Rectangle {
length: u32,
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
@ -44,7 +44,7 @@ fn main() {
To define the function within the context of `Rectangle`, we start an `impl`
(*implementation*) block. Then we move the `area` function within the `impl`
curly braces and change the first (and in this case, only) parameter to be
curly brackets and change the first (and in this case, only) parameter to be
`self` in the signature and everywhere within the body. In `main` where we
called the `area` function and passed `rect1` as an argument, we can instead
use *method syntax* to call the `area` method on our `Rectangle` instance.
@ -131,9 +131,9 @@ method:
```rust,ignore
fn main() {
let rect1 = Rectangle { length: 50, width: 30 };
let rect2 = Rectangle { length: 40, width: 10 };
let rect3 = Rectangle { length: 45, width: 60 };
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
@ -161,8 +161,8 @@ parameter will be by looking at the code that calls the method:
read `rect2` (rather than write, which would mean wed need a mutable borrow),
and we want `main` to retain ownership of `rect2` so we can use it again after
calling the `can_hold` method. The return value of `can_hold` will be a
boolean, and the implementation will check whether the length and width of
`self` are both greater than the length and width of the other `Rectangle`,
boolean, and the implementation will check whether the width and height of
`self` are both greater than the width and height of the other `Rectangle`,
respectively. Lets add the new `can_hold` method to the `impl` block from
Listing 5-13, shown in Listing 5-15:
@ -171,17 +171,17 @@ Listing 5-13, shown in Listing 5-15:
```rust
# #[derive(Debug)]
# struct Rectangle {
# length: u32,
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width
self.width > other.width && self.height > other.height
}
}
```
@ -205,7 +205,7 @@ function.
Associated functions are often used for constructors that will return a new
instance of the struct. For example, we could provide an associated function
that would have one dimension parameter and use that as both length and width,
that would have one dimension parameter and use that as both width and height,
thus making it easier to create a square `Rectangle` rather than having to
specify the same value twice:
@ -214,13 +214,13 @@ specify the same value twice:
```rust
# #[derive(Debug)]
# struct Rectangle {
# length: u32,
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { length: size, width: size }
Rectangle { width: size, height: size }
}
}
```
@ -239,19 +239,19 @@ in its own `impl` block:
```rust
# #[derive(Debug)]
# struct Rectangle {
# length: u32,
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn area(&self) -> u32 {
self.length * self.width
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.length > other.length && self.width > other.width
self.width > other.width && self.height > other.height
}
}
```

View File

@ -249,7 +249,7 @@ m.call();
The body of the method would use `self` to get the value that we called the
method on. In this example, 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.
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
entire `match` expression.
Curly braces typically arent used if the match arm code is short, as it is in
Listing 6-3 where each arm just returns a value. If you want to run multiple
lines of code in a match arm, you can use curly braces. For example, the
Curly brackets typically arent used if the match arm code is short, as it is
in Listing 6-3 where each arm just returns a value. If you want to run multiple
lines of code in a match arm, you can use curly brackets. For example, the
following code would print out “Lucky penny!” every time the method was called
with a `Coin::Penny` but would still return the last value of the block, `1`:
@ -99,7 +99,7 @@ As an example, lets change one of our enum variants to hold data inside it.
From 1999 through 2008, the United States minted quarters with different
designs for each of the 50 states on one side. No other coins got state
designs, so only quarters have this extra value. We can add this information to
our `enum` by changing the `Quarter` variant to include a `State` value stored
our `enum` by changing the `Quarter` variant to include a `UsState` value stored
inside it, which weve done here in Listing 6-4:
```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:
* The `mod` keyword declares a new module. Code within the module appears
either immediately following this declaration within curly braces or in
either immediately following this declaration within curly brackets or in
another file.
* By default, functions, types, constants, and modules are private. The `pub`
keyword makes an item public and therefore visible outside its namespace.

View File

@ -63,7 +63,7 @@ mod network {
```
After the `mod` keyword, we put the name of the module, `network`, and then a
block of code in curly braces. Everything inside this block is inside the
block of code in curly brackets. Everything inside this block is inside the
namespace `network`. In this case, we have a single function, `connect`. If we
wanted to call this function from a script outside the `network` module, we
would need to specify the module and use the namespace syntax `::`, like so:
@ -383,7 +383,7 @@ previously, we can do what the note suggests:
1. Make a new *directory* named *network*, the parent modules name.
2. Move the *src/network.rs* file into the new *network* directory, and
rename *src/network/mod.rs*.
rename it to *src/network/mod.rs*.
3. Move the submodule file *src/server.rs* into the *network* directory.
Here are commands to carry out these steps:

View File

@ -83,7 +83,7 @@ directly.
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
importing multiple items from one namespace, you can list them using curly
braces and commas in the last position, like so:
brackets and commas in the last position, like so:
```rust
enum TrafficLight {

View File

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

View File

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

View File

@ -1,61 +1,62 @@
## Strings
Weve already talked about strings a bunch in Chapter 4, but lets take a more
in-depth look at them now. Strings are an area that new Rustaceans commonly get
stuck on. This is due to a combination of three things: Rusts propensity for
making sure to expose possible errors, strings being a more complicated data
structure than many programmers give them credit for, and UTF-8. These things
combine in a way that can seem difficult when coming from other languages.
We talked about strings in Chapter 4, but well look at them in more depth now.
New Rustaceans commonly get stuck on strings due to a combination of three
concepts: Rusts propensity for exposing possible errors, strings being a more
complicated data structure than many programmers give them credit for, and
UTF-8. These concepts combine in a way that can seem difficult when youre
coming from other programming languages.
The reason strings are in the collections chapter is that strings are
This discussion of strings is in the collections chapter because strings are
implemented as a collection of bytes plus some methods to provide useful
functionality when those bytes are interpreted as text. In this section, 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`
is different than the other collections, namely how indexing into a `String` is
complicated by the differences between how people and computers interpret
`String` data.
### What is a String?
### What Is a String?
Before we can dig into those aspects, we need to talk about what exactly we
mean by the term *string*. Rust actually only has one string type in the core
language itself: `str`, the string slice, which is usually seen in its borrowed
form, `&str`. We talked about *string slices* in Chapter 4: these are a
reference to some UTF-8 encoded string data stored elsewhere. String literals,
for example, are stored in the binary output of the program, and are therefore
string slices.
Well first define what we mean by the term *string*. Rust has only one string
type in the core language, which is the string slice `str` that is usually seen
in its borrowed form `&str`. In Chapter 4, we talked about *string slices*,
which are references to some UTF-8 encoded string data stored elsewhere. String
literals, for example, are stored in the binary output of the program and are
therefore string slices.
The type called `String` is provided in Rusts standard library rather than
coded into the core language, and is a growable, mutable, owned, UTF-8 encoded
string type. When Rustaceans talk about “strings” in Rust, they usually mean
both the `String` and the string slice `&str` types, not just one of those.
This section is largely about `String`, but both these types are used heavily
in Rusts standard library. Both `String` and string slices are UTF-8 encoded.
The `String` type is provided in Rusts standard library rather than coded into
the core language and is a growable, mutable, owned, UTF-8 encoded string type.
When Rustaceans refer to “strings” in Rust, they usually mean the `String` and
the string slice `&str` types, not just one of those types. Although this
section is largely about `String`, both types are used heavily in Rusts
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
`OsString`, `OsStr`, `CString`, and `CStr`. Library crates may provide even
`OsString`, `OsStr`, `CString`, and `CStr`. Library crates can provide even
more options for storing string data. Similar to the `*String`/`*Str` naming,
they often provide an owned and borrowed variant, just like `String`/`&str`.
These string types may store different encodings or be represented in memory in
a different way, for example. We wont be talking about these other string
These string types can store text in different encodings or be represented in
memory in a different way, for example. We wont discuss these other string
types in this chapter; see their API documentation for more about how to use
them and when each is appropriate.
### Creating a New String
Many of the same operations available with `Vec` are available with `String` as
well, starting with the `new` function to create a string, like so:
well, starting with the `new` function to create a string, shown in Listing 8-9:
```rust
let mut s = String::new();
```
This creates a new empty string called `s` that we can then load data into.
<span class="caption">Listing 8-9: Creating a new, empty `String`</span>
Often, 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
that implements the `Display` trait, which string literals do:
that implements the `Display` trait, which string literals do. Listing 8-10
shows two examples:
```rust
let data = "initial contents";
@ -66,78 +67,104 @@ let s = data.to_string();
let s = "initial contents".to_string();
```
This creates a string containing `initial contents`.
<span class="caption">Listing 8-10: Using the `to_string` method to create a
`String` from a string literal</span>
This code creates a string containing `initial contents`.
We can also use the function `String::from` to create a `String` from a string
literal. This is equivalent to using `to_string`:
literal. The code in Listing 8-11 is equivalent to the code from Listing 8-10
that uses `to_string`:
```rust
let s = String::from("initial contents");
```
Because strings are used for so many things, there are many different generic
APIs that can be used for strings, so there are a lot of options. Some of them
can feel redundant, but they all have their place! In this case, `String::from`
and `.to_string` end up doing the exact same thing, so which you choose is a
matter of style.
<span class="caption">Listing 8-11: Using the `String::from` function to create
a `String` from a string literal</span>
Because strings are used for so many things, we can use many different generic
APIs for strings, providing us with a lot of options. Some of them can seem
redundant, but they all have their place! In this case, `String::from` and
`to_string` do the same thing, so which you choose is a matter of style.
Remember that strings are UTF-8 encoded, so we can include any properly encoded
data in them:
data in them, as shown in Listing 8-12:
```rust
let hello = "السلام عليكم";
let hello = "Dobrý den";
let hello = "Hello";
let hello = "שָׁלוֹם";
let hello = "नमस्ते";
let hello = "こんにちは";
let hello = "안녕하세요";
let hello = "你好";
let hello = "Olá";
let hello = "Здравствуйте";
let hello = "Hola";
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
```
<span class="caption">Listing 8-12: Storing greetings in different languages in
strings</span>
All of these are valid `String` values.
### Updating a String
A `String` can grow in size and its contents can change just like the contents
of a `Vec`, by pushing more data into it. In addition, `String` has
concatenation operations implemented with the `+` operator for convenience.
A `String` can grow in size and its contents can change, just like the contents
of a `Vec`, by pushing more data into it. In addition, we can conveniently use
the `+` operator or the `format!` macro to concatenate `String` values together.
#### Appending to a String with Push
#### Appending to a String with `push_str` and `push`
We can grow a `String` by using the `push_str` method to append a string slice:
We can grow a `String` by using the `push_str` method to append a string slice,
as shown in Listing 8-13:
```rust
let mut s = String::from("foo");
s.push_str("bar");
```
`s` will contain “foobar” after these two lines. The `push_str` method takes a
<span class="caption">Listing 8-13: Appending a string slice to a `String`
using the `push_str` method</span>
After these two lines, `s` will contain `foobar`. The `push_str` method takes a
string slice because we dont necessarily want to take ownership of the
parameter. For example, it would be unfortunate if we werent able to use `s2`
after appending its contents to `s1`:
parameter. For example, the code in Listing 8-14 shows that it would be
unfortunate if we werent able to use `s2` after appending its contents to `s1`:
```rust
let mut s1 = String::from("foo");
let s2 = String::from("bar");
let s2 = "bar";
s1.push_str(&s2);
println!("s2 is {}", s2);
```
The `push` method is defined to have a single character as a parameter and add
it to the `String`:
<span class="caption">Listing 8-14: Using a string slice after appending its
contents to a `String`</span>
If the `push_str` method took ownership of `s2`, we 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
let mut s = String::from("lo");
s.push('l');
```
After this, `s` will contain “lol”.
<span class="caption">Listing 8-15: Adding one character to a `String` value
using `push`</span>
#### Concatenation with the + Operator or the `format!` Macro
As a result of this code, `s` will contain `lol`.
Often, well want to combine two existing strings together. One way is to use
the `+` operator like this:
#### Concatenation with the `+` Operator or the `format!` Macro
Often, well want to combine two existing strings. One way is to use the `+`
operator, as shown in Listing 8-16:
```rust
let s1 = String::from("Hello, ");
@ -145,8 +172,11 @@ let s2 = String::from("world!");
let s3 = s1 + &s2; // Note that s1 has been moved here and can no longer be used
```
After this code the String `s3` will contain `Hello, world!`. The reason that
`s1` is no longer valid after the addition and the reason that we used a
<span class="caption">Listing 8-16: Using the `+` operator to combine two
`String` values into a new `String` value</span>
As a result of this code, the string `s3` will contain `Hello, world!`. The
reason `s1` is no longer valid after the addition and the reason we used a
reference to `s2` has to do with the signature of the method that gets called
when we use the `+` operator. The `+` operator uses the `add` method, whose
signature looks something like this:
@ -155,32 +185,32 @@ signature looks something like this:
fn add(self, s: &str) -> String {
```
This isnt the exact signature thats in the standard library; there `add` is
defined using generics. Here, were looking at the signature of `add` with
concrete types substituted for the generic ones, which is what happens when we
call this method with `String` values. Well be discussing generics in
Chapter 10. This signature gives us the clues we need to understand the tricky
bits of the `+` operator.
This isnt the exact signature thats in the standard library: in the standard
library, `add` is defined using generics. Here, were looking at the signature
of `add` with concrete types substituted for the generic ones, which is what
happens when we call this method with `String` values. Well discuss generics
in Chapter 10. This signature gives us the clues we need to understand the
tricky bits of the `+` operator.
First of all, `s2` has an `&`, meaning that we are adding a *reference* of the
second string to the first string. This is because of the `s` parameter in the
`add` function: we can only add a `&str` to a `String`, we cant add two
`String` values together. But wait - the type of `&s2` is `&String`, not
`&str`, as specified in the second parameter to `add`. Why does our example
compile? We are able to use `&s2` in the call to `add` because a `&String`
argument can be *coerced* into a `&str` - when the `add` function is called,
Rust uses something called a *deref coercion*, which you could think of here as
turning `&s2` into `&s2[..]` for use in the `add` function. Well discuss deref
coercion in more depth in Chapter 15. Because `add` does not take ownership of
the parameter, `s2` will still be a valid `String` after this operation.
First, `s2` has an `&`, meaning that were adding a *reference* of the second
string to the first string because of the `s` parameter in the `add` function:
we can only add a `&str` to a `String`; we cant add two `String` values
together. But wait - the type of `&s2` is `&String`, not `&str`, as specified
in the second parameter to `add`. Why does Listing 8-16 compile? We are able to
use `&s2` in the call to `add` because the compiler can *coerce* the `&String`
argument into a `&str`. When we call the `add` method, Rust uses something
called a *deref coercion*, which you could think of here as turning `&s2` into
`&s2[..]`. Well discuss deref coercion in more depth in Chapter 15. Because
`add` does not take ownership of the `s` parameter, `s2` will still be a valid
`String` after this operation.
Second, we can see in the signature that `add` takes ownership of `self`,
because `self` does *not* have an `&`. This means `s1` in the above example
will be moved into the `add` call and no longer be valid after that. So while
`let s3 = s1 + &s2;` looks like it will copy both strings and create a new one,
this statement actually takes ownership of `s1`, appends a copy of the contents
of `s2`, then returns ownership of the result. In other words, it looks like
its making a lot of copies, but isnt: the implementation is more efficient
because `self` does *not* have an `&`. This means `s1` in Listing 8-16 will be
moved into the `add` call and no longer be valid after that. So although `let
s3 = s1 + &s2;` looks like it will copy both strings and create a new one, this
statement actually takes ownership of `s1`, appends a copy of the contents of
`s2`, and then returns ownership of the result. In other words, it looks like
its making a lot of copies but isnt: the implementation is more efficient
than copying.
If we need to concatenate multiple strings, the behavior of `+` gets unwieldy:
@ -193,8 +223,8 @@ let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
```
`s` will be “tic-tac-toe” at this point. With all of the `+` and `"`
characters, it gets hard to see whats going on. For more complicated string
At this point, `s` will be `tic-tac-toe`. With all of the `+` and `"`
characters, its difficult to see whats going on. For more complicated string
combining, we can use the `format!` macro:
```rust
@ -205,24 +235,27 @@ let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
```
This code will also set `s` to “tic-tac-toe”. The `format!` macro works in the
same way as `println!`, but instead of printing the output to the screen, it
returns a `String` with the contents. This version is much easier to read, and
also does not take ownership of any of its parameters.
This code also sets `s` to `tic-tac-toe`. The `format!` macro works in the same
way as `println!`, but instead of printing the output to the screen, it returns
a `String` with the contents. The version of the code using `format!` is much
easier to read and also doesnt take ownership of any of its parameters.
### Indexing into Strings
In many other languages, accessing individual characters in a string by
referencing them by index is a valid and common operation. In Rust, however, if
we try to access parts of a `String` using indexing syntax, well get an error.
That is, this code:
In many other programming languages, accessing individual characters in a
string by referencing them by index is a valid and common operation. However,
if we try to access parts of a `String` using indexing syntax in Rust, well
get an error. Consider the code in Listing 8-17:
```rust,ignore
let s1 = String::from("hello");
let h = s1[0];
```
will result in this error:
<span class="caption">Listing 8-17: Attempting to use indexing syntax with a
String</span>
This code will result in the following error:
```text
error: the trait bound `std::string::String: std::ops::Index<_>` is not
@ -233,33 +266,31 @@ satisfied [--explain E0277]
note: the type `std::string::String` cannot be indexed by `_`
```
The error and the note tell the story: Rust strings dont support indexing. So
the follow-up question is, why not? In order to answer that, we have to talk a
bit about how Rust stores strings in memory.
The error and the note tell the story: Rust strings dont support indexing. But
why not? To answer that question, we need to discuss how Rust stores strings in
memory.
#### Internal Representation
A `String` is a wrapper over a `Vec<u8>`. Lets take a look at some of our
properly-encoded UTF-8 example strings from before. First, this one:
A `String` is a wrapper over a `Vec<u8>`. Lets look at some of our properly
encoded UTF-8 example strings from Listing 8-12. First, this one:
```rust
let len = String::from("Hola").len();
```
In this case, `len` will be four, which means the `Vec` storing the string
“Hola” is four bytes long: each of these letters takes one byte when encoded in
UTF-8. What about this example, though?
“Hola” is four bytes long. Each of these letters takes one byte when encoded in
UTF-8. But what about the following line?
```rust
let len = String::from("Здравствуйте").len();
```
A person asked how long the string is might say 12. However, Rusts answer
is 24. This is the number of bytes that it takes to encode “Здравствуйте” in
UTF-8, since each Unicode scalar value takes two bytes of storage. Therefore,
an index into the strings bytes will not always correlate to a valid Unicode
scalar value.
Asked how long the string is, you might say 12. However, Rusts answer is 24:
thats the number of bytes it takes to encode “Здравствуйте” in UTF-8, because
each Unicode scalar value takes two bytes of storage. Therefore, an index into
the strings bytes will not always correlate to a valid Unicode scalar value.
To demonstrate, consider this invalid Rust code:
```rust,ignore
@ -270,19 +301,20 @@ let answer = &hello[0];
What should the value of `answer` be? Should it be `З`, the first letter? When
encoded in UTF-8, the first byte of `З` is `208`, and the second is `151`, so
`answer` should in fact be `208`, but `208` is not a valid character on its
own. Returning `208` is likely not what a person would want if they asked for
the first letter of this string, but thats the only data that Rust has at byte
index 0. Returning the byte value is probably not what people want, even with
only Latin letters: `&"hello"[0]` would return `104`, not `h`. To avoid
returning an unexpected value and causing bugs that might not be discovered
immediately, Rust chooses to not compile this code at all and prevent
misunderstandings earlier.
own. Returning `208` is likely not what a user would want if they asked for the
first letter of this string; however, thats the only data that Rust has at
byte index 0. Returning the byte value is probably not what users want, even if
the string contains only Latin letters: if `&"hello"[0]` was valid code that
returned the byte value, it would return `104`, not `h`. To avoid returning an
unexpected value and causing bugs that might not be discovered immediately,
Rust 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
to look at strings, from Rusts perspective: as bytes, scalar values, and
grapheme clusters (the closest thing to what people would call *letters*).
Another point about UTF-8 is that there are actually three relevant ways to
look at strings from Rusts perspective: as bytes, scalar values, and grapheme
clusters (the closest thing to what we would call *letters*).
If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is
ultimately stored as a `Vec` of `u8` values that looks like this:
@ -292,7 +324,7 @@ ultimately stored as a `Vec` of `u8` values that looks like this:
224, 165, 135]
```
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
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
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
["न", "म", "स्", "ते"]
@ -313,19 +345,21 @@ Rust provides different ways of interpreting the raw string data that computers
store so that each program can choose the interpretation it needs, no matter
what human language the data is in.
A final reason Rust does not allow you to index into a `String` to get a
A final reason Rust doesnt allow us to index into a `String` to get a
character is that indexing operations are expected to always take constant time
(O(1)). It isnt possible to guarantee that performance with a `String`,
though, since Rust would have to walk through the contents from the beginning
to the index to determine how many valid characters there were.
(O(1)). But it isnt possible to guarantee that performance with a `String`,
because Rust would have to walk through the contents from the beginning to the
index to determine how many valid characters there were.
### Slicing Strings
Because its not clear what the return type of string indexing should be, and
it is often a bad idea to index into a string, Rust dissuades you from doing so
by asking you to be more specific if you really need it. The way you can be
more specific than indexing using `[]` with a single number is using `[]` with
a range to create a string slice containing particular bytes:
Indexing into a string is often a bad idea because its not clear what the
return type of the string indexing operation should be: a byte value, a
character, a grapheme cluster, or a string slice. Therefore, Rust asks you to
be more specific if you really need to use indices to create string slices. To
be more specific in your indexing and indicate that you want a string slice,
rather than indexing using `[]` with a single number, you can use `[]` with a
range to create a string slice containing particular bytes:
```rust
let hello = "Здравствуйте";
@ -334,27 +368,28 @@ let s = &hello[0..4];
```
Here, `s` will be a `&str` that contains the first four bytes of the string.
Earlier, we mentioned that each of these characters was two bytes, so that
means that `s` will be “Зд”.
Earlier, we mentioned that each of these characters was two bytes, which means
`s` will be `Зд`.
What would happen if we did `&hello[0..1]`? The answer: it will panic at
runtime, in the same way that accessing an invalid index in a vector does:
What would happen if we used `&hello[0..1]`? The answer: Rust will panic at
runtime in the same way that accessing an invalid index in a vector does:
```text
thread 'main' panicked at 'index 0 and/or 1 in `Здравствуйте` do not lie on
character boundary', ../src/libcore/str/mod.rs:1694
```
You should use this with caution, since it can cause your program to crash.
You should use ranges to create string slices with caution, because it can
crash your program.
### Methods for Iterating Over Strings
Luckily, there are other ways we can access elements in a String.
Fortunately, we can access elements in a string in other ways.
If we need to perform operations on individual Unicode scalar values, the best
way to do so is to use the `chars` method. Calling `chars` on “नमस्ते”
separates out and returns six values of type `char`, and you can iterate over
the result in order to access each element:
way to do so is to use the `chars` method. Calling `chars` on “नमस्ते” separates
out and returns six values of type `char`, and you can iterate over the result
in order to access each element:
```rust
for c in "नमस्ते".chars() {
@ -362,7 +397,7 @@ for c in "नमस्ते".chars() {
}
```
This code will print:
This code will print the following:
```text
@ -392,22 +427,22 @@ This code will print the 18 bytes that make up this `String`, starting with:
// ... etc
```
But make sure to remember that valid Unicode scalar values may be made up of
more than one byte.
But be sure to remember that valid Unicode scalar values may be made up of more
than one byte.
Getting grapheme clusters from strings is complex, so this functionality is not
provided by the standard library. There are crates available on crates.io if
this is the functionality you need.
provided by the standard library. Crates are available on
[crates.io](https://crates.io) if this is the functionality you need.
### Strings are Not so Simple
### Strings Are Not So Simple
To summarize, strings are complicated. Different programming languages make
different choices about how to present this complexity to the programmer. Rust
has chosen to make the correct handling of `String` data the default behavior
for all Rust programs, which does mean programmers have to put more thought
into handling UTF-8 data upfront. This tradeoff exposes more of the complexity
of strings than other programming languages do, but this will prevent you from
having to handle errors involving non-ASCII characters later in your
development lifecycle.
for all Rust programs, which means programmers have to put more thought into
handling UTF-8 data upfront. This trade-off exposes more of the complexity of
strings than other programming languages do but prevents you from having to
handle errors involving non-ASCII characters later in your development life
cycle.
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
*hashing function*, which determines how it places these keys and values into
memory. Many different programming languages support this kind of data
structure, but often with a different name: hash, map, object, hash table, or
associative array, just to name a few.
structure, but often use a different name, such as hash, map, object, hash
table, or associative array, just to name a few.
Hash maps are useful for when you want to be able to look up data not by an
index, as you can with vectors, but by using a key that can be of any type. For
example, in a game, you could keep track of each teams score in a hash map
where each key is a teams name and the values are each teams score. Given a
team name, you can retrieve their score.
Hash maps are useful for when you want to look up data not by an index, as you
can with vectors, but by using a key that can be of any type. For example, in a
game, you could keep track of each teams score in a hash map where each key is
a teams name and the values are each teams score. Given a team name, you can
retrieve its score.
Well go over the basic API of hash maps in this chapter, but there are many
more goodies hiding in the functions defined on `HashMap` by the standard
library. As always, check the standard library documentation for more
information.
Well go over the basic API of hash maps in this section, but many more goodies
are hiding in the functions defined on `HashMap<K, V>` by the standard library.
As always, check the standard library documentation for more information.
### Creating a New Hash Map
We can create an empty `HashMap` with `new`, and add elements with `insert`.
Here were keeping track of the scores of two teams whose names are Blue and
Yellow. The Blue team will start with 10 points and the Yellow team starts with
50:
We can create an empty hash map with `new` and add elements with `insert`. In
Listing 8-18, were keeping track of the scores of two teams whose names are
Blue and Yellow. The Blue team will start with 10 points, and the Yellow team
starts with 50:
```rust
use std::collections::HashMap;
@ -34,6 +33,9 @@ scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
```
<span class="caption">Listing 8-18: Creating a new hash map and inserting some
keys and values</span>
Note that we need to first `use` the `HashMap` from the collections portion of
the standard library. Of our three common collections, this one is the least
often used, so 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
vector of tuples, where each tuple consists of a key and its value. The
`collect` method gathers up data into a number of collection types, including
`collect` method gathers data into a number of collection types, including
`HashMap`. For example, if we had the team names and initial scores in two
separate vectors, we can use the `zip` method to create a vector of tuples
where “Blue” is paired with 10, and so forth. Then we can use the `collect`
method to turn that vector of tuples into a `HashMap`:
method to turn that vector of tuples into a `HashMap` as shown in Listing 8-19:
```rust
use std::collections::HashMap;
@ -62,17 +64,20 @@ let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
```
<span class="caption">Listing 8-19: Creating a hash map from a list of teams
and a list of scores</span>
The type annotation `HashMap<_, _>` is needed here because its possible to
`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,
however, we use underscores and Rust can infer the types that the hash map
contains based on the types of the data in the vector.
however, we use underscores, and Rust can infer the types that the hash map
contains based on the types of the data in the vectors.
### Hash Maps and Ownership
For types that implement the `Copy` trait, like `i32`, the values are copied
into the hash map. For owned values like `String`, the values will be moved and
the hash map will be the owner of those values:
the hash map will be the owner of those values as demonstrated in Listing 8-20:
```rust
use std::collections::HashMap;
@ -82,20 +87,25 @@ let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
// field_name and field_value are invalid at this point
// field_name and field_value are invalid at this point, try using them and
// see what compiler error you get!
```
We would not be able to use the bindings `field_name` and `field_value` after
they have been moved into the hash map with the call to `insert`.
<span class="caption">Listing 8-20: Showing that keys and values are owned by
the hash map once theyre inserted</span>
If we insert references to values into the hash map, the values themselves will
not be moved into the hash map. The values that the references point to must be
valid for at least as long as the hash map is valid, though. We will talk more
about these issues in the Lifetimes section of Chapter 10.
We arent able to use the variables `field_name` and `field_value` after
theyve been moved into the hash map with the call to `insert`.
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
We can get a value out of the hash map by providing its key to the `get` method:
We can get a value out of the hash map by providing its key to the `get` method
as shown in Listing 8-21:
```rust
use std::collections::HashMap;
@ -109,11 +119,14 @@ let team_name = String::from("Blue");
let score = scores.get(&team_name);
```
<span class="caption">Listing 8-21: Accessing the score for the Blue team
stored in the hash map</span>
Here, `score` will have the value thats associated with the Blue team, and the
result will be `Some(&10)`. The result is wrapped in `Some` because `get`
returns an `Option<&V>`; if theres no value for that key in the hash map, `get`
will return `None`. The program will need to handle the `Option` in one of the
ways that we covered in Chapter 6.
returns an `Option<&V>`; if theres no value for that key in the hash map,
`get` will return `None`. The program will need to handle the `Option` in one
of the ways that we covered in Chapter 6.
We can iterate over each key/value pair in a hash map in a similar manner as we
do with vectors, using a `for` loop:
@ -131,7 +144,7 @@ for (key, value) in &scores {
}
```
This will print each pair, in an arbitrary order:
This code will print each pair in an arbitrary order:
```text
Yellow: 50
@ -140,22 +153,22 @@ Blue: 10
### Updating a Hash Map
While the number of keys and values is growable, each individual key can only
have one value associated with it at a time. When we want to change the data in
a hash map, we have to decide how to handle the case when a key already has a
value assigned. We could choose to replace the old value with the new value,
completely disregarding the old value. We could choose to keep the old value
and ignore the new value, and only add the new value if the key *doesnt*
already have a value. Or we could combine the old value and the new value.
Lets look at how to do each of these!
Although the number of keys and values is growable, each key can only have one
value associated with it at a time. When we want to change the data in a hash
map, we have to decide how to handle the case when a key already has a value
assigned. We could replace the old value with the new value, completely
disregarding the old value. We could keep the old value and ignore the new
value, and only add the new value if the key *doesnt* already have a value. Or
we could combine the old value and the new value. Lets look at how to do each
of these!
#### Overwriting a Value
If we insert a key and a value into a hash map, then insert that same key with
a different value, the value associated with that key will be replaced. Even
though this following code calls `insert` twice, the hash map will only contain
one key/value pair because were inserting the value for the Blue teams key
both times:
If we insert a key and a value into a hash map, and then insert that same key
with a different value, the value associated with that key will be replaced.
Even though the code in Listing 8-22 calls `insert` twice, the hash map will
only contain one key/value pair because were inserting the value for the Blue
teams key both times:
```rust
use std::collections::HashMap;
@ -168,18 +181,22 @@ scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);
```
This will print `{"Blue": 25}`. The original value of 10 has been overwritten.
<span class="caption">Listing 8-22: Replacing a value stored with a particular
key</span>
This code will print `{"Blue": 25}`. The original value of `10` has been
overwritten.
#### Only Insert If the Key Has No Value
Its common to want to check if a particular key has a value and, if it does
not, insert a value for it. Hash maps have a special API for this, called
`entry`, that takes the key we want to check as an argument. The return value
of the `entry` function is an enum, `Entry`, that represents a value that might
or might not exist. Lets say that we want to check if the key for the Yellow
Its common to check whether a particular key has a value, and if it doesnt,
insert a value for it. Hash maps have a special API for this called `entry`
that takes the key we want to check as a parameter. The return value of the
`entry` function is an enum called `Entry` that represents a value that might
or might not exist. 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
50, and the same for the Blue team. With the entry API, the code for this looks
like:
50, and the same for the Blue team. Using the `entry` API, the code looks like
Listing 8-23:
```rust
use std::collections::HashMap;
@ -193,23 +210,29 @@ scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);
```
The `or_insert` method on `Entry` returns the value for the corresponding
`Entry` key if it exists, and if not, inserts its argument as the new value for
this key and returns the modified `Entry`. This is much cleaner than writing
the logic ourselves, and in addition, plays more nicely with the borrow checker.
<span class="caption">Listing 8-23: Using the `entry` method to only insert if
the key does not already have a value</span>
This code will print `{"Yellow": 50, "Blue": 10}`. The first call to `entry`
will insert the key for the Yellow team with the value 50, since the Yellow
team doesnt have a value already. The second call to `entry` will not change
the hash map since the Blue team already has the value 10.
The `or_insert` method on `Entry` is defined to return the value for the
corresponding `Entry` key if that key exists, and if not, inserts the parameter
as the new value for this key and returns the modified `Entry`. This technique
is much cleaner than writing the logic ourselves, and in addition, plays more
nicely with the borrow checker.
#### Update a Value Based on the Old Value
Running the code in Listing 8-23 will print `{"Yellow": 50, "Blue": 10}`. The
first call to `entry` will insert the key for the Yellow team with the value
`50` because the Yellow team 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
it, based on the old value. For instance, if we wanted to count how many times
each word appeared in some text, we could use a hash map with the words as keys
and increment the value to keep track of how many times weve seen that word.
If this is the first time weve seen a word, well first insert the value `0`.
#### Updating a Value Based on the Old Value
Another common use case for hash maps is to look up a keys value and then
update it based on the old value. For instance, Listing 8-24 shows code that
counts how many times each word appears in some text. We use a hash map with
the words as keys and increment the value to keep track of how many times weve
seen that word. If its the first time weve seen a word, well first insert
the value `0`:
```rust
use std::collections::HashMap;
@ -226,47 +249,51 @@ for word in text.split_whitespace() {
println!("{:?}", map);
```
This will print `{"world": 2, "hello": 1, "wonderful": 1}`. The `or_insert`
method actually returns a mutable reference (`&mut V`) to the value for this
key. Here we store that mutable reference in the `count` variable, so in order
to assign to that value we must first dereference `count` using the asterisk
(`*`). The mutable reference goes out of scope at the end of the `for` loop, so
all of these changes are safe and allowed by the borrowing rules.
<span class="caption">Listing 8-24: Counting occurrences of words using a hash
map that stores words and counts</span>
This code will print `{"world": 2, "hello": 1, "wonderful": 1}`. The
`or_insert` method actually returns a mutable reference (`&mut V`) to the value
for this key. Here we store that mutable reference in the `count` variable, so
in order to assign to that value we must first dereference `count` using the
asterisk (`*`). The mutable reference goes out of scope at the end of the `for`
loop, so all of these changes are safe and allowed by the borrowing rules.
### Hashing Function
By default, `HashMap` uses a cryptographically secure hashing function that can
provide resistance to Denial of Service (DoS) attacks. This is not the fastest
hashing algorithm out there, but the tradeoff for better security that comes
hashing algorithm available, but the trade-off for better security that comes
with the drop in performance is worth it. If you profile your code and find
that the default hash function is too slow for your purposes, you can switch to
another function by specifying a different *hasher*. A hasher is a type that
implements the `BuildHasher` trait. 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
hasher from scratch; crates.io has libraries that others have shared that
provide hashers implementing many common hashing algorithms.
hasher from scratch; [crates.io](https://crates.io) has libraries shared by
other Rust users that provide hashers implementing many common hashing
algorithms.
## Summary
Vectors, strings, and hash maps will take you far in programs where you need to
store, access, and modify data. Here are some exercises you should now be
equipped to solve:
Vectors, strings, and hash maps will provide a large amount of functionality
that you need in programs where you need to store, access, and modify data.
Here are some exercises you should now be equipped to solve:
* Given a list of integers, use a vector and return the mean (average), median
(when sorted, the value in the middle position), and mode (the value that
occurs most often; a hash map will be helpful here) of the list.
* Convert strings to Pig Latin, where the first consonant of each word is moved
to the end of the word with an added “ay”, so “first” becomes “irst-fay.
Words that start with a vowel get “hay” added to the end instead (“apple”
becomes “apple-hay”). Remember about UTF-8 encoding!
* Convert strings to pig latin. The first consonant of each word is moved to
the end of the word and “ay” is added, so “first” becomes “irst-fay.” Words
that start with a vowel have “hay” added to the end instead (“apple” becomes
“apple-hay”). Keep in mind the details about UTF-8 encoding!
* Using a hash map and vectors, create a text interface to allow a user to add
employee names to a department in the company. For example, “Add Sally to
Engineering” or “Add Amir to Sales. Then let the user retrieve a list of all
employee names to a department in a company. For example, “Add Sally to
Engineering” or “Add Amir to Sales. Then let the user retrieve a list of all
people in a department or all people in the company by department, sorted
alphabetically.
The standard library API documentation describes methods these types have that
will be helpful for these exercises!
The standard library API documentation describes methods that vectors, strings,
and hash maps have that will be helpful for these exercises!
Were getting into more complex programs where operations can fail, which means
its a perfect time to go over error handling next!
Were getting into more complex programs in which operations can fail; so, its
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
of life in software, so Rust has a number of features for handling situations
in which something goes wrong. In many cases, Rust will require you to
acknowledge the possibility of an error occurring and take some action before
your code will compile. This makes your program more robust by ensuring that
you wont only discover errors after youve deployed your code to production.
in which something goes wrong. In many cases, Rust requires you to acknowledge
the possibility of an error occurring and take some action before your code
will compile. This requirement makes your program more robust by ensuring that
youll discover errors and handle them appropriately before youve deployed
your code to production!
Rust groups errors into two major categories: *recoverable* and *unrecoverable*
errors. Recoverable errors are situations when its usually reasonable to
report the problem to the user and retry the operation, like a file not being
found. Unrecoverable errors are always symptoms of bugs, like trying to access
a location beyond the end of an array.
errors. Recoverable errors are situations in which its reasonable to report
the problem to the user and retry the operation, like a file not found error.
Unrecoverable errors are always symptoms of bugs, like trying to access a
location beyond the end of an array.
Most languages 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
exceptions. Instead, it has the value `Result<T, E>` for recoverable errors and
the `panic!` macro that stops execution when it encounters unrecoverable
errors. This chapter will cover calling `panic!` first, then talk about
returning `Result<T, E>` values. Finally, well discuss considerations to take
into account when deciding whether to try to recover from an error or to stop
execution.
errors. This chapter covers calling `panic!` first and then talks about
returning `Result<T, E>` values. Additionally, well explore considerations to
take into account when deciding whether to try to recover from an error or to
stop execution.

View File

@ -1,30 +1,31 @@
## Unrecoverable Errors with `panic!`
Sometimes, bad things happen, and theres nothing that you can do about it. For
these cases, Rust has the `panic!` macro. When this macro executes, your
program will print a failure message, unwind and clean up the stack, and then
quit. The most common situation this occurs in is when a bug of some kind has
been detected and its not clear to the programmer how to handle the error.
Sometimes, bad things happen in your code, and theres nothing you can do about
it. In these cases, Rust has the `panic!` macro. When the `panic!` macro
executes, your program will print a failure message, unwind and clean up the
stack, and then quit. The most common situation this occurs in is when a bug of
some kind has been detected, and 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
> *unwinding*, which means Rust walks back up the stack and cleans up the data
> from each function it encounters, but this walking and cleanup is a lot of
> work. The alternative is to immediately *abort*, which ends the program
> without cleaning up. Memory that the program was using will then need to be
> cleaned up by the operating system. If in your project you need to make the
> resulting binary as small as possible, you can switch from unwinding to
> aborting on panic by adding `panic = 'abort'` to the appropriate `[profile]`
> sections in your *Cargo.toml*. For example, if you want to abort on panic in
> release mode:
> By default, when a `panic!` occurs, the program starts *unwinding*, which
> means Rust walks back up the stack and cleans up the data from each function
> it encounters. But this walking back and cleanup is a lot of work. The
> alternative is to immediately *abort*, which ends the program without
> cleaning up. Memory that the program was using will then need to be cleaned
> up by the operating system. If in your project you need to make the resulting
> binary as small as possible, you can switch from unwinding to aborting on
> panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in
> your *Cargo.toml* file. For example, if you want to abort on panic in release
> mode, add this:
>
> ```toml
> [profile.release]
> panic = 'abort'
> ```
Lets try calling `panic!` with a simple program:
Lets try calling `panic!` in a simple program:
<span class="filename">Filename: src/main.rs</span>
@ -34,7 +35,7 @@ fn main() {
}
```
If you run it, youll see something like this:
When you run the program, youll see something like this:
```text
$ cargo run
@ -46,23 +47,26 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
```
The last three lines contain the error message caused by the call to `panic!`.
The first line shows our panic message and the place in our source code where
the panic occurred: *src/main.rs:2* indicates that its the second line of our
*src/main.rs* file.
The call to `panic!` causes the error message contained in the last three
lines. The first line shows our panic message and the place in our source code
where the panic occurred: *src/main.rs:2* indicates that its the second line
of our *src/main.rs* file.
In this case, the line indicated is part of our code, and if we go to that line
we see the `panic!` macro call. In other cases, the `panic!` call might be in
code that our code calls. The filename and line number reported by the error
message will be someone elses code where the `panic!` macro is called, not the
line of our code that eventually led to the `panic!`. We can use the backtrace
of the functions the `panic!` call came from to figure this out.
In this case, the line indicated is part of our code, and if we go to that
line, we see the `panic!` macro call. In other cases, the `panic!` call might
be in code that our code calls. The filename and line number reported by the
error message will be someone elses code where the `panic!` macro is called,
not the line of our code that eventually led to the `panic!` call. We can use
the backtrace of the functions the `panic!` call came from to figure out the
part of our code that is causing the problem. Well discuss what a backtrace is
in more detail next.
### Using a `panic!` Backtrace
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
the macro directly:
the macro directly. Listing 9-1 has some code that attempts to access an
element by index in a vector:
<span class="filename">Filename: src/main.rs</span>
@ -74,22 +78,25 @@ fn main() {
}
```
Were attempting to access the hundredth element of our vector, but it only has
three elements. In this situation, Rust will panic. Using `[]` is supposed to
return an element, but if you pass an invalid index, theres no element that
Rust could return here that would be correct.
<span class="caption">Listing 9-1: Attempting to access an element beyond the
end of a vector, which will cause a `panic!`</span>
Other languages like C will attempt to give you exactly what you asked for in
Here, 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
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
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
that is stored after the array.
In order to protect your program from this sort of vulnerability, if you try to
read an element at an index that doesnt exist, Rust will stop execution and
refuse to continue. Lets try it and see:
To protect your program from this sort of vulnerability, if you try to read an
element at an index that doesnt exist, Rust will stop execution and refuse to
continue. Lets try it and see:
```text
$ cargo run
@ -102,14 +109,21 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
```
This points at a file we didnt write, *libcollections/vec.rs*. Thats the
implementation of `Vec<T>` in the standard library. The code that gets run when
we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is where
the `panic!` is actually happening.
This error points at a file we didnt write, *libcollections/vec.rs*. Thats
the implementation of `Vec<T>` in the standard library. The code that gets run
when we use `[]` on our vector `v` is in *libcollections/vec.rs*, and that is
where the `panic!` is actually happening.
The next note line tells us that we can set the `RUST_BACKTRACE` environment
variable to get a backtrace of exactly what happened to cause the error. Lets
try that. Listing 9-1 shows the output:
variable to get a backtrace of exactly what happened to cause the error. A
*backtrace* is a list of all the functions that have been called to get to this
point. Backtraces in Rust work like they do in other languages: the key to
reading the backtrace is to start from the top and read until you see files you
wrote. 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
$ RUST_BACKTRACE=1 cargo run
@ -151,29 +165,26 @@ stack backtrace:
17: 0x0 - <unknown>
```
<span class="caption">Listing 9-1: The backtrace generated by a call to
<span class="caption">Listing 9-2: The backtrace generated by a call to
`panic!` displayed when the environment variable `RUST_BACKTRACE` is set</span>
Thats a lot of output! Line 11 of the backtrace points to the line in our
project causing the problem: *src/main.rs*, line four. A backtrace is a list of
all the functions that have been called to get to this point. Backtraces in
Rust work like they do in other languages: the key to reading the backtrace is
to start from the top and read until you see files you wrote. 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.
Thats a lot of output! The exact output you see might be different depending
on your operating system and Rust version. In order to get backtraces with this
information, debug symbols must be enabled. Debug symbols are enabled by
default when using cargo build or cargo run without the --release flag, as we
have here.
If we dont want our program to panic, the location pointed to by the first
line mentioning a file we wrote is where we should start investigating in order
to figure out how we got to this location with values that caused the panic. In
our example where we deliberately wrote code that would panic in order to
demonstrate how to use backtraces, the way to fix the panic is to not try to
request an element at index 100 from a vector that only contains three items.
When your code panics in the future, youll need to figure out for your
particular case what action the code is taking with what values that causes the
panic and what the code should do instead.
In the output in Listing 9-2, line 11 of the backtrace points to the line in
our project thats causing the problem: *src/main.rs* in line 4. If we dont
want our program to panic, the location pointed to by the first line mentioning
a file we wrote is where we should start investigating to figure out how we got
to this location with values that caused the panic. In Listing 9-1 where we
deliberately wrote code that would panic in order to demonstrate how to use
backtraces, the way to fix the panic is to not request an element at index 100
from a vector that only contains three items. When your code panics in the
future, 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
later in the chapter. Next, well now look at how to recover from an error with
`Result`.
Well come back to `panic!` and when we should and should not use `panic!` to
handle error conditions later in the chapter. Next, well look at how to
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
file instead of terminating the process.
Recall from Chapter 2 the section on “[Handling Potential Failure with the
`Result` Type][handle_failure]<!-- ignore -->” that the `Result` enum is defined
Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result`
Type][handle_failure]<!-- ignore --> section that the `Result` enum is defined
as having two variants, `Ok` and `Err`, as follows:
[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type
@ -19,7 +19,7 @@ enum Result<T, E> {
}
```
The `T` and `E` are generic type parameters; 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
the type of the value that will be returned in a success case within the `Ok`
variant, and `E` represents the type of the error that will be returned in a
@ -29,7 +29,7 @@ library has defined on it in many different situations where the successful
value and error value we want to return may differ.
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>
@ -41,21 +41,21 @@ fn main() {
}
```
<span class="caption">Listing 9-2: Opening a file</span>
<span class="caption">Listing 9-3: Opening a file</span>
How do we know `File::open` returns a `Result`? We could look at the standard
library API documentation, or we could ask the compiler! If we give `f` a type
annotation of some type that we know the return type of the function is *not*,
annotation of a type that we know the return type of the function is *not* and
then we try to compile the code, the compiler will tell us that the types 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
change the `let f` statement to:
change the `let f` statement to this:
```rust,ignore
let f: u32 = File::open("hello.txt");
```
Attempting to compile now gives us:
Attempting to compile now gives us the following output:
```text
error[E0308]: mismatched types
@ -76,9 +76,9 @@ error value is `std::io::Error`.
This return type means the call to `File::open` might succeed and return to us
a file handle that we can read from or write to. The function call also might
fail: for example, the file might not exist, or we might not have permission to
fail: for example, the file might not exist or we might not have permission to
access the file. The `File::open` function needs to have a way to tell us
whether it succeeded or failed, and at the same time give us either the file
whether it succeeded or failed and at the same time give us either the file
handle or error information. This information is exactly what the `Result` enum
conveys.
@ -87,9 +87,9 @@ In the case where `File::open` succeeds, the value we will have in the variable
it fails, the value in `f` will be an instance of `Err` that contains more
information about the kind of error that happened.
We need to add to the code from Listing 9-2 to take different actions depending
on the value `File::open` returned. Listing 9-3 shows one way to handle the
`Result` with a basic tool: the `match` expression that we learned about in
We need to add to the code in Listing 9-3 to take different actions depending
on the value `File::open` returned. Listing 9-4 shows one way to handle the
`Result` using a basic tool: the `match` expression that we discussed in
Chapter 6.
<span class="filename">Filename: src/main.rs</span>
@ -109,7 +109,7 @@ fn main() {
}
```
<span class="caption">Listing 9-3: Using a `match` expression to handle the
<span class="caption">Listing 9-4: Using a `match` expression to handle the
`Result` variants we might have</span>
Note that, like the `Option` enum, the `Result` enum and its variants have been
@ -131,19 +131,23 @@ thread 'main' panicked at 'There was a problem opening the file: Error { repr:
Os { code: 2, message: "No such file or directory" } }', src/main.rs:8
```
As usual, this output tells us exactly what has gone wrong.
### Matching on Different Errors
The code in Listing 9-3 will `panic!` no matter the reason that `File::open`
failed. What wed really like to do instead is take different actions for
different failure reasons: if `File::open` failed because the file doesnt
exist, we want to create the file and return the handle to the new file. If
`File::open` failed for any other reason, for example because we didnt have
permission to open the file, we still want to `panic!` in the same way as we
did in Listing 9-3. Lets look at Listing 9-4, which adds another arm to the
`match`:
The code in Listing 9-4 will `panic!` no matter the reason that `File::open`
failed. What we want to do instead is take different actions for different
failure reasons: if `File::open` failed because the file doesnt exist, we want
to create the file and return the handle to the new file. If `File::open`
failed for any other reason, for example because we didnt have permission to
open the file, we still want the code to `panic!` in the same way as it did in
Listing 9-4. Look at Listing 9-5, which adds another arm to the `match`:
<span class="filename">Filename: src/main.rs</span>
<!-- ignore this test because otherwise it creates hello.txt which causes other
tests to fail lol -->
```rust,ignore
use std::fs::File;
use std::io::ErrorKind;
@ -174,7 +178,7 @@ fn main() {
}
```
<span class="caption">Listing 9-4: Handling different kinds of errors in
<span class="caption">Listing 9-5: Handling different kinds of errors in
different ways</span>
The type of the value that `File::open` returns inside the `Err` variant is
@ -182,36 +186,38 @@ The type of the value that `File::open` returns inside the `Err` variant is
has a method `kind` that we can call to get an `io::ErrorKind` value.
`io::ErrorKind` is an enum provided by the standard library that has variants
representing the different kinds of errors that might result from an `io`
operation. The variant were interested in is `ErrorKind::NotFound`, which
indicates the file were trying to open doesnt exist yet.
operation. The variant we want to use is `ErrorKind::NotFound`, which indicates
the file were trying to open doesnt exist yet.
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
pattern. This condition must be true in order for that arms code to get run;
otherwise, the pattern matching will move on to consider the next arm in the
`match`. The `ref` in the pattern is needed so that `error` is not moved into
the guard condition but is merely referenced by it. The reason `ref` is used to
take a reference in a pattern instead of `&` will be covered in detail in
Chapter 18. In short, in the context of a pattern, `&` matches a reference and
gives us its value, but `ref` matches a value and gives us a reference to it.
pattern. This condition must be true for that arms code to be run; otherwise,
the pattern matching will move on to consider the next arm in the `match`. The
`ref` in the pattern is needed so `error` is not moved into the guard condition
but is merely referenced by it. The reason `ref` is used to take a reference in
a pattern instead of `&` will be covered in detail in Chapter 18. In short, in
the context of a pattern, `&` matches a reference and gives us its value, but
`ref` matches a value and gives us a reference to it.
The condition we want to check in the match guard is whether the value returned
by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is,
we try to create the file with `File::create`. However, since `File::create`
could also fail, we need to add an inner `match` statement as well! When the
we try to create the file with `File::create`. However, because `File::create`
could also fail, we need to add an inner `match` statement as well. When the
file 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
besides the missing file error.
of the outer `match` stays the same so the program panics on any error besides
the missing file error.
### Shortcuts for Panic on Error: `unwrap` and `expect`
Using `match` works well enough, but it can be a bit verbose and doesnt always
communicate intent well. The `Result<T, E>` type has many helper methods
defined on it to do various things. One of those methods, called `unwrap`, is a
defined on it to do various tasks. One of those methods, called `unwrap`, is a
shortcut method that is implemented just like the `match` statement we wrote in
Listing 9-3. If the `Result` value is the `Ok` variant, `unwrap` will return
Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return
the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will
call the `panic!` macro for us.
call the `panic!` macro for us. Here is an example of `unwrap` in action:
<span class="filename">Filename: src/main.rs</span>
```rust,should_panic
use std::fs::File;
@ -230,10 +236,12 @@ repr: Os { code: 2, message: "No such file or directory" } }',
/stable-dist-rustc/build/src/libcore/result.rs:868
```
Theres another method similar to `unwrap` that lets us also choose the
`panic!` error message: `expect`. Using `expect` instead of `unwrap` and
providing good error messages can convey your intent and make tracking down the
source of a panic easier. The syntax of `expect` looks like this:
Another method, `expect`, which is similar to `unwrap`, lets us also choose the
`panic!` error message. Using `expect` instead of `unwrap` and providing good
error messages can convey your intent and make tracking down the source of a
panic easier. The syntax of `expect` looks like this:
<span class="filename">Filename: src/main.rs</span>
```rust,should_panic
use std::fs::File;
@ -244,8 +252,8 @@ fn main() {
```
We use `expect` in the same way as `unwrap`: to return the file handle or call
the `panic!` macro. The error message that `expect` uses in its call to
`panic!` will be the parameter that we pass to `expect` instead of the default
the `panic!` macro. The error message used by `expect` in its call to `panic!`
will be the parameter that we pass to `expect`, rather than the default
`panic!` message that `unwrap` uses. Heres what it looks like:
```text
@ -254,19 +262,27 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
/stable-dist-rustc/build/src/libcore/result.rs:868
```
Because this error message starts with the text we specified, `Failed to open
hello.txt`, it will be easier to find where in the code this error message is
coming from. If we use `unwrap` in multiple places, it can take more time to
figure out exactly which `unwrap` is causing the panic because all `unwrap`
calls that panic print the same message.
### Propagating Errors
When writing a function whose implementation calls something that might fail,
instead of handling the error within this function, you can choose to let your
caller know about the error so they can decide what to do. This is known as
*propagating* the error, and gives more control to the calling code where there
When youre writing a function whose implementation calls something that might
fail, instead of handling the error within this function, you can return the
error to the calling code so that it can decide what to do. This is known as
*propagating* the error and gives more control to the calling code where there
might be more information or logic that dictates how the error should be
handled than what you have available in the context of your code.
For example, Listing 9-5 shows a function that reads a username from a file. If
For example, Listing 9-6 shows a function that reads a username from a file. If
the file doesnt exist or cant be read, this function will return those errors
to the code that called this function:
<span class="filename">Filename: src/main.rs</span>
```rust
use std::io;
use std::io::Read;
@ -289,59 +305,61 @@ fn read_username_from_file() -> Result<String, io::Error> {
}
```
<span class="caption">Listing 9-5: A function that returns errors to the
<span class="caption">Listing 9-6: A function that returns errors to the
calling code using `match`</span>
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
concrete type `String`, and the generic type `E` has been filled in with the
concrete type `io::Error`. If this function succeeds without any problems, the
caller of this function will receive an `Ok` value that holds a `String` — the
username that this function read from the file. If this function encounters any
problems, the caller of this function will receive an `Err` value that holds an
instance of `io::Error` that contains more information about what the problems
were. We chose `io::Error` as the return type of this function because that
happens to be the type of the error value returned from both of the operations
were calling in this functions body that might fail: the `File::open`
function and the `read_to_string` method.
code that calls this function will receive an `Ok` value that holds a
`String`—the username that this function read from the file. If this function
encounters any problems, the code that calls this function will receive an
`Err` value that holds an instance of `io::Error` that contains more
information about what the problems were. We chose `io::Error` as the return
type of this function because that happens to be the type of the error value
returned from both of the operations were calling in this functions body that
might fail: the `File::open` function and the `read_to_string` method.
The body of the function starts by calling the `File::open` function. Then we
handle the `Result` value returned with a `match` similar to the `match` in
Listing 9-3, only instead of calling `panic!` in the `Err` case, we return
Listing 9-4, only instead of calling `panic!` in the `Err` case, we return
early from this function and pass the error value from `File::open` back to the
caller as this functions error value. If `File::open` succeeds, we store the
file handle in the variable `f` and continue.
calling code as this functions error value. If `File::open` succeeds, we store
the file handle in the variable `f` and continue.
Then we create a new `String` in variable `s` and call the `read_to_string`
method on the file handle in `f` in order to read the contents of the file into
`s`. The `read_to_string` method also returns a `Result` because it might fail,
even though `File::open` succeeded. So we need another `match` to handle that
method on the file handle in `f` to read the contents of the file into `s`. The
`read_to_string` method also returns a `Result` because it might fail, even
though `File::open` succeeded. So we need another `match` to handle that
`Result`: if `read_to_string` succeeds, then our function has succeeded, and we
return the username from the file thats now in `s` wrapped in an `Ok`. If
`read_to_string` fails, we return the error value in the same way that we
returned the error value in the `match` that handled the return value of
`File::open`. We dont need to explicitly say `return`, however, since this is
the last expression in the function.
`File::open`. However, we dont need to explicitly say `return`, because this
is the last expression in the function.
The code that calls this code will then handle getting either an `Ok` value
that contains a username or an `Err` value that contains an `io::Error`. We
dont know what the caller will do with those values. If they get an `Err`
value, they could choose to call `panic!` and crash their program, use a
dont know what the calling code will do with those values. If the calling code
gets an `Err` value, it could call `panic!` and crash the program, use a
default username, or look up the username from somewhere other than a file, for
example. We dont have enough information on what the caller is actually trying
to do, so we propagate all the success or error information upwards for them to
handle as they see fit.
example. We dont have enough information on what the calling code is actually
trying to do, so we propagate all the success or error information upwards for
it to handle appropriately.
This pattern of propagating errors is so common in Rust that there is dedicated
syntax to make this easier: `?`.
This pattern of propagating errors is so common in Rust that Rust provides the
question mark operator `?` to make this easier.
### A Shortcut for Propagating Errors: `?`
#### A Shortcut for Propagating Errors: `?`
Listing 9-6 shows an implementation of `read_username_from_file` that has the
same functionality as it had in Listing 9-5, but this implementation uses the
Listing 9-7 shows an implementation of `read_username_from_file` that has the
same functionality as it had in Listing 9-6, but this implementation uses the
question mark operator:
<span class="filename">Filename: src/main.rs</span>
```rust
use std::io;
use std::io::Read;
@ -355,26 +373,42 @@ fn read_username_from_file() -> Result<String, io::Error> {
}
```
<span class="caption">Listing 9-6: A function that returns errors to the
<span class="caption">Listing 9-7: A function that returns errors to the
calling code using `?`</span>
The `?` placed after a `Result` value is defined to work the exact same way as
the `match` expressions we defined to handle the `Result` values in Listing
9-5. If the value of the `Result` is an `Ok`, the value inside the `Ok` will
The `?` placed after a `Result` value is defined to work in almost the same way
as the `match` expressions we defined to handle the `Result` values in Listing
9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will
get returned from this expression and the program will continue. If the value
is an `Err`, the value inside the `Err` will be returned from the whole
function as if we had used the `return` keyword so that the error value gets
propagated to the caller.
function as if we had used the `return` keyword so the error value gets
propagated to the calling code.
In the context of Listing 9-6, the `?` at the end of the `File::open` call will
The one difference between the `match` expression from Listing 9-6 and what the
question mark operator does is that when using the question mark operator,
error values go through the `from` function defined in the `From` trait in the
standard library. Many error types implement the `from` function to convert an
error of one type into an error of another type. When used by the question mark
operator, the call to the `from` function converts the error type that the
question mark operator gets into the error type defined in the return type of
the current function that 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, `?`
will return early out of the whole function and give any `Err` value to our
caller. The same thing applies to the `?` at the end of the `read_to_string`
call.
will return early out of the whole function and give any `Err` value to the
calling code. The same thing applies to the `?` at the end of the
`read_to_string` call.
The `?` eliminates a lot of boilerplate and makes this functions
implementation simpler. We could even shorten this code further by chaining
method calls immediately after the `?`:
method calls immediately after the `?` as shown in Listing 9-8:
<span class="filename">Filename: src/main.rs</span>
```rust
use std::io;
@ -390,21 +424,24 @@ fn read_username_from_file() -> Result<String, io::Error> {
}
```
<span class="caption">Listing 9-8: Chaining method calls after the question
mark operator</span>
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
chained the call to `read_to_string` directly onto the result of
`File::open("hello.txt")?`. We still have a `?` at the end of the
`read_to_string` call, and we still return an `Ok` value containing the
username in `s` when both `File::open` and `read_to_string` succeed rather than
returning errors. The functionality is again the same as in Listing 9-5 and
Listing 9-6, this is just a different, more ergonomic way to write it.
returning errors. The functionality is again the same as in Listing 9-6 and
Listing 9-7; this is just a different, more ergonomic way to write it.
### `?` Can Only Be Used in Functions That Return `Result`
#### `?` Can Only Be Used in Functions That Return Result
The `?` can only be used in functions that have a return type of `Result`,
since it is defined to work in exactly the same way as the `match` expression
we defined in Listing 9-5. The part of the `match` that requires a return type
of `Result` is `return Err(e)`, so the return type of the function must be a
because it is defined to work in the same way as the `match` expression we
defined in Listing 9-6. The part of the `match` that requires a return type of
`Result` is `return Err(e)`, so the return type of the function must be a
`Result` to be compatible with this `return`.
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
that doesn't return a result is STILL confusing. Since we want to only explain
`?` now, I've changed the example, but if you try running this code you WON'T
get the error message below.
I'm bugging people to try and get
https://github.com/rust-lang/rust/issues/35946 fixed soon, hopefully before this
chapter gets through copy editing-- at that point I'll make sure to update this
error message. /Carol -->
When we compile this, we get the following error message:
When we compile this code, we get the following error message:
```text
error[E0308]: mismatched types
-->
error[E0277]: the `?` operator can only be used in a function that returns
`Result` (or another type that implements `std::ops::Try`)
--> src/main.rs:4:13
|
3 | let f = File::open("hello.txt")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum
`std::result::Result`
4 | let f = File::open("hello.txt")?;
| ------------------------
| |
| cannot use the `?` operator in a function that returns `()`
| in this macro invocation
|
= note: expected type `()`
= note: found type `std::result::Result<_, _>`
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
```
This error is pointing out that we have mismatched types: the `main` function
has a return type of `()`, but the `?` might return a `Result`. In functions
that dont return `Result`, when you call other functions that return `Result`,
youll need to use a `match` or one of the `Result` methods to handle it,
instead of using `?` to potentially propagate the error to the caller.
This error points out that were only allowed to use the question mark operator
in a function that returns `Result`. In functions that dont return `Result`,
when you call other functions that return `Result`, youll need to use a
`match` or one of the `Result` methods to handle it instead of using `?` to
potentially propagate the error to the calling code.
Now that 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

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
`Result`? When code panics, theres no way to recover. You could choose to call
`panic!` for any error situation, whether theres a possible way to recover or
not, but then youre making the decision for your callers that a situation is
unrecoverable. When you choose to return a `Result` value, you give your caller
options, rather than making the decision for them. They could choose to attempt
to recover in a way thats appropriate for their situation, or they could
decide that actually, an `Err` value in this case is unrecoverable, so they can
call `panic!` and turn your recoverable error into an unrecoverable one.
Therefore, returning `Result` is a good default choice when youre defining a
function that might fail.
`Result`? When code panics, theres no way to recover. You could call `panic!`
for any error situation, whether theres a possible way to recover or not, but
then youre making the decision on behalf of the code calling your code that a
situation is unrecoverable. When you choose to return a `Result` value, you
give the calling code options rather than making the decision for it. The
calling code could choose to attempt to recover in a way thats appropriate for
its situation, or it could decide that an `Err` value in this case is
unrecoverable, so it can call `panic!` and turn your recoverable error into an
unrecoverable one. Therefore, returning `Result` is a good default choice when
youre defining a function that might fail.
There are a few situations in which its more appropriate to write code that
panics instead of returning a `Result`, but they are less common. Lets discuss
why its appropriate to panic in examples, prototype code, and tests, then
situations where you as a human can know a method wont fail that the compiler
cant reason about, and conclude with some general guidelines on how to decide
In a few situations its more appropriate to write code that panics instead of
returning a `Result`, but they are less common. Lets explore why its
appropriate to panic in examples, prototype code, and tests; then in situations
where you as a human can know a method wont fail that the compiler cant
reason about; and conclude with some general guidelines on how to decide
whether to panic in library code.
### Examples, Prototype Code, and Tests: Perfectly Fine to Panic
### Examples, Prototype Code, and Tests Are All Places its Perfectly Fine to Panic
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
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
application to handle errors, which can differ based on what the rest of your
code is doing.
`panic!` is meant as a placeholder for the way that youd want your application
to handle errors, which can differ based on what the rest of your code is doing.
Similarly, the `unwrap` and `expect` methods are very handy when prototyping,
before 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
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
sense to do.
is marked as a failure, calling `unwrap` or `expect` is exactly what should
happen.
### Cases When You Have More Information Than The Compiler
### Cases When You Have More Information Than the Compiler
It would also be appropriate to call `unwrap` when you have some other logic
that ensures the `Result` will have an `Ok` value, but the logic 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
failing in general, even though its logically impossible in your particular
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:
```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
of the `parse` method: we still get a `Result` value, and the compiler will
still make us handle the `Result` as if the `Err` variant is still a
possibility since the compiler 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
being hardcoded into the program, and therefore *did* have a possibility of
failure, wed definitely want to handle the `Result` in a more robust way
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 rather
than being hardcoded into the program, and therefore *did* have a possibility
of failure, wed definitely want to handle the `Result` in a more robust way
instead.
### Guidelines for Error Handling
Its advisable to have your code `panic!` when its possible that you could end
up in a bad state—in this context, bad state is when some assumption,
guarantee, contract, or invariant has been broken, such as when invalid values,
contradictory values, or missing values are passed to your code—plus one or
more of the following:
Its advisable to have your code `panic!` when its possible that your code
could end up in a bad state. In this context, bad state is when some
assumption, guarantee, contract, or invariant has been broken, such as when
invalid values, contradictory values, or missing values are passed to your
code—plus one or more of the following:
* The bad state is not something thats *expected* to happen occasionally
* 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
* 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.
* 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
thing might be to `panic!` and alert the person using your library to the bug
in their code so that they can fix it during development. Similarly, `panic!`
is often appropriate if youre calling external code that is out of your
control, and it returns an invalid state that you have no way of fixing.
choice might be to `panic!` and alert the person using your library to the bug
in their code so they can fix it during development. Similarly, `panic!` is
often appropriate if youre calling external code that is out of your control,
and it returns an invalid state that you have no way of fixing.
When a bad state is reached, but its expected to happen no matter how well you
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,
or an HTTP request returning a status that indicates you have hit a rate limit.
In these cases, you should indicate that failure is an expected possibility by
returning a `Result` in order to propagate these bad states upwards so that the
caller can decide how they would like to handle the problem. To `panic!`
wouldnt be the best way to handle these cases.
making a `panic!` call. Examples of this include a parser being given malformed
data or an HTTP request returning a status that indicates you have hit a rate
limit. In these cases, you should indicate that failure is an expected
possibility by returning a `Result` to propagate these bad states upwards so
the calling code can decide how to handle the problem. To `panic!` wouldnt be
the best way to handle these cases.
When your code performs operations on values, your code should verify the
values are valid first, and `panic!` if the values arent valid. This is mostly
for safety reasons: attempting to operate on invalid data can expose your code
to vulnerabilities. This is the main reason that the standard library will
`panic!` if you attempt an out-of-bounds array access: trying to access memory
that doesnt belong to the current data structure is a common security problem.
to vulnerabilities. This is the main reason the standard library will `panic!`
if you attempt an out-of-bounds memory access: trying to access memory that
doesnt belong to the current data structure is a common security problem.
Functions often have *contracts*: their behavior is only guaranteed if the
inputs meet particular requirements. Panicking when the contract is violated
makes sense because a contract violation always indicates a caller-side bug,
and it is not a kind of error you want callers to have to explicitly handle. In
fact, theres no reasonable way for calling code to recover: the calling
*programmers* need to fix the code. Contracts for a function, especially when a
violation will cause a panic, should be explained in the API documentation for
the function.
and its not a kind of error you want the calling code to have to explicitly
handle. In fact, theres no reasonable way for calling code to recover: the
calling *programmers* need to fix the code. Contracts for a function,
especially when a violation will cause a panic, should be explained in the API
documentation for the function.
Having lots of error checks in all of your functions would be verbose and
annoying, though. Luckily, you can use Rusts type system (and thus the type
checking the compiler does) to do a lot of the checks for you. If your function
However, having lots of error checks in all of your functions would be verbose
and annoying. Fortunately, you can use Rusts type system (and thus the type
checking the compiler does) to do many of the checks for you. If your function
has a particular type as a parameter, you can proceed with your codes logic
knowing that the compiler has already ensured you have a valid value. For
example, if you have a type rather than an `Option`, your program expects to
have *something* rather than *nothing*. Your code then 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
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
@ -123,19 +122,19 @@ parameter is never negative.
### Creating Custom Types for Validation
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
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 those numbers before checking it against our secret number, only that
it was positive. In this case, the consequences were not very dire: our output
of “Too high” or “Too low” would still be correct. It would be a useful
enhancement to guide the user towards valid guesses, though, and have different
behavior when a user guesses a number thats out of range versus when a user
types, for example, letters instead.
one step further and look at creating a custom type for validation. Recall the
guessing game in Chapter 2 where our code asked the user to guess a number
between 1 and 100. We never validated that the users guess was between those
numbers before checking it against our secret number; we only validated that
the guess was positive. In this case, the consequences were not very dire: our
output of “Too high” or “Too low” would still be correct. It would be a useful
enhancement to guide the user toward valid guesses and have different behavior
when a user guesses a number thats out of range versus when a user types, for
example, letters instead.
One way to do this would be to parse the guess as an `i32` instead of only a
`u32`, to allow potentially negative numbers, then add a check for the number
being in range:
`u32` to allow potentially negative numbers, and then add a check for the
number being in range, like so:
```rust,ignore
loop {
@ -156,7 +155,7 @@ loop {
}
```
The `if` expression checks to see if our value is out of range, tells the user
The `if` expression checks whether our value is out of range, tells the user
about the problem, and calls `continue` to start the next iteration of the loop
and ask for another guess. After the `if` expression, we can proceed with the
comparisons between `guess` and the secret number knowing that `guess` is
@ -170,7 +169,7 @@ to have a check like this in every function.
Instead, we can make a new type and put the validations in a function to create
an instance of the type rather than repeating the validations everywhere. That
way, 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
receives a value between 1 and 100:
@ -196,7 +195,7 @@ impl Guess {
}
```
<span class="caption">Listing 9-8: A `Guess` type that will only continue with
<span class="caption">Listing 9-9: A `Guess` type that will only continue with
values between 1 and 100</span>
First, we define a struct named `Guess` that has a field named `value` that
@ -205,35 +204,35 @@ holds a `u32`. This is where the number will be stored.
Then we implement an associated function named `new` on `Guess` that creates
instances of `Guess` values. The `new` function is defined to have one
parameter named `value` of type `u32` and to return a `Guess`. The code in the
body of the `new` function tests `value` to make sure it is between 1 and 100.
If `value` doesnt pass this test, we call `panic!`, which will alert the
programmer who is calling this code that they have a bug they need to fix,
since creating a `Guess` with a `value` outside this range would violate the
contract that `Guess::new` is relying on. The conditions in which `Guess::new`
might panic should be discussed in its public-facing API documentation; well
cover documentation conventions around indicating the possibility of a `panic!`
in the API documentation that you create in Chapter 14. If `value` does pass
the test, we create a new `Guess` with its `value` field set to the `value`
parameter and return the `Guess`.
body of the `new` function tests `value` to make sure its between 1 and 100.
If `value` doesnt pass this test, we make a `panic!` call, which will alert
the programmer who is writing the calling code that they have a bug they need
to fix, because creating a `Guess` with a `value` outside this range would
violate the contract that `Guess::new` is relying on. The conditions in which
`Guess::new` might panic should be discussed in its public-facing API
documentation; well cover documentation conventions indicating the possibility
of a `panic!` in the API documentation that you create in Chapter 14. If
`value` does pass the test, we create a new `Guess` with its `value` field set
to the `value` parameter and return the `Guess`.
Next, we implement a method named `value` that borrows `self`, doesnt have any
other parameters, and returns a `u32`. This is a kind of method sometimes
called a *getter*, since its purpose is to get some data from its fields and
called a *getter*, because its purpose is to get some data from its fields and
return it. This public method is necessary because the `value` field of the
`Guess` struct is private. Its important that the `value` field is private so
that code using the `Guess` struct is not allowed to set `value` directly:
callers outside the module *must* use the `Guess::new` function to create an
instance of `Guess`, which ensures theres no way for a `Guess` to have a
`value` that hasnt been checked by the conditions in the `Guess::new` function.
code using the `Guess` struct is not allowed to set `value` directly: code
outside the module *must* use the `Guess::new` function to create an instance
of `Guess`, which ensures theres no way for a `Guess` to have a `value` that
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
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
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
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
@ -241,6 +240,7 @@ operations might fail in a way that your code could recover from. You can use
success or failure as well. Using `panic!` and `Result` in the appropriate
situations will make your code more reliable in the face of inevitable problems.
Now that weve seen useful ways that the standard library uses generics with
the `Option` and `Result` enums, lets talk about how generics work and how you
can make use of them in your code.
Now that youve seen useful ways that the standard library uses generics with
the `Option` and `Result` enums, well talk about how generics work and how you
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>
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
describe the behaviors that types that implement this trait will need to have,
in this case `fn summary(&self) -> String`. After the method signature, instead
of providing an implementation within curly braces, we put a semicolon. Each
type that implements this trait must then provide its own custom behavior for
the body of the method, but the compiler will enforce that any type that has
the `Summarizable` trait will have the method `summary` defined for it with
this signature exactly.
case `Summarizable`. Inside curly brackets we declare the method signatures
that describe the behaviors that types that implement this trait will need to
have, in this case `fn summary(&self) -> String`. After the method signature,
instead of providing an implementation within curly brackets, we put a
semicolon. Each type that implements this trait must then provide its own
custom behavior for the body of the method, but the compiler will enforce that
any type that has the `Summarizable` trait will have the method `summary`
defined for it with this signature exactly.
A trait can have multiple methods in its body, with the method signatures
listed one per line and each line ending in a semicolon.
@ -106,7 +106,7 @@ related to a trait. The difference is after `impl`, we put the trait name that
we want to implement, then say `for` and the name of the type that we want to
implement the trait for. Within the `impl` block, we put the method signatures
that the trait definition has defined, but instead of putting a semicolon after
each signature, we put curly braces and fill in the method body with the
each signature, we put curly brackets and fill in the method body with the
specific behavior that we want the methods of the trait to have for the
particular type.
@ -187,7 +187,7 @@ behavior. When we implement the trait on a particular type, we can choose to
keep or override each methods default behavior.
Listing 10-15 shows how we could have chosen to specify a default string for
the `summary` method of the `Summarize` trait instead of only choosing to only
the `summary` method of the `Summarize` trait instead of choosing to only
define the method signature like we did in Listing 10-12:
<span class="filename">Filename: lib.rs</span>
@ -240,7 +240,7 @@ implementation.
Default implementations are allowed to call the other methods in the same
trait, even if those other methods dont have a default implementation. In this
way, a trait can provide a lot of useful functionality and only require
implementers to specify a small part of it. We could choose to have the
implementors to specify a small part of it. We could choose to have the
`Summarizable` trait also have an `author_summary` method whose implementation
is required, then a `summary` method that has a default implementation that
calls the `author_summary` method:
@ -519,7 +519,7 @@ let s = 3.to_string();
```
Blanket implementations appear in the documentation for the trait in the
“Implementers” section.
“Implementors” section.
Traits and trait bounds let us write code that uses generic type parameters in
order to reduce duplication, but still specify to the compiler exactly what

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
out of scope when we hit the closing curly brace on line 7, ending the inner
out of scope when we hit the closing curly bracket on line 7, ending the inner
scope. But `r` is valid for the outer scope; its scope is larger and we say
that it “lives longer.” If Rust allowed this code to work, `r` would be
referencing memory that was deallocated when `x` went out of scope, and
@ -101,9 +101,9 @@ Listing 10-18 with annotations showing the lifetimes of the variables:
correct? I want to leave a note for production, make sure we can make that
clear -->
<!-- Yes, the inside block for the `'b` lifetime starts with the `let x = 5;`
line and ends with the first closing curly brace on the 7th line. Do you think
the text art comments work or should we make an SVG diagram that has nicer
looking arrows and labels? /Carol -->
line and ends with the first closing curly bracket on the 7th line. Do you
think the text art comments work or should we make an SVG diagram that has
nicer looking arrows and labels? /Carol -->
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`
@ -184,7 +184,7 @@ and below). If these topics are confusing you in this context, I'd be
interested to know if rereading Chapter 4 clears up that confusion.
/Carol -->
Refer back to the “String Slices as Arguments” section of Chapter 4 for more
Refer back to the “String Slices as Parameters” section of Chapter 4 for more
discussion about why these are the arguments we want.
If we try to implement the `longest` function as shown in Listing 10-22, it

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`
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
the `assert!` macro.
code in *src/lib.rs* and write some tests for it using the `assert!` macro.
<span class="filename">Filename: src/lib.rs</span>
@ -592,7 +591,7 @@ Listing 11-8 shows how wed write a test that checks the error conditions of
<span class="filename">Filename: src/lib.rs</span>
```rust
struct Guess {
pub struct Guess {
value: u32,
}
@ -638,7 +637,7 @@ Looks good! Now lets introduce a bug in our code, by removing the condition
that the `new` function will panic if the value is greater than 100:
```rust
# struct Guess {
# pub struct Guess {
# value: u32,
# }
#
@ -686,7 +685,7 @@ different messages depending on whether the value was too small or too large:
<span class="filename">Filename: src/lib.rs</span>
```rust
struct Guess {
pub struct Guess {
value: u32,
}
@ -764,7 +763,7 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
```
The failure message indicates that this test did indeed panic as we expected,
but the panic message `did not include expected string 'Guess value must be
but the panic message did not include expected string `'Guess value must be
less than or equal to 100'`. We can see the panic message that we did get,
which in this case was `Guess value must be greater than or equal to 1, got
200.` We could then start figuring out where our bug was!

Some files were not shown because too many files have changed in this diff Show More