From b60c3e2c1185e32ea830780c53c548e0a047bf59 Mon Sep 17 00:00:00 2001 From: York Xiang Date: Thu, 30 Apr 2015 23:41:02 +0800 Subject: [PATCH 01/23] Closes #15919 --- src/test/compile-fail/issue-15919.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/test/compile-fail/issue-15919.rs diff --git a/src/test/compile-fail/issue-15919.rs b/src/test/compile-fail/issue-15919.rs new file mode 100644 index 0000000000000..1fd1d44d0a791 --- /dev/null +++ b/src/test/compile-fail/issue-15919.rs @@ -0,0 +1,14 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x = [0usize; 9223372036854775808]; // We don't officially support 128-bit architecture +//~^ error: the type `[usize; 9223372036854775808]` is too big for the current architecture +} From 4c8b8139496138f81d1d69932c7b3af09bd8148f Mon Sep 17 00:00:00 2001 From: York Xiang Date: Thu, 30 Apr 2015 23:41:02 +0800 Subject: [PATCH 02/23] Closes #15919 --- src/test/compile-fail/issue-15919.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/issue-15919.rs b/src/test/compile-fail/issue-15919.rs index 1fd1d44d0a791..1461cf2423888 100644 --- a/src/test/compile-fail/issue-15919.rs +++ b/src/test/compile-fail/issue-15919.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// error-pattern: the type `[i32; 9223372036854775808]` is too big for the current architecture + fn main() { - let x = [0usize; 9223372036854775808]; // We don't officially support 128-bit architecture -//~^ error: the type `[usize; 9223372036854775808]` is too big for the current architecture + let _x = [0; 9223372036854775808]; } From 9b3d315fff2e7009d3ebd945bb848c4afa2ebfb4 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 8 May 2015 15:15:03 -0700 Subject: [PATCH 03/23] std: Update crate docs Attempted to organize them in a way more relevant to what newbies would be interested in hearing. --- src/libstd/lib.rs | 98 +++++++++++++++++++-------------------- src/libstd/prelude/mod.rs | 2 +- src/libstd/prelude/v1.rs | 2 +- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 6a84c6ace47b4..1b99b2989370a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -12,33 +12,35 @@ //! //! The Rust Standard Library provides the essential runtime //! functionality for building portable Rust software. -//! It is linked to all Rust crates by default. //! -//! ## Intrinsic types and operations +//! It is linked to all Rust crates by default as though they +//! contained a crate-level `extern crate std` crate import. Therefore +//! the standard library can be accessed in `use` statements through +//! the path `std`, as in `use std::thread`, or in expressions through +//! the absolute path `::std`, as in `::std::thread::sleep_ms(100)`. //! -//! The [`ptr`](ptr/index.html) and [`mem`](mem/index.html) -//! modules deal with unsafe pointers and memory manipulation. -//! [`marker`](marker/index.html) defines the special built-in traits, -//! and [`raw`](raw/index.html) the runtime representation of Rust types. -//! These are some of the lowest-level building blocks in Rust. +//! Furthermore, the standard library defines [The Rust +//! Prelude](prelude/index.html), a small collection of items, mostly +//! traits, that are imported into and available in every module. //! -//! ## Math on primitive types and math traits +//! ## What is in the standard library //! -//! Although basic operations on primitive types are implemented -//! directly by the compiler, the standard library additionally -//! defines many common operations through traits defined in -//! mod [`num`](num/index.html). +//! The standard library is minimal, a set of battle-tested +//! core types and shared abstractions for the [broader Rust +//! ecosystem][https://crates.io] to build on. //! -//! ## Pervasive types +//! The [primitive types](#primitives), though not defined in the +//! standard library, are documented here, as are the predefined +//! [macros](#macros). //! -//! The [`option`](option/index.html) and [`result`](result/index.html) -//! modules define optional and error-handling types, `Option` and `Result`. -//! [`iter`](iter/index.html) defines Rust's iterator protocol -//! along with a wide variety of iterators. -//! [`Cell` and `RefCell`](cell/index.html) are for creating types that -//! manage their own mutability. +//! ## Containers and collections //! -//! ## Vectors, slices and strings +//! The [`option`](option/index.html) and +//! [`result`](result/index.html) modules define optional and +//! error-handling types, `Option` and `Result`. The +//! [`iter`](iter/index.html) module defines Rust's iterator trait, +//! [`Iterater`](iter/trait.Iterator.html), which works with the `for` +//! loop to access collections. //! //! The common container type, `Vec`, a growable vector backed by an array, //! lives in the [`vec`](vec/index.html) module. Contiguous, unsized regions @@ -56,42 +58,36 @@ //! macro, and for converting from strings use the //! [`FromStr`](str/trait.FromStr.html) trait. //! -//! ## Platform abstractions +//! Data may be shared by placing it a reference-counted box, the +//! [`Rc`][rc/index.html] type, and if further contained in a [`Cell` +//! or `RefCell`](cell/index.html), may be mutated as well as shared. +//! Likewise, in a concurrent setting it is common to pair an +//! atomically-reference-counted box, [`Arc`](sync/struct.Arc.html), +//! with a [`Mutex`](sync/struct.Mutex.html) to get the same effect. //! -//! Besides basic data types, the standard library is largely concerned -//! with abstracting over differences in common platforms, most notably -//! Windows and Unix derivatives. The [`os`](os/index.html) module -//! provides a number of basic functions for interacting with the -//! operating environment, including program arguments, environment -//! variables, and directory navigation. The [`path`](path/index.html) -//! module encapsulates the platform-specific rules for dealing -//! with file paths. -//! -//! `std` also includes the [`ffi`](ffi/index.html) module for interoperating -//! with the C language. -//! -//! ## Concurrency, I/O, and the runtime +//! The [`collections`](collections/index.html) module defines maps, +//! sets, linked lists and other typical collection types, including +//! the common [`HashMap`](collections/struct.HashMap.html). //! -//! The [`thread`](thread/index.html) module contains Rust's threading abstractions. -//! [`sync`](sync/index.html) contains further, primitive, shared memory types, -//! including [`atomic`](sync/atomic/index.html), and [`mpsc`](sync/mpsc/index.html), -//! which contains the channel types for message passing. +//! ## Platform abstractions and I/O //! -//! Common types of I/O, including files, TCP, UDP, pipes, Unix domain sockets, and -//! process spawning, are defined in the [`io`](io/index.html) module. -//! -//! Rust's I/O and concurrency depends on a small runtime interface -//! that lives, along with its support code, in mod [`rt`](rt/index.html). -//! While a notable part of the standard library's architecture, this -//! module is not intended for public use. +//! Besides basic data types, the standard library is largely concerned +//! with abstracting over differences in common platforms, most notably +//! Windows and Unix derivatives. //! -//! ## The Rust prelude and macros +//! Common types of I/O, including [files](fs/struct.File.html), +//! [TCP](net/struct.TcpStream.html), +//! [UDP](net/struct.UdpSocket.html), are defined in the +//! [`io`](io/index.html), [`fs`](fs/index.html), and +//! [`net`](net/index.html) modulesu. //! -//! Finally, the [`prelude`](prelude/index.html) defines a -//! common set of traits, types, and functions that are made available -//! to all code by default. [`macros`](macros/index.html) contains -//! all the standard macros, such as `assert!`, `panic!`, `println!`, -//! and `format!`, also available to all Rust code. +//! The [`thread`](thread/index.html) module contains Rust's threading +//! abstractions. [`sync`](sync/index.html) contains further, +//! primitive, shared memory types, including +//! [`atomic`](sync/atomic/index.html), and +//! [`mpsc`](sync/mpsc/index.html), which contains the channel types +//! for message passing. + // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364) #![cfg_attr(stage0, feature(custom_attribute))] #![crate_name = "std"] diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index 09fa10dacf98c..7bfe1cdd7294d 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The Rust prelude +//! The Rust Prelude //! //! Because `std` is required by most serious Rust software, it is //! imported at the topmost level of every crate by default, as if the diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 6dc11c505a914..46c0103e08764 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The first version of the prelude of the standard library. +//! The first version of the prelude of The Rust Standard Library. #![stable(feature = "rust1", since = "1.0.0")] From 4b520592eac6b43c72016145855d5616fb16910e Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Mon, 11 May 2015 14:28:31 -0500 Subject: [PATCH 04/23] Add error explanations for E0049, E0050, E0069, E0106, E0107, E0166. --- src/librustc_typeck/check/mod.rs | 3 +- src/librustc_typeck/diagnostics.rs | 185 ++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3769e9fa0f36a..fd44f6c7678e6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3268,7 +3268,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, if let Err(_) = fcx.mk_eqty(false, infer::Misc(expr.span), result_type, ty::mk_nil(fcx.tcx())) { span_err!(tcx.sess, expr.span, E0069, - "`return;` in function returning non-nil"); + "`return;` in a function whose return type is \ + not `()`"); }, Some(ref e) => { check_expr_coercable_to_type(fcx, &**e, result_type); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index ea872d1014425..99932cdf23b6b 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -19,6 +19,51 @@ methods that do not have default implementations), as well as any required trait items like associated types or constants. "##, +E0049: r##" +This error indicates that an attempted implementation of a trait method +has the wrong number of type parameters. + +For example, the trait below has a method `foo` with a type parameter `T`, +but the implementation of `foo` for the type `Bar` is missing this parameter: + +``` +trait Foo { + fn foo(T) -> Self; +} + +struct Bar; + +// error: method `foo` has 0 type parameters but its trait declaration has 1 +// type parameter +impl Foo for Bar { + fn foo(x: bool) -> Self { Bar } +} +``` +"##, + +E0050: r##" +This error indicates that an attempted implementation of a trait method +has the wrong number of function parameters. + +For example, the trait below has a method `foo` with two function parameters +(`&self` and `u8`), but the implementation of `foo` for the type `Bar` omits +the `u8` parameter: + +``` +trait Foo { + fn foo(&self, u8) -> bool; +} + +struct Bar; + +// error: method `foo` has 1 parameter but the declaration in trait `Foo::foo` +// has 2 +impl Foo for Bar { + fn foo(&self) -> bool { true } +} +``` +"##, + E0054: r##" It is not allowed to cast to a bool. If you are trying to cast a numeric type to a bool, you can compare it with zero instead: @@ -63,6 +108,22 @@ LinkedList::new() += 1; ``` "##, +E0069: r##" +This error means that the compiler found a function whose body contains a +`return;` statement but whose return type is not `()`. For example: + +``` +// error +fn foo() -> u8 { + return; +} +``` + +When you omit the value from a `return` expression (that is, when you use +`return;` instead of `return x;`), the value `()` gets returned. So `return;` +is always incorrect for a function whose return type is not `()`. +"##, + E0081: r##" Enum discriminants are used to differentiate enum variants stored in memory. This error indicates that the same value was used for two or more variants, @@ -138,6 +199,110 @@ enum Empty {} ``` "##, +E0106: r##" +This error indicates that a lifetime is missing from a type. If it is an error +inside a function signature, the problem may be with failing to adhere to the +lifetime elision rules (see below). + +Here are some simple examples of where you'll run into this error: + +``` +struct Foo { x: &bool } // error +struct Foo<'a> { x: &'a bool } // correct + +enum Bar { A(u8), B(&bool), } // error +enum Bar<'a> { A(u8), B(&'a bool), } // correct + +type MyStr = &str; // error +type MyStr<'a> = &'a str; //correct + +``` + +Lifetime elision is a special, limited kind of inference for lifetimes in +function signatures which allows you to leave out lifetimes in certain cases. +For example, the lifetimes on parameter in the following function signatures +have been left out, but they still compile successfully: + +``` +fn foo(x: &str) { } + +fn bar(x: &str, y: &str) { } + +fn baz(x: &str) -> &str { x } +``` + +To explain the lifetime elision rules, we need to first discuss some background. +The lifetime elision rules consider each lifetime in a function signature, +whether it's elided or not, to be in a certain position, either *input +position*, for function parameters, or *output position*, for the return type. +For example, the function: + +``` +fn hello<'a>(name: &'a str) -> (&'static str, &str) { + ("hello", name) +} +``` + +has a signature with one lifetime in input position and two lifetimes in output +position. + +The lifetime elision rules require that any function signature with an elided +output lifetime must either have + + - exactly one input lifetime + - or, multiple input lifetimes, but the function must also be a method with a + `&self` or `&mut self` receiver + +In the first case, the output lifetime is inferred to be the same as the unique +input lifetime. In the second case, the lifetime is instead inferred to be the +same as the lifetime on `&self` or `&mut self`. + +Here are some examples of elision errors: + +``` +// error, no input lifetimes +fn foo() -> &str { ... } + +// error, `x` and `y` have distinct lifetimes inferred +fn bar(x: &str, y: &str) -> &str { ... } + +// error, `y`'s lifetime is inferred to be distinct from `x`'s +fn baz<'a>(x: &'a str, y: &str) -> &str { ... } +``` +"##, + +E0107: r##" +This error means that an incorrect number of lifetime parameters were provided +for a type (like a struct or enum) or trait. + +Some basic examples include: + +``` +struct Foo<'a>(&'a str); +enum Bar { A, B, C } + +struct Baz<'a> { + foo: Foo, // error: expected 1, found 0 + bar: Bar<'a>, // error: expected 0, found 1 +} +``` + +Here's an example that is currently an error, but may work in a future version +of Rust: + +``` +struct Foo<'a>(&'a str); + +trait Quux { } +impl Quux for Foo { } // error: expected 1, found 0 +``` + +Lifetime elision in implementation headers was part of the lifetime elision +RFC. It is, however, [currently unimplemented][iss15872]. + +[iss15872]: https://github.com/rust-lang/rust/issues/15872 +"##, + E0131: r##" It is not possible to define `main` with type parameters, or even with function parameters. When `main` is present, it must take no arguments and return `()`. @@ -152,6 +317,20 @@ fn(isize, *const *const u8) -> isize ``` "##, +E0166: r##" +This error means that the compiler found a return expression in a function +marked as diverging. A function diverges if it has `!` in the place of the +return type in its signature. For example: + +``` +fn foo() -> ! { return; } // error +``` + +For a function that diverges, every control path in the function must end +with a call to `panic!()` or another diverging function. Attempting to return +from a diverging function is an error. +"##, + E0184: r##" Explicitly implementing both Drop and Copy for a type is currently disallowed. This feature can make some sense in theory, but the current implementation is @@ -313,8 +492,6 @@ register_diagnostics! { E0040, // explicit use of destructor method E0044, // foreign items may not have type parameters E0045, // variadic function must have C calling convention - E0049, - E0050, E0053, E0055, // method has an incompatible type for trait E0057, // method has an incompatible type for trait @@ -323,7 +500,6 @@ register_diagnostics! { E0061, E0066, E0068, - E0069, E0070, E0071, E0072, @@ -346,8 +522,6 @@ register_diagnostics! { E0102, E0103, E0104, - E0106, - E0107, E0116, E0117, E0118, @@ -365,7 +539,6 @@ register_diagnostics! { E0159, E0163, E0164, - E0166, E0167, E0168, E0172, From 4fee8d3d89edab42efa7d7d4291e23af827e6bdf Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Mon, 11 May 2015 23:36:54 -0500 Subject: [PATCH 05/23] Add error explanation for E0066. This also updates the error message to remove mention of "managed heap", which was removed in 8a91d33. --- src/librustc_typeck/check/mod.rs | 6 +++--- src/librustc_typeck/diagnostics.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fd44f6c7678e6..169c4f6649739 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3033,8 +3033,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let mut checked = false; opt_place.as_ref().map(|place| match place.node { ast::ExprPath(None, ref path) => { - // FIXME(pcwalton): For now we hardcode the two permissible - // places: the exchange heap and the managed heap. + // FIXME(pcwalton): For now we hardcode the only permissible + // place: the exchange heap. let definition = lookup_full_def(tcx, path.span, place.id); let def_id = definition.def_id(); let referent_ty = fcx.expr_ty(&**subexpr); @@ -3048,7 +3048,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, if !checked { span_err!(tcx.sess, expr.span, E0066, - "only the managed heap and exchange heap are currently supported"); + "only the exchange heap is currently supported"); fcx.write_ty(id, tcx.types.err); } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 99932cdf23b6b..ecfab0d2c192e 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -91,6 +91,16 @@ enum variant, one of the fields was not provided. Each field should be specified exactly once. "##, +E0066: r##" +Box placement expressions (like C++'s "placement new") do not support any +place expression except the exchange heap (i.e. `std::boxed::HEAP`). +Furthermore, the syntax is changing to use `in` instead of `box`. See [RFC +470][rfc470] and [RFC 809][rfc809] for more details. + +[rfc470]: https://github.com/rust-lang/rfcs/pull/470 +[rfc809]: https://github.com/rust-lang/rfcs/pull/809 +"##, + E0067: r##" The left-hand side of an assignment operator must be an lvalue expression. An lvalue expression represents a memory location and includes item paths (ie, @@ -498,7 +508,6 @@ register_diagnostics! { E0059, E0060, E0061, - E0066, E0068, E0070, E0071, From c3dc769f76e38cdb64453c4acdf945c10ca14400 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 03:05:16 -0500 Subject: [PATCH 06/23] Add error explanation for E0322. --- src/librustc_typeck/diagnostics.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index ecfab0d2c192e..3f8b1a0ee196e 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -481,6 +481,13 @@ const B: [u32; foo()] = []; use std::{f64, u8}; const C: [u32; u8::MAX + f64::EPSILON] = []; ``` +"##, + +E0322: r##" +The `Sized` trait is a special trait built-in to the compiler for types with a +constant size known at compile-time. This trait is automatically implemented +for types as needed by the compiler, and it is currently disallowed to +explicitly implement it for a type. "## } @@ -621,7 +628,6 @@ register_diagnostics! { E0319, // trait impls for defaulted traits allowed just for structs/enums E0320, // recursive overflow during dropck E0321, // extended coherence rules for defaulted traits violated - E0322, // cannot implement Sized explicitly E0323, // implemented an associated const when another trait item expected E0324, // implemented a method when another trait item expected E0325, // implemented an associated type when another trait item expected From 71f6ed8464d3fe910d905ac8ed38ec83e5bd58aa Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 03:06:30 -0500 Subject: [PATCH 07/23] Remove comment description of E0234. This error code has been removed. --- src/librustc_typeck/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 3f8b1a0ee196e..e27ae857cf80a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -611,7 +611,7 @@ register_diagnostics! { E0231, // only named substitution parameters are allowed E0232, // this attribute must have a value E0233, - E0234, // `for` loop expression has type which does not implement the `Iterator` trait + E0234, E0235, // structure constructor specifies a structure of type but E0236, // no lang item for range syntax E0237, // no lang item for range syntax From cbeda68028c7c641c08bb74cac540dfd97433213 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 04:05:57 -0500 Subject: [PATCH 08/23] Add error explanation for E0201. --- src/librustc_typeck/diagnostics.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e27ae857cf80a..7c4edc1648059 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -350,6 +350,24 @@ it has been disabled for now. [iss20126]: https://github.com/rust-lang/rust/issues/20126 "##, +E0201: r##" +It is an error to define a method--a trait method or an inherent method--more +than once. + +For example, + +``` +struct Foo(u8); + +impl Foo { + fn bar() {} + + // error: duplicate method + fn bar(&self) -> bool { self.0 > 5 } +} +``` +"##, + E0204: r##" An attempt to implement the `Copy` trait for a struct failed because one of the fields does not implement `Copy`. To fix this, you must implement `Copy` for the @@ -580,7 +598,6 @@ register_diagnostics! { E0198, // negative implementations are not unsafe E0199, // implementing trait is not unsafe E0200, // trait requires an `unsafe impl` declaration - E0201, // duplicate method in trait impl E0202, // associated items are not allowed in inherent impls E0203, // type parameter has more than one relaxed default bound, // and only one is supported From 46753dacb1f0ab26ea4fdfa8fe1661066ce81dd2 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Wed, 13 May 2015 01:32:12 +1200 Subject: [PATCH 09/23] Record correct span for static mut items --- src/librustc_trans/save/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index d80086da20315..89cda6d785f07 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -520,12 +520,12 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); // If the variable is immutable, save the initialising expression. - let value = match mt { - ast::MutMutable => String::from_str(""), - ast::MutImmutable => self.span.snippet(expr.span), + let (value, keyword) = match mt { + ast::MutMutable => (String::from_str(""), keywords::Mut), + ast::MutImmutable => (self.span.snippet(expr.span), keywords::Static), }; - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Static); + let sub_span = self.span.sub_span_after_keyword(item.span, keyword); self.fmt.static_str(item.span, sub_span, item.id, From a22b3270b8dd0c2d23cb420b07ad1feb9691db2d Mon Sep 17 00:00:00 2001 From: Shmuale Mark Date: Tue, 12 May 2015 10:47:14 -0400 Subject: [PATCH 10/23] book: typo fixes, wording improvements. The text in iterators.md wasn't wrong, but it read awkwardly to my ear. --- src/doc/trpl/enums.md | 3 ++- src/doc/trpl/error-handling.md | 2 +- src/doc/trpl/iterators.md | 6 +++--- src/doc/trpl/lifetimes.md | 2 +- src/doc/trpl/ownership.md | 2 +- src/doc/trpl/primitive-types.md | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/doc/trpl/enums.md b/src/doc/trpl/enums.md index 80ea25eb35ce9..dade9044fdbe8 100644 --- a/src/doc/trpl/enums.md +++ b/src/doc/trpl/enums.md @@ -56,7 +56,8 @@ Character::Digit(10); Hand::Digit; ``` -Both variants are named `Digit`, but since they’re scoped to the `enum` name, +Both variants are named `Digit`, but since they’re scoped to the `enum` name +there's no ambiguity. Not supporting these operations may seem rather limiting, but it’s a limitation which we can overcome. There are two ways: by implementing equality ourselves, diff --git a/src/doc/trpl/error-handling.md b/src/doc/trpl/error-handling.md index 95b39a660636a..b3689968b7fd1 100644 --- a/src/doc/trpl/error-handling.md +++ b/src/doc/trpl/error-handling.md @@ -204,7 +204,7 @@ Because these kinds of situations are relatively rare, use panics sparingly. In certain circumstances, even though a function may fail, we may want to treat it as a panic instead. For example, `io::stdin().read_line(&mut buffer)` returns -an `Result`, when there is an error reading the line. This allows us to +a `Result`, when there is an error reading the line. This allows us to handle and possibly recover from error. If we don't want to handle this error, and would rather just abort the program, diff --git a/src/doc/trpl/iterators.md b/src/doc/trpl/iterators.md index abb14a6020524..e0cc45c254b99 100644 --- a/src/doc/trpl/iterators.md +++ b/src/doc/trpl/iterators.md @@ -212,9 +212,9 @@ see why consumers matter. As we've said before, an iterator is something that we can call the `.next()` method on repeatedly, and it gives us a sequence of things. Because you need to call the method, this means that iterators -are *lazy* and don't need to generate all of the values upfront. -This code, for example, does not actually generate the numbers -`1-100`, and just creates a value that represents the sequence: +can be *lazy* and not generate all of the values upfront. This code, +for example, does not actually generate the numbers `1-100`, instead +creating a value that merely represents the sequence: ```rust let nums = 1..100; diff --git a/src/doc/trpl/lifetimes.md b/src/doc/trpl/lifetimes.md index 86164a08a430f..40def201dcc94 100644 --- a/src/doc/trpl/lifetimes.md +++ b/src/doc/trpl/lifetimes.md @@ -41,7 +41,7 @@ With that in mind, let’s learn about lifetimes. # Lifetimes Lending out a reference to a resource that someone else owns can be -complicated, however. For example, imagine this set of operations: +complicated. For example, imagine this set of operations: - I acquire a handle to some kind of resource. - I lend you a reference to the resource. diff --git a/src/doc/trpl/ownership.md b/src/doc/trpl/ownership.md index 971bb7cd700db..2dabfa917fbd0 100644 --- a/src/doc/trpl/ownership.md +++ b/src/doc/trpl/ownership.md @@ -108,7 +108,7 @@ println!("v[0] is: {}", v[0]); Same error: “use of moved value.” When we transfer ownership to something else, we say that we’ve ‘moved’ the thing we refer to. You don’t need some sort of -special annotation here, it’s the default thing that Rust does. +special annotation here; it’s the default thing that Rust does. ## The details diff --git a/src/doc/trpl/primitive-types.md b/src/doc/trpl/primitive-types.md index bb2bf028700d2..fc307413c5eff 100644 --- a/src/doc/trpl/primitive-types.md +++ b/src/doc/trpl/primitive-types.md @@ -82,7 +82,7 @@ Let’s go over them by category: Integer types come in two varieties: signed and unsigned. To understand the difference, let’s consider a number with four bits of size. A signed, four-bit number would let you store numbers from `-8` to `+7`. Signed numbers use -“two’s compliment representation”. An unsigned four bit number, since it does +“two’s complement representation”. An unsigned four bit number, since it does not need to store negatives, can store values from `0` to `+15`. Unsigned types use a `u` for their category, and signed types use `i`. The `i` From 6f3701d604d604126c7e511c53cd05856dbc27fc Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Mon, 11 May 2015 23:43:58 -0700 Subject: [PATCH 11/23] readme: Rework the arch support matrix --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f92fc653e0ef3..da59629b049f3 100644 --- a/README.md +++ b/README.md @@ -87,9 +87,11 @@ fetch snapshots, and an OS that can execute the available snapshot binaries. Snapshot binaries are currently built and tested on several platforms: -* Windows (7, 8, Server 2008 R2), x86 and x86-64 (64-bit support added in Rust 0.12.0) -* Linux (2.6.18 or later, various distributions), x86 and x86-64 -* OSX 10.7 (Lion) or greater, x86 and x86-64 +| Platform \ Architecture | x86 | x86_64 | +|--------------------------------|-----|--------| +| Windows (7, 8, Server 2008 R2) | ✓ | ✓ | +| Linux (2.6.18 or later) | ✓ | ✓ | +| OSX (10.7 Lion or later) | ✓ | ✓ | You may find that other platforms work, but these are our officially supported build environments that are most likely to work. From 6ebba71a75986271e537ab4363f905787a0a222a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 12 May 2015 10:53:57 -0700 Subject: [PATCH 12/23] doc: Address feedback --- src/libstd/lib.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 1b99b2989370a..c0d8d8eacf797 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -13,11 +13,12 @@ //! The Rust Standard Library provides the essential runtime //! functionality for building portable Rust software. //! -//! It is linked to all Rust crates by default as though they -//! contained a crate-level `extern crate std` crate import. Therefore -//! the standard library can be accessed in `use` statements through -//! the path `std`, as in `use std::thread`, or in expressions through -//! the absolute path `::std`, as in `::std::thread::sleep_ms(100)`. +//! The rust standard library is available to all rust crates by +//! default, just as if contained an `extern crate std` import at the +//! crate root. Therefore the standard library can be accessed in +//! `use` statements through the path `std`, as in `use std::thread`, +//! or in expressions through the absolute path `::std`, as in +//! `::std::thread::sleep_ms(100)`. //! //! Furthermore, the standard library defines [The Rust //! Prelude](prelude/index.html), a small collection of items, mostly @@ -58,7 +59,7 @@ //! macro, and for converting from strings use the //! [`FromStr`](str/trait.FromStr.html) trait. //! -//! Data may be shared by placing it a reference-counted box, the +//! Data may be shared by placing it in a reference-counted box or the //! [`Rc`][rc/index.html] type, and if further contained in a [`Cell` //! or `RefCell`](cell/index.html), may be mutated as well as shared. //! Likewise, in a concurrent setting it is common to pair an @@ -79,12 +80,12 @@ //! [TCP](net/struct.TcpStream.html), //! [UDP](net/struct.UdpSocket.html), are defined in the //! [`io`](io/index.html), [`fs`](fs/index.html), and -//! [`net`](net/index.html) modulesu. +//! [`net`](net/index.html) modules. //! //! The [`thread`](thread/index.html) module contains Rust's threading -//! abstractions. [`sync`](sync/index.html) contains further, -//! primitive, shared memory types, including -//! [`atomic`](sync/atomic/index.html), and +//! abstractions. [`sync`](sync/index.html) contains further +//! primitive shared memory types, including +//! [`atomic`](sync/atomic/index.html) and //! [`mpsc`](sync/mpsc/index.html), which contains the channel types //! for message passing. From 33a30f272f019f3490800b189aee2307053ab403 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 13:44:50 -0500 Subject: [PATCH 13/23] Improve error explanations for E0049, E0050, E0166. --- src/librustc_typeck/diagnostics.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 7c4edc1648059..d8c92b9746930 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -28,7 +28,7 @@ but the implementation of `foo` for the type `Bar` is missing this parameter: ``` trait Foo { - fn foo(T) -> Self; + fn foo(x: T) -> Self; } struct Bar; @@ -51,7 +51,7 @@ the `u8` parameter: ``` trait Foo { - fn foo(&self, u8) -> bool; + fn foo(&self, x: u8) -> bool; } struct Bar; @@ -336,9 +336,9 @@ return type in its signature. For example: fn foo() -> ! { return; } // error ``` -For a function that diverges, every control path in the function must end -with a call to `panic!()` or another diverging function. Attempting to return -from a diverging function is an error. +For a function that diverges, every control path in the function must never +return, for example with a `loop` that never breaks or a call to another +diverging function (such as `panic!()`). "##, E0184: r##" From 5c77f0d98fbc36ed3982e1f000d9290f31f05bfa Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 13:53:40 -0500 Subject: [PATCH 14/23] Remove extended explanation of lifetime elision from E0106. I've just added a link instead to the lifetime elision section of the book. --- src/librustc_typeck/diagnostics.rs | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index d8c92b9746930..abe91c51e15d3 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -230,31 +230,7 @@ type MyStr<'a> = &'a str; //correct Lifetime elision is a special, limited kind of inference for lifetimes in function signatures which allows you to leave out lifetimes in certain cases. -For example, the lifetimes on parameter in the following function signatures -have been left out, but they still compile successfully: - -``` -fn foo(x: &str) { } - -fn bar(x: &str, y: &str) { } - -fn baz(x: &str) -> &str { x } -``` - -To explain the lifetime elision rules, we need to first discuss some background. -The lifetime elision rules consider each lifetime in a function signature, -whether it's elided or not, to be in a certain position, either *input -position*, for function parameters, or *output position*, for the return type. -For example, the function: - -``` -fn hello<'a>(name: &'a str) -> (&'static str, &str) { - ("hello", name) -} -``` - -has a signature with one lifetime in input position and two lifetimes in output -position. +For more background on lifetime elision see [the book][book-le]. The lifetime elision rules require that any function signature with an elided output lifetime must either have @@ -279,6 +255,8 @@ fn bar(x: &str, y: &str) -> &str { ... } // error, `y`'s lifetime is inferred to be distinct from `x`'s fn baz<'a>(x: &'a str, y: &str) -> &str { ... } ``` + +[book-le]: http://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision "##, E0107: r##" From 0ad15bbc2b80a07e130a61a770e439256afa2e9a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 7 May 2015 06:15:49 -0400 Subject: [PATCH 15/23] TRPL: release channels --- src/doc/trpl/SUMMARY.md | 1 + src/doc/trpl/release-channels.md | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/doc/trpl/release-channels.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index de7ded76280f6..fef61dcc70229 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -15,6 +15,7 @@ * [Concurrency](concurrency.md) * [Error Handling](error-handling.md) * [FFI](ffi.md) + * [Release Channels](release-channels.md) * [Syntax and Semantics](syntax-and-semantics.md) * [Variable Bindings](variable-bindings.md) * [Functions](functions.md) diff --git a/src/doc/trpl/release-channels.md b/src/doc/trpl/release-channels.md new file mode 100644 index 0000000000000..03e65539a2042 --- /dev/null +++ b/src/doc/trpl/release-channels.md @@ -0,0 +1,45 @@ +% Release Channels + +The Rust project uses a concept called ‘release channels’ to manage releases. +It’s important to understand this process to choose which version of Rust +your project should use. + +# Overview + +There are three channels for Rust releases: + +* Nightly +* Beta +* Stable + +New nightly releases are created once a day. Every six weeks, the latest +nightly release is promoted to ‘Beta’. At that point, it will only receive +patches to fix serious errors. Six weeks later, the beta is promoted to +‘Stable’, and becomes the next release of `1.x`. + +This process happens in parallel. So every six weeks, on the same day, +nightly goes to beta, beta goes to stable. When `1.x` is released, at +the same time, `1.(x + 1)-beta` is released, and the nightly becomes the +first version of `1.(x + 2)-nightly`. + +# Choosing a version + +Generally speaking, unless you have a specific reason, you should be using the +stable release channel. These releases are intended for a general audience. + +However, depending on your interest in Rust, you may choose to use nightly +instead. The basic tradeoff is this: in the nightly channel, you can use +unstable, new Rust features. However, unstable features are subject to change, +and so any new nightly release may break your code. If you use the stable +release, you cannot use experimental features, but the next release of Rust +will not cause significant issues through breaking changes. + +# Helping the ecosystem through CI + +What about beta? We encourage all Rust users who use the stable release channel +to also test against the beta channel in their continuous integration systems. +This will help alert the team in case there’s an accidental regression. + +Additionally, testing against nightly can catch regressions even sooner, and so +if you don’t mind a third build, we’d appreciate testing against all channels. + From 9a3e98be1c20781e566d12d14940c8dd3d237b15 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 11 May 2015 18:53:37 -0400 Subject: [PATCH 16/23] TRPL: Drop --- src/doc/trpl/drop.md | 68 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/doc/trpl/drop.md b/src/doc/trpl/drop.md index af58e23561c36..8bc25ef90d382 100644 --- a/src/doc/trpl/drop.md +++ b/src/doc/trpl/drop.md @@ -1,3 +1,67 @@ -% `Drop` +% Drop -Coming soon! +Now that we’ve discussed traits, let’s talk about a particular trait provided +by the Rust standard library, [`Drop`][drop]. The `Drop` trait provides a way +to run some code when a value goes out of scope. For example: + +[drop]: ../std/ops/trait.Drop.html + +```rust +struct HasDrop; + +impl Drop for HasDrop { + fn drop(&mut self) { + println!("Dropping!"); + } +} + +fn main() { + let x = HasDrop; + + // do stuff + +} // x goes out of scope here +``` + +When `x` goes out of scope at the end of `main()`, the code for `Drop` will +run. `Drop` has one method, which is also called `drop()`. It takes a mutable +reference to `self`. + +That’s it! The mechanics of `Drop` are very simple, but there are some +subtleties. For example, values are dropped in the opposite order they are +declared. Here’s another example: + +```rust +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() { + let firecracker = Firework { strength: 1 }; + let tnt = Firework { strength: 100 }; +} +``` + +This will output: + +```text +BOOM times 100!!! +BOOM times 1!!! +``` + +The TNT goes off before the firecracker does, because it was declared +afterwards. Last in, first out. + +So what is `Drop` good for? Generally, `Drop` is used to clean up any resources +associated with a `struct`. For example, the [`Arc` type][arc] is a +reference-counted type. When `Drop` is called, it will decrement the reference +count, and if the total number of references is zero, will clean up the +underlying value. + +[arc]: ../std/sync/struct.Arc.html From fc6372ea1f3ab98ab1a0252843a93a7045e95849 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 12 May 2015 13:34:29 -0400 Subject: [PATCH 17/23] TRPL: Rust inside other languages --- src/doc/trpl/SUMMARY.md | 1 + src/doc/trpl/rust-inside-other-languages.md | 353 ++++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 src/doc/trpl/rust-inside-other-languages.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index de7ded76280f6..29ec66861f153 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -6,6 +6,7 @@ * [Hello, Cargo!](hello-cargo.md) * [Learn Rust](learn-rust.md) * [Guessing Game](guessing-game.md) + * [Rust inside other languages](rust-inside-other-languages.md) * [Effective Rust](effective-rust.md) * [The Stack and the Heap](the-stack-and-the-heap.md) * [Testing](testing.md) diff --git a/src/doc/trpl/rust-inside-other-languages.md b/src/doc/trpl/rust-inside-other-languages.md new file mode 100644 index 0000000000000..a1ae50a0c5396 --- /dev/null +++ b/src/doc/trpl/rust-inside-other-languages.md @@ -0,0 +1,353 @@ +% Rust Inside Other Languages + +For our third project, we’re going to choose something that shows off one of +Rust’s greatest strengths: a lack of a substantial runtime. + +As organizations grow, they increasingly rely on a multitude of programming +languages. Different programming languages have different strengths and +weaknesses, and a polyglot stack lets you use a particular language where +its strengths make sense, and use a different language where it’s weak. + +A very common area where many programming languages are weak is in runtime +performance of programs. Often, using a language that is slower, but offers +greater programmer productivity is a worthwhile trade-off. To help mitigate +this, they provide a way to write some of your system in C, and then call +the C code as though it were written in the higher-level language. This is +called a ‘foreign function interface’, often shortened to ‘FFI’. + +Rust has support for FFI in both directions: it can call into C code easily, +but crucially, it can also be called _into_ as easily as C. Combined with +Rust’s lack of a garbage collector and low runtime requirements, this makes +Rust a great candidate to embed inside of other languages when you need +some extra oomph. + +There is a whole [chapter devoted to FFI][ffi] and its specifics elsewhere in +the book, but in this chapter, we’ll examine this particular use-case of FFI, +with three examples, in Ruby, Python, and JavaScript. + +[ffi]: ffi.html + +# The problem + +There are many different projects we could choose here, but we’re going to +pick an example where Rust has a clear advantage over many other languages: +numeric computing and threading. + +Many languages, for the sake of consistency, place numbers on the heap, rather +than on the stack. Especially in languages that focus on object-oriented +programming and use garbage collection, heap allocation is the default. Sometimes +optimizations can stack allocate particular numbers, but rather than relying +on an optimizer to do its job, we may want to ensure that we’re always using +primitive number types rather than some sort of object type. + +Second, many languages have a ‘global interpreter lock’, which limits +concurrency in many situations. This is done in the name of safety, which is +a positive effect, but it limits the amount of work that can be done at the +same time, which is a big negative. + +To emphasize these two aspects, we’re going to create a little project that +uses these two aspects heavily. Since the focus of the example is the embedding +of Rust into the languages, rather than the problem itself, we’ll just use a +toy example: + +> Start ten threads. Inside each thread, count from one to five million. After +> All ten threads are finished, print out ‘done!’. + +I chose five million based on my particular computer. Here’s an example of this +code in Ruby: + +```ruby +threads = [] + +10.times do + threads << Thread.new do + count = 0 + + 5_000_000.times do + count += 1 + end + end +end + +threads.each {|t| t.join } +puts "done!" +``` + +Try running this example, and choose a number that runs for a few seconds. +Depending on your computer’s hardware, you may have to increase or decrease the +number. + +On my system, running this program takes `2.156` seconds. And, if I use some +sort of process monitoring tool, like `top`, I can see that it only uses one +core on my machine. That’s the GIL kicking in. + +While it’s true that this is a synthetic program, one can imagine many problems +that are similar to this in the real world. For our purposes, spinning up some +busy threads represents some sort of parallel, expensive computation. + +# A Rust library + +Let’s re-write this problem in Rust. First, let’s make a new project with +Cargo: + +```bash +$ cargo new embed +$ cd embed +``` + +This program is fairly easy to write in Rust: + +```rust +use std::thread; + +fn process() { + let handles: Vec<_> = (0..10).map(|_| { + thread::spawn(|| { + let mut _x = 0; + for _ in (0..5_000_001) { + _x += 1 + } + }) + }).collect(); + + for h in handles { + h.join().ok().expect("Could not join a thread!"); + } +} +``` + +Some of this should look familiar from previous examples. We spin up ten +threads, collecting them into a `handles` vector. Inside of each thread, we +loop five million times, and add one to `_x` each time. Why the underscore? +Well, if we remove it and compile: + +```bash +$ cargo build + Compiling embed v0.1.0 (file:///home/steve/src/embed) +src/lib.rs:3:1: 16:2 warning: function is never used: `process`, #[warn(dead_code)] on by default +src/lib.rs:3 fn process() { +src/lib.rs:4 let handles: Vec<_> = (0..10).map(|_| { +src/lib.rs:5 thread::spawn(|| { +src/lib.rs:6 let mut x = 0; +src/lib.rs:7 for _ in (0..5_000_001) { +src/lib.rs:8 x += 1 + ... +src/lib.rs:6:17: 6:22 warning: variable `x` is assigned to, but never used, #[warn(unused_variables)] on by default +src/lib.rs:6 let mut x = 0; + ^~~~~ +``` + +That first warning is because we are building a library. If we had a test +for this function, the warning would go away. But for now, it’s never +called. + +The second is related to `x` versus `_x`. Because we never actually _do_ +anything with `x`, we get a warning about it. In our case, that’s perfectly +okay, as we’re just trying to waste CPU cycles. Prefixing `x` with the +underscore removes the warning. + +Finally, we join on each thread. + +Right now, however, this is a Rust library, and it doesn’t expose anything +that’s callable from C. If we tried to hook this up to another language right +now, it wouldn’t work. We only need to make two small changes to fix this, +though. The first is modify the beginning of our code: + +```rust,ignore +#[no_mangle] +pub extern fn process() { +``` + +We have to add a new attribute, `no_mangle`. When you create a Rust library, it +changes the name of the function in the compiled output. The reasons for this +are outside the scope of this tutorial, but in order for other languages to +know how to call the function, we need to not do that. This attribute turns +that behavior off. + +The other change is the `pub extern`. The `pub` means that this function should +be callable from outside of this module, and the `extern` says that it should +be able to be called from C. That’s it! Not a whole lot of change. + +The second thing we need to do is to change a setting in our `Cargo.toml`. Add +this at the bottom: + +```toml +[lib] +name = "embed" +crate-type = ["dylib"] +``` + +This tells Rust that we want to compile our library into a standard dynamic +library. By default, Rust compiles into an ‘rlib’, a Rust-specific format. + +Let’s build the project now: + +```bash +$ cargo build --release + Compiling embed v0.1.0 (file:///home/steve/src/embed) +``` + +We’ve chosen `cargo build --release`, which builds with optimizations on. We +want this to be as fast as possible! You can find the output of the library in +`target/release`: + +```bash +$ ls target/release/ +build deps examples libembed.so native +``` + +That `libembed.so` is our ‘shared object’ library. We can use this file +just like any shared object library written in C! As an aside, this may be +`embed.dll` or `libembed.dylib`, depending on the platform. + +Now that we’ve got our Rust library built, let’s use it from our Ruby. + +# Ruby + +Open up a `embed.rb` file inside of our project, and do this: + +```ruby +require 'ffi' + +module Hello + extend FFI::Library + ffi_lib 'target/release/libembed.so' + attach_function :process, [], :void +end + +Hello.process + +puts "done!” +``` + +Before we can run this, we need to install the `ffi` gem: + +```bash +$ gem install ffi # this may need sudo +Fetching: ffi-1.9.8.gem (100%) +Building native extensions. This could take a while... +Successfully installed ffi-1.9.8 +Parsing documentation for ffi-1.9.8 +Installing ri documentation for ffi-1.9.8 +Done installing documentation for ffi after 0 seconds +1 gem installed +``` + +And finally, we can try running it: + +```bash +$ ruby embed.rb +done! +$ +``` + +Whoah, that was fast! On my system, this took `0.086` seconds, rather than +the two seconds the pure Ruby version took. Let’s break down this Ruby +code: + +```ruby +require 'ffi' +``` + +We first need to require the `ffi` gem. This lets us interface with our +Rust library like a C library. + +```ruby +module Hello + extend FFI::Library + ffi_lib 'target/release/libembed.so' +``` + +The `ffi` gem’s authors recommend using a module to scope the functions +we’ll import from the shared library. Inside, we `extend` the necessary +`FFI::Library` module, and then call `ffi_lib` to load up our shared +object library. We just pass it the path that our library is stored, +which as we saw before, is `target/release/libembed.so`. + +```ruby +attach_function :process, [], :void +``` + +The `attach_function` method is provided by the FFI gem. It’s what +connects our `process()` function in Rust to a Ruby function of the +same name. Since `process()` takes no arguments, the second parameter +is an empty array, and since it returns nothing, we pass `:void` as +the final argument. + +```ruby +Hello.process +``` + +This is the actual call into Rust. The combination of our `module` +and the call to `attach_function` sets this all up. It looks like +a Ruby function, but is actually Rust! + +```ruby +puts "done!" +``` + +Finally, as per our project’s requirements, we print out `done!`. + +That’s it! As we’ve seen, bridging between the two languages is really easy, +and buys us a lot of performance. + +Next, let’s try Python! + +# Python + +Create an `embed.py` file in this directory, and put this in it: + +```python +from ctypes import cdll + +lib = cdll.LoadLibrary("target/release/libembed.so") + +lib.process() + +print("done!") +``` + +Even easier! We use `cdll` from the `ctypes` module. A quick call +to `LoadLibrary` later, and we can call `process()`. + +On my system, this takes `0.017` seconds. Speedy! + +# Node.js + +Node isn’t a language, but it’s currently the dominant implementation of +server-side JavaScript. + +In order to do FFI with Node, we first need to install the library: + +```bash +$ npm install ffi +``` + +After that installs, we can use it: + +```javascript +var ffi = require('ffi'); + +var lib = ffi.Library('target/release/libembed', { + 'process': [ 'void', [] ] +}); + +lib.process(); + +console.log("done!"); +``` + +It looks more like the Ruby example than the Python example. We use +the `ffi` module to get access to `ffi.Library()`, which loads up +our shared object. We need to annotate the return type and argument +types of the function, which are 'void' for return, and an empty +array to signify no arguments. From there, we just call it and +print the result. + +On my system, this takes a quick `0.092` seconds. + +# Conclusion + +As you can see, the basics of doing this are _very_ easy. Of course, +there's a lot more that we could do here. Check out the [FFI][ffi] +chapter for more details. From 2ba61698ccb2c43ad1ea081988ef499735bfa62d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 11 May 2015 18:21:49 -0400 Subject: [PATCH 18/23] TRPL: dining philosophers --- src/doc/trpl/SUMMARY.md | 1 + src/doc/trpl/dining-philosophers.md | 690 ++++++++++++++++++++++++++++ 2 files changed, 691 insertions(+) create mode 100644 src/doc/trpl/dining-philosophers.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index de7ded76280f6..4f099d1fd73e2 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -6,6 +6,7 @@ * [Hello, Cargo!](hello-cargo.md) * [Learn Rust](learn-rust.md) * [Guessing Game](guessing-game.md) + * [Dining Philosophers](dining-philosophers.md) * [Effective Rust](effective-rust.md) * [The Stack and the Heap](the-stack-and-the-heap.md) * [Testing](testing.md) diff --git a/src/doc/trpl/dining-philosophers.md b/src/doc/trpl/dining-philosophers.md new file mode 100644 index 0000000000000..b1bea4f819ef4 --- /dev/null +++ b/src/doc/trpl/dining-philosophers.md @@ -0,0 +1,690 @@ +% Dining Philosophers + +For our second project, let’s look at a classic concurrency problem. It’s +called ‘the dining philosophers’. It was originally conceived by Dijkstra in +1965, but we’ll use the version from [this paper][paper] by Tony Hoare in 1985. + +[paper]: http://www.usingcsp.com/cspbook.pdf + +> In ancient times, a wealthy philanthropist endowed a College to accommodate +> five eminent philosophers. Each philosopher had a room in which he could +> engage in his professional activity of thinking; there was also a common +> dining room, furnished with a circular table, surrounded by five chairs, each +> labelled by the name of the philosopher who was to sit in it. They sat +> anticlockwise around the table. To the left of each philosopher there was +> laid a golden fork, and in the centre stood a large bowl of spaghetti, which +> was constantly replenished. A philosopher was expected to spend most of his +> time thinking; but when he felt hungry, he went to the dining room, sat down +> in his own chair, picked up his own fork on his left, and plunged it into the +> spaghetti. But such is the tangled nature of spaghetti that a second fork is +> required to carry it to the mouth. The philosopher therefore had also to pick +> up the fork on his right. When we was finished he would put down both his +> forks, get up from his chair, and continue thinking. Of course, a fork can be +> used by only one philosopher at a time. If the other philosopher wants it, he +> just has to wait until the fork is available again. + +This classic problem shows off a few different elements of concurrency. The +reason is that it's actually slightly tricky to implement: a simple +implementation can deadlock. For example, let's consider a simple algorithm +that would solve this problem: + +1. A philosopher picks up the fork on their left. +2. They then pick up the fork on their right. +3. They eat. +4. They return the forks. + +Now, let’s imagine this sequence of events: + +1. Philosopher 1 begins the algorithm, picking up the fork on their left. +2. Philosopher 2 begins the algorithm, picking up the fork on their left. +3. Philosopher 3 begins the algorithm, picking up the fork on their left. +4. Philosopher 4 begins the algorithm, picking up the fork on their left. +5. Philosopher 5 begins the algorithm, picking up the fork on their left. +6. ... ? All the forks are taken, but nobody can eat! + +There are different ways to solve this problem. We’ll get to our solution in +the tutorial itself. For now, let’s get started modelling the problem itself. +We’ll start with the philosophers: + +```rust +struct Philosopher { + name: String, +} + +impl Philosopher { + fn new(name: &str) -> Philosopher { + Philosopher { + name: name.to_string(), + } + } +} + +fn main() { + let p1 = Philosopher::new("Baruch Spinoza"); + let p2 = Philosopher::new("Gilles Deleuze"); + let p3 = Philosopher::new("Karl Marx"); + let p4 = Philosopher::new("Friedrich Nietzsche"); + let p5 = Philosopher::new("Michel Foucault"); +} +``` + +Here, we make a [`struct`][struct] to represent a philosopher. For now, +a name is all we need. We choose the [`String`][string] type for the name, +rather than `&str`. Generally speaking, working with a type which owns its +data is easier than working with one that uses references. + +Let’s continue: + +```rust +# struct Philosopher { +# name: String, +# } +impl Philosopher { + fn new(name: &str) -> Philosopher { + Philosopher { + name: name.to_string(), + } + } +} +``` + +This `impl` block lets us define things on `Philosopher` structs. In this case, +we define an ‘associated function’ called `new`. The first line looks like this: + +```rust +# struct Philosopher { +# name: String, +# } +# impl Philosopher { +fn new(name: &str) -> Philosopher { +# Philosopher { +# name: name.to_string(), +# } +# } +# } +``` + +We take one argument, a `name`, of type `&str`. This is a reference to another +string. It returns an instance of our `Philosopher` struct. + +```rust +# struct Philosopher { +# name: String, +# } +# impl Philosopher { +# fn new(name: &str) -> Philosopher { +Philosopher { + name: name.to_string(), +} +# } +# } +``` + +This creates a new `Philosopher`, and sets its `name` to our `name` argument. +Not just the argument itself, though, as we call `.to_string()` on it. This +will create a copy of the string that our `&str` points to, and give us a new +`String`, which is the type of the `name` field of `Philosopher`. + +Why not accept a `String` directly? It’s nicer to call. If we took a `String`, +but our caller had a `&str`, they’d have to call this method themselves. The +downside of this flexibility is that we _always_ make a copy. For this small +program, that’s not particularly important, as we know we’ll just be using +short strings anyway. + +One last thing you’ll notice: we just define a `Philosopher`, and seemingly +don’t do anything with it. Rust is an ‘expression based’ language, which means +that almost everything in Rust is an expression which returns a value. This is +true of functions as well, the last expression is automatically returned. Since +we create a new `Philosopher` as the last expression of this function, we end +up returning it. + +This name, `new()`, isn’t anything special to Rust, but it is a convention for +functions that create new instances of structs. Before we talk about why, let’s +look at `main()` again: + +```rust +# struct Philosopher { +# name: String, +# } +# +# impl Philosopher { +# fn new(name: &str) -> Philosopher { +# Philosopher { +# name: name.to_string(), +# } +# } +# } +# +fn main() { + let p1 = Philosopher::new("Baruch Spinoza"); + let p2 = Philosopher::new("Gilles Deleuze"); + let p3 = Philosopher::new("Karl Marx"); + let p4 = Philosopher::new("Friedrich Nietzsche"); + let p5 = Philosopher::new("Michel Foucault"); +} +``` + +Here, we create five variable bindings with five new philosophers. These are my +favorite five, but you can substitute anyone you want. If we _didn’t_ define +that `new()` function, it would look like this: + +```rust +# struct Philosopher { +# name: String, +# } +fn main() { + let p1 = Philosopher { name: "Baruch Spinoza".to_string() }; + let p2 = Philosopher { name: "Gilles Deleuze".to_string() }; + let p3 = Philosopher { name: "Karl Marx".to_string() }; + let p4 = Philosopher { name: "Friedrich Nietzche".to_string() }; + let p5 = Philosopher { name: "Michel Foucault".to_string() }; +} +``` + +That’s much noisier. Using `new` has other advantages too, but even in +this simple case, it ends up being nicer to use. + +Now that we’ve got the basics in place, there’s a number of ways that we can +tackle the broader problem here. I like to start from the end first: let’s +set up a way for each philosopher to finish eating. As a tiny step, let’s make +a method, and then loop through all the philosophers, calling it: + +```rust +struct Philosopher { + name: String, +} + +impl Philosopher { + fn new(name: &str) -> Philosopher { + Philosopher { + name: name.to_string(), + } + } + + fn eat(&self) { + println!("{} is done eating.", self.name); + } +} + +fn main() { + let philosophers = vec![ + Philosopher::new("Baruch Spinoza"), + Philosopher::new("Gilles Deleuze"), + Philosopher::new("Karl Marx"), + Philosopher::new("Friedrich Nietzsche"), + Philosopher::new("Michel Foucault"), + ]; + + for p in &philosophers { + p.eat(); + } +} +``` + +Let’s look at `main()` first. Rather than have five individual variable +bindings for our philosophers, we make a `Vec` of them instead. `Vec` is +also called a ‘vector’, and it’s a growable array type. We then use a +[`for`][for] loop to iterate through the vector, getting a reference to each +philosopher in turn. + +[for]: for-loops.html + +In the body of the loop, we call `p.eat()`, which is defined above: + +```rust,ignore +fn eat(&self) { + println!("{} is done eating.", self.name); +} +``` + +In Rust, methods take an explicit `self` parameter. That’s why `eat()` is a +method, but `new` is an associated function: `new()` has no `self`. For our +first version of `eat()`, we just print out the name of the philosopher, and +mention they’re done eating. Running this program should give you the following +output: + +```text +Baruch Spinoza is done eating. +Gilles Deleuze is done eating. +Karl Marx is done eating. +Friedrich Nietzsche is done eating. +Michel Foucault is done eating. +``` + +Easy enough, they’re all done! We haven’t actually implemented the real problem +yet, though, so we’re not done yet! + +Next, we want to make our philosophers not just finish eating, but actually +eat. Here’s the next version: + +```rust +use std::thread; + +struct Philosopher { + name: String, +} + +impl Philosopher { + fn new(name: &str) -> Philosopher { + Philosopher { + name: name.to_string(), + } + } + + fn eat(&self) { + println!("{} is eating.", self.name); + + thread::sleep_ms(1000); + + println!("{} is done eating.", self.name); + } +} + +fn main() { + let philosophers = vec![ + Philosopher::new("Baruch Spinoza"), + Philosopher::new("Gilles Deleuze"), + Philosopher::new("Karl Marx"), + Philosopher::new("Friedrich Nietzsche"), + Philosopher::new("Michel Foucault"), + ]; + + for p in &philosophers { + p.eat(); + } +} +``` + +Just a few changes. Let’s break it down. + +```rust,ignore +use std::thread; +``` + +`use` brings names into scope. We’re going to start using the `thread` module +from the standard library, and so we need to `use` it. + +```rust,ignore + fn eat(&self) { + println!("{} is eating.", self.name); + + thread::sleep_ms(1000); + + println!("{} is done eating.", self.name); + } +``` + +We now print out two messages, with a `sleep_ms()` in the middle. This will +simulate the time it takes a philosopher to eat. + +If you run this program, You should see each philosopher eat in turn: + +```text +Baruch Spinoza is eating. +Baruch Spinoza is done eating. +Gilles Deleuze is eating. +Gilles Deleuze is done eating. +Karl Marx is eating. +Karl Marx is done eating. +Friedrich Nietzsche is eating. +Friedrich Nietzsche is done eating. +Michel Foucault is eating. +Michel Foucault is done eating. +``` + +Excellent! We’re getting there. There’s just one problem: we aren’t actually +operating in a concurrent fashion, which is a core part of the problem! + +To make our philosophers eat concurrently, we need to make a small change. +Here’s the next iteration: + +```rust +use std::thread; + +struct Philosopher { + name: String, +} + +impl Philosopher { + fn new(name: &str) -> Philosopher { + Philosopher { + name: name.to_string(), + } + } + + fn eat(&self) { + println!("{} is eating.", self.name); + + thread::sleep_ms(1000); + + println!("{} is done eating.", self.name); + } +} + +fn main() { + let philosophers = vec![ + Philosopher::new("Baruch Spinoza"), + Philosopher::new("Gilles Deleuze"), + Philosopher::new("Karl Marx"), + Philosopher::new("Friedrich Nietzsche"), + Philosopher::new("Michel Foucault"), + ]; + + let handles: Vec<_> = philosophers.into_iter().map(|p| { + thread::spawn(move || { + p.eat(); + }) + }).collect(); + + for h in handles { + h.join().unwrap(); + } +} +``` + +All we’ve done is change the loop in `main()`, and added a second one! Here’s the +first change: + +```rust,ignore +let handles: Vec<_> = philosophers.into_iter().map(|p| { + thread::spawn(move || { + p.eat(); + }) +}).collect(); +``` + +While this is only five lines, they’re a dense four. Let’s break it down. + +```rust,ignore +let handles: Vec<_> = +``` + +We introduce a new binding, called `handles`. We’ve given it this name because +we are going to make some new threads, and that will return some handles to those +threads that let us control their operation. We need to explicitly annotate +the type here, though, due to an issue we’ll talk about later. The `_` is +a type placeholder. We’re saying “`handles` is a vector of something, but you +can figure out what that something is, Rust.” + +```rust,ignore +philosophers.into_iter().map(|p| { +``` + +We take our list of philosophers and call `into_iter()` on it. This creates an +iterator that takes ownership of each philosopher. We need to do this to pass +them to our threads. We take that iterator and call `map` on it, which takes a +closure as an argument and calls that closure on each element in turn. + +```rust,ignore + thread::spawn(move || { + p.eat(); + }) +``` + +Here’s where the concurrency happens. The `thread::spawn` function takes a closure +as an argument and executes that closure in a new thread. This closure needs +an extra annotation, `move`, to indicate that the closure is going to take +ownership of the values it’s capturing. Primarily, the `p` variable of the +`map` function. + +Inside the thread, all we do is call `eat()` on `p`. + +```rust,ignore +}).collect(); +``` + +Finally, we take the result of all those `map` calls and collect them up. +`collect()` will make them into a collection of some kind, which is why we +needed to annotate the return type: we want a `Vec`. The elements are the +return values of the `thread::spawn` calls, which are handles to those threads. +Whew! + +```rust,ignore +for h in handles { + h.join().unwrap(); +} +``` + +At the end of `main()`, we loop through the handles and call `join()` on them, +which blocks execution until the thread has completed execution. This ensures +that the threads complete their work before the program exits. + +If you run this program, you’ll see that the philosophers eat out of order! +We have mult-threading! + +```text +Gilles Deleuze is eating. +Gilles Deleuze is done eating. +Friedrich Nietzsche is eating. +Friedrich Nietzsche is done eating. +Michel Foucault is eating. +Baruch Spinoza is eating. +Baruch Spinoza is done eating. +Karl Marx is eating. +Karl Marx is done eating. +Michel Foucault is done eating. +``` + +But what about the forks? We haven’t modeled them at all yet. + +To do that, let’s make a new `struct`: + +```rust +use std::sync::Mutex; + +struct Table { + forks: Vec>, +} +``` + +This `Table` has an vector of `Mutex`es. A mutex is a way to control +concurrency: only one thread can access the contents at once. This is exactly +the property we need with our forks. We use an empty tuple, `()`, inside the +mutex, since we’re not actually going to use the value, just hold onto it. + +Let’s modify the program to use the `Table`: + +```rust +use std::thread; +use std::sync::{Mutex, Arc}; + +struct Philosopher { + name: String, + left: usize, + right: usize, +} + +impl Philosopher { + fn new(name: &str, left: usize, right: usize) -> Philosopher { + Philosopher { + name: name.to_string(), + left: left, + right: right, + } + } + + fn eat(&self, table: &Table) { + let _left = table.forks[self.left].lock().unwrap(); + let _right = table.forks[self.right].lock().unwrap(); + + println!("{} is eating.", self.name); + + thread::sleep_ms(1000); + + println!("{} is done eating.", self.name); + } +} + +struct Table { + forks: Vec>, +} + +fn main() { + let table = Arc::new(Table { forks: vec![ + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + ]}); + + let philosophers = vec![ + Philosopher::new("Baruch Spinoza", 0, 1), + Philosopher::new("Gilles Deleuze", 1, 2), + Philosopher::new("Karl Marx", 2, 3), + Philosopher::new("Friedrich Nietzsche", 3, 4), + Philosopher::new("Michel Foucault", 0, 4), + ]; + + let handles: Vec<_> = philosophers.into_iter().map(|p| { + let table = table.clone(); + + thread::spawn(move || { + p.eat(&table); + }) + }).collect(); + + for h in handles { + h.join().unwrap(); + } +} +``` + +Lots of changes! However, with this iteration, we’ve got a working program. +Let’s go over the details: + +```rust,ignore +use std::sync::{Mutex, Arc}; +``` + +We’re going to use another structure from the `std::sync` package: `Arc`. +We’ll talk more about it when we use it. + +```rust,ignore +struct Philosopher { + name: String, + left: usize, + right: usize, +} +``` + +We need to add two more fields to our `Philosopher`. Each philosopher is going +to have two forks: the one on their left, and the one on their right. +We’ll use the `usize` type to indicate them, as it’s the type that you index +vectors with. These two values will be the indexes into the `forks` our `Table` +has. + +```rust,ignore +fn new(name: &str, left: usize, right: usize) -> Philosopher { + Philosopher { + name: name.to_string(), + left: left, + right: right, + } +} +``` + +We now need to construct those `left` and `right` values, so we add them to +`new()`. + +```rust,ignore +fn eat(&self, table: &Table) { + let _left = table.forks[self.left].lock().unwrap(); + let _right = table.forks[self.right].lock().unwrap(); + + println!("{} is eating.", self.name); + + thread::sleep_ms(1000); + + println!("{} is done eating.", self.name); +} +``` + +We have two new lines. We’ve also added an argument, `table`. We access the +`Table`’s list of forks, and then use `self.left` and `self.right` to access +the fork at that particular index. That gives us access to the `Mutex` at that +index, and we call `lock()` on it. If the mutex is currently being accessed by +someone else, we’ll block until it becomes available. + +The call to `lock()` might fail, and if it does, we want to crash. In this +case, the error that could happen is that the mutex is [‘poisoned’][poison], +which is what happens when the thread panics while the lock is held. Since this +shouldn’t happen, we just use `unwrap()`. + +[poison]: ../std/sync/struct.Mutex.html#poisoning + +One other odd thing about these lines: we’ve named the results `_left` and +`_right`. What’s up with that underscore? Well, we aren’t planning on +_using_ the value inside the lock. We just want to acquire it. As such, +Rust will warn us that we never use the value. By using the underscore, +we tell Rust that this is what we intended, and it won’t throw a warning. + +What about releasing the lock? Well, that will happen when `_left` and +`_right` go out of scope, automatically. + +```rust,ignore + let table = Arc::new(Table { forks: vec![ + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + Mutex::new(()), + ]}); +``` + +Next, in `main()`, we make a new `Table` and wrap it in an `Arc`. +‘arc’ stands for ‘atomic reference count’, and we need that to share +our `Table` across multiple threads. As we share it, the reference +count will go up, and when each thread ends, it will go back down. + + +```rust,ignore +let philosophers = vec![ + Philosopher::new("Baruch Spinoza", 0, 1), + Philosopher::new("Gilles Deleuze", 1, 2), + Philosopher::new("Karl Marx", 2, 3), + Philosopher::new("Friedrich Nietzsche", 3, 4), + Philosopher::new("Michel Foucault", 0, 4), +]; +``` + +We need to pass in our `left` and `right` values to the constructors for our +`Philosopher`s. But there’s one more detail here, and it’s _very_ important. If +you look at the pattern, it’s all consistent until the very end. Monsieur +Foucault should have `4, 0` as arguments, but instead, has `0, 4`. This is what +prevents deadlock, actually: one of our philosophers is left handed! This is +one way to solve the problem, and in my opinion, it’s the simplest. + +```rust,ignore +let handles: Vec<_> = philosophers.into_iter().map(|p| { + let table = table.clone(); + + thread::spawn(move || { + p.eat(&table); + }) +}).collect(); +``` + +Finally, inside of our `map()`/`collect()` loop, we call `table.clone()`. The +`clone()` method on `Arc` is what bumps up the reference count, and when it +goes out of scope, it decrements the count. You’ll notice we can introduce a +new binding to `table` here, and it will shadow the old one. This is often used +so that you don’t need to come up with two unique names. + +With this, our program works! Only two philosophers can eat at any one time, +and so you’ll get some output like this: + +```text +Gilles Deleuze is eating. +Friedrich Nietzsche is eating. +Friedrich Nietzsche is done eating. +Gilles Deleuze is done eating. +Baruch Spinoza is eating. +Karl Marx is eating. +Baruch Spinoza is done eating. +Michel Foucault is eating. +Karl Marx is done eating. +Michel Foucault is done eating. +``` + +Congrats! You’ve implemented a classic concurrency problem in Rust. From 457aed7ca003d7cbef2cda8360ad9b8b5bb83650 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 12 May 2015 14:13:03 -0400 Subject: [PATCH 19/23] trpl: move tuple-structs.md into structs.md --- src/doc/trpl/SUMMARY.md | 1 - src/doc/trpl/structs.md | 60 +++++++++++++++++++++++++++++++++++ src/doc/trpl/tuple-structs.md | 60 ----------------------------------- 3 files changed, 60 insertions(+), 61 deletions(-) delete mode 100644 src/doc/trpl/tuple-structs.md diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index de7ded76280f6..2ab71189ff8fc 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -43,7 +43,6 @@ * [Universal Function Call Syntax](ufcs.md) * [Crates and Modules](crates-and-modules.md) * [`const` and `static`](const-and-static.md) - * [Tuple Structs](tuple-structs.md) * [Attributes](attributes.md) * [`type` aliases](type-aliases.md) * [Casting between types](casting-between-types.md) diff --git a/src/doc/trpl/structs.md b/src/doc/trpl/structs.md index fcf928e427c25..2f8d2bfdea0a1 100644 --- a/src/doc/trpl/structs.md +++ b/src/doc/trpl/structs.md @@ -117,3 +117,63 @@ ones, and it will copy the values you don’t specify: let origin = Point3d { x: 0, y: 0, z: 0 }; let point = Point3d { z: 1, x: 2, .. origin }; ``` + +# Tuple structs + +Rust has another data type that’s like a hybrid between a [tuple][tuple] and a +struct, called a ‘tuple struct’. Tuple structs have a name, but +their fields don’t: + +```rust +struct Color(i32, i32, i32); +struct Point(i32, i32, i32); +``` + +[tuple]: primitive-types.html#tuples + +These two will not be equal, even if they have the same values: + +```rust +# struct Color(i32, i32, i32); +# struct Point(i32, i32, i32); +let black = Color(0, 0, 0); +let origin = Point(0, 0, 0); +``` + +It is almost always better to use a struct than a tuple struct. We would write +`Color` and `Point` like this instead: + +```rust +struct Color { + red: i32, + blue: i32, + green: i32, +} + +struct Point { + x: i32, + y: i32, + z: i32, +} +``` + +Now, we have actual names, rather than positions. Good names are important, +and with a struct, we have actual names. + +There _is_ one case when a tuple struct is very useful, though, and that’s a +tuple struct with only one element. We call this the ‘newtype’ pattern, because +it allows you to create a new type, distinct from that of its contained value +and expressing its own semantic meaning: + +```rust +struct Inches(i32); + +let length = Inches(10); + +let Inches(integer_length) = length; +println!("length is {} inches", integer_length); +``` + +As you can see here, you can extract the inner integer type through a +destructuring `let`, just as with regular tuples. In this case, the +`let Inches(integer_length)` assigns `10` to `integer_length`. diff --git a/src/doc/trpl/tuple-structs.md b/src/doc/trpl/tuple-structs.md deleted file mode 100644 index bdaef70711ae4..0000000000000 --- a/src/doc/trpl/tuple-structs.md +++ /dev/null @@ -1,60 +0,0 @@ -% Tuple Structs - -Rust has another data type that's like a hybrid between a [tuple][tuple] and a -[struct][struct], called a ‘tuple struct’. Tuple structs have a name, but -their fields don’t: - -```rust -struct Color(i32, i32, i32); -struct Point(i32, i32, i32); -``` - -[tuple]: primitive-types.html#tuples -[struct]: structs.html - -These two will not be equal, even if they have the same values: - -```rust -# struct Color(i32, i32, i32); -# struct Point(i32, i32, i32); -let black = Color(0, 0, 0); -let origin = Point(0, 0, 0); -``` - -It is almost always better to use a struct than a tuple struct. We would write -`Color` and `Point` like this instead: - -```rust -struct Color { - red: i32, - blue: i32, - green: i32, -} - -struct Point { - x: i32, - y: i32, - z: i32, -} -``` - -Now, we have actual names, rather than positions. Good names are important, -and with a struct, we have actual names. - -There _is_ one case when a tuple struct is very useful, though, and that’s a -tuple struct with only one element. We call this the ‘newtype’ pattern, because -it allows you to create a new type, distinct from that of its contained value -and expressing its own semantic meaning: - -```rust -struct Inches(i32); - -let length = Inches(10); - -let Inches(integer_length) = length; -println!("length is {} inches", integer_length); -``` - -As you can see here, you can extract the inner integer type through a -destructuring `let`, as we discussed previously in ‘tuples’. In this case, the -`let Inches(integer_length)` assigns `10` to `integer_length`. From 797d8e28695a9f349a3155958e06c527250bdac9 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 12 May 2015 16:13:34 -0400 Subject: [PATCH 20/23] trpl/structs: Document unit-like structs --- src/doc/trpl/structs.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/doc/trpl/structs.md b/src/doc/trpl/structs.md index 2f8d2bfdea0a1..ad7ead9319989 100644 --- a/src/doc/trpl/structs.md +++ b/src/doc/trpl/structs.md @@ -177,3 +177,22 @@ println!("length is {} inches", integer_length); As you can see here, you can extract the inner integer type through a destructuring `let`, just as with regular tuples. In this case, the `let Inches(integer_length)` assigns `10` to `integer_length`. + +# Unit-like structs + +You can define a struct with no members at all: + +```rust +struct Electron; +``` + +Such a struct is called ‘unit-like’ because it resembles the empty +tuple, `()`, sometimes called ‘unit’. Like a tuple struct, it defines a +new type. + +This is rarely useful on its own (although sometimes it can serve as a +marker type), but in combination with other features, it can become +useful. For instance, a library may ask you to create a structure that +implements a certain [trait][trait] to handle events. If you don’t have +any data you need to store in the structure, you can just create a +unit-like struct. From 8d50216e9dfb055d28abf9da35f91542c2e9fe90 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 12 May 2015 15:34:52 -0400 Subject: [PATCH 21/23] trpl/enums: Rewrite --- src/doc/trpl/SUMMARY.md | 2 +- src/doc/trpl/enums.md | 95 +++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index 2ab71189ff8fc..b8b249fafe1c9 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -27,9 +27,9 @@ * [References and Borrowing](references-and-borrowing.md) * [Lifetimes](lifetimes.md) * [Mutability](mutability.md) + * [Structs](structs.md) * [Enums](enums.md) * [Match](match.md) - * [Structs](structs.md) * [Patterns](patterns.md) * [Method Syntax](method-syntax.md) * [Vectors](vectors.md) diff --git a/src/doc/trpl/enums.md b/src/doc/trpl/enums.md index 80ea25eb35ce9..443f569a3e5ab 100644 --- a/src/doc/trpl/enums.md +++ b/src/doc/trpl/enums.md @@ -1,68 +1,63 @@ % Enums -Rust has a ‘sum type’, an `enum`. Enums are an incredibly useful feature of -Rust, and are used throughout the standard library. An `enum` is a type which -relates a set of alternates to a specific name. For example, below we define -`Character` to be either a `Digit` or something else. +An `enum` in Rust is a type that represents data that could be one of +several possible variants: ```rust -enum Character { - Digit(i32), - Other, +enum Message { + Quit, + ChangeColor(i32, i32, i32), + Move { x: i32, y: i32 }, + Write(String), } ``` -Most types are allowed as the variant components of an `enum`. Here are some -examples: +Each variant can optionally have data associated with it. The syntax for +defining variants resembles the syntaxes used to define structs: you can +have variants with no data (like unit-like structs), variants with named +data, and variants with unnamed data (like tuple structs). Unlike +separate struct definitions, however, an `enum` is a single type. A +value of the enum can match any of the variants. For this reason, an +enum is sometimes called a ‘sum type’: the set of possible values of the +enum is the sum of the sets of possible values for each variant. -```rust -struct Empty; -struct Color(i32, i32, i32); -struct Length(i32); -struct Stats { Health: i32, Mana: i32, Attack: i32, Defense: i32 } -struct HeightDatabase(Vec); -``` - -You see that, depending on its type, an `enum` variant may or may not hold data. -In `Character`, for instance, `Digit` gives a meaningful name for an `i32` -value, where `Other` is only a name. However, the fact that they represent -distinct categories of `Character` is a very useful property. - -The variants of an `enum` by default are not comparable with equality operators -(`==`, `!=`), have no ordering (`<`, `>=`, etc.), and do not support other -binary operations such as `*` and `+`. As such, the following code is invalid -for the example `Character` type: - -```rust,ignore -// These assignments both succeed -let ten = Character::Digit(10); -let four = Character::Digit(4); - -// Error: `*` is not implemented for type `Character` -let forty = ten * four; +We use the `::` syntax to use the name of each variant: they’re scoped by the name +of the `enum` itself. This allows both of these to work: -// Error: `<=` is not implemented for type `Character` -let four_is_smaller = four <= ten; +```rust +# enum Message { +# Move { x: i32, y: i32 }, +# } +let x: Message = Message::Move { x: 3, y: 4 }; + +enum BoardGameTurn { + Move { squares: i32 }, + Pass, +} -// Error: `==` is not implemented for type `Character` -let four_equals_ten = four == ten; +let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 }; ``` -We use the `::` syntax to use the name of each variant: They’re scoped by the name -of the `enum` itself. This allows both of these to work: +Both variants are named `Move`, but since they’re scoped to the name of +the enum, they can both be used without conflict. + +A value of an enum type contains information about which variant it is, +in addition to any data associated with that variant. This is sometimes +referred to as a ‘tagged union’, since the data includes a ‘tag’ +indicating what type it is. The compiler uses this information to +enforce that you’re accessing the data in the enum safely. For instance, +you can’t simply try to destructure a value as if it were one of the +possible variants: ```rust,ignore -Character::Digit(10); -Hand::Digit; +fn process_color_change(msg: Message) { + let Message::ChangeColor(r, g, b) = msg; // compile-time error +} ``` -Both variants are named `Digit`, but since they’re scoped to the `enum` name, - -Not supporting these operations may seem rather limiting, but it’s a limitation -which we can overcome. There are two ways: by implementing equality ourselves, -or by pattern matching variants with [`match`][match] expressions, which you’ll -learn in the next section. We don’t know enough about Rust to implement -equality yet, but we’ll find out in the [`traits`][traits] section. +We’ll see how to safely get data out of enums when we learn about the +[`match`][match] and [`if let`][if-let] statements in the next few +chapters. [match]: match.html -[traits]: traits.html +[if-let]: if-let.html From f59f41e04c044f322285f80d17916bd207d8ed04 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 12 May 2015 20:14:26 -0400 Subject: [PATCH 22/23] trpl/match: Add an example for matching on enums --- src/doc/trpl/match.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/doc/trpl/match.md b/src/doc/trpl/match.md index 86b9445338966..2bb2359ba5a01 100644 --- a/src/doc/trpl/match.md +++ b/src/doc/trpl/match.md @@ -61,3 +61,40 @@ let number = match x { ``` Sometimes it’s a nice way of converting something from one type to another. + +# Matching on enums + +Another important use of the `match` keyword is to process the possible +variants of an enum: + +```rust +enum Message { + Quit, + ChangeColor(i32, i32, i32), + Move { x: i32, y: i32 }, + Write(String), +} + +fn quit() { /* ... */ } +fn change_color(r: i32, g: i32, b: i32) { /* ... */ } +fn move_cursor(x: i32, y: i32) { /* ... */ } + +fn process_message(msg: Message) { + match msg { + Message::Quit => quit(), + Message::ChangeColor(r, g, b) => change_color(r, g, b), + Message::Move { x: x, y: y } => move_cursor(x, y), + Message::Write(s) => println!("{}", s), + }; +} +``` + +Again, the Rust compiler checks exhaustiveness, so it demands that you +have a match arm for every variant of the enum. If you leave one off, it +will give you a compile-time error unless you use `_`. + +Unlike the previous uses of `match`, you can’t use the normal `if` +statement to do this. You can use the [`if let`][if-let] statement, +which can be seen as an abbreviated form of `match`. + +[if-let][if-let.html] From 1d34f923a81f2d5c5ff89186c0fa565c7e9e7e2f Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Tue, 12 May 2015 22:02:42 -0500 Subject: [PATCH 23/23] Update error messages for tests in compile-fail for E0066 and E0069. --- src/test/compile-fail/issue-14084.rs | 2 +- src/test/compile-fail/ret-non-nil.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/issue-14084.rs b/src/test/compile-fail/issue-14084.rs index 92e0dd3ad0e52..003c6644f7f02 100644 --- a/src/test/compile-fail/issue-14084.rs +++ b/src/test/compile-fail/issue-14084.rs @@ -12,5 +12,5 @@ fn main() { box ( () ) 0; - //~^ ERROR: only the managed heap and exchange heap are currently supported + //~^ ERROR: only the exchange heap is currently supported } diff --git a/src/test/compile-fail/ret-non-nil.rs b/src/test/compile-fail/ret-non-nil.rs index 4ee3cf4abac4e..6be98fbd82773 100644 --- a/src/test/compile-fail/ret-non-nil.rs +++ b/src/test/compile-fail/ret-non-nil.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: `return;` in function returning non-nil +// error-pattern: `return;` in a function whose return type is not `()` fn f() { return; }