diff --git a/src/doc/trpl/README.md b/src/doc/trpl/README.md index 6e8d394afa5b1..b2e1a6ec0bc19 100644 --- a/src/doc/trpl/README.md +++ b/src/doc/trpl/README.md @@ -1,39 +1,192 @@ % The Rust Programming Language -Welcome! This book will teach you about [the Rust Programming -Language](http://www.rust-lang.org/). Rust is a modern systems programming -language focusing on safety and speed. It accomplishes these goals by being -memory safe without using garbage collection. +Welcome! This book will teach you about the [Rust Programming Language][rust]. +Rust is a systems programming language focused on three goals: safety, speed, +and concurrency. It maintains these goals without having a garbage collector, +making it a useful language for a number of use cases other languages aren’t +good at: embedding in other languages, programs with specific space and time +requirements, and writing low-level code, like device drivers and operating +systems. It improves on current languages targeting this space by having a +number of compile-time safety checks that produce no runtime overhead, while +eliminating all data races. Rust also aims to achieve ‘zero-cost abstrations’ +even though some of these abstractions feel like those of a high-level +language. Even then, Rust still allows precise control like a low-level +language would. -"The Rust Programming Language" is split into three sections, which you can -navigate through the menu on the left. +[rust]: http://rust-lang.org -

Basics

+“The Rust Programming Language” is split into seven sections. This introduction +is the first. After this: -This section is a linear introduction to the basic syntax and semantics of -Rust. It has individual sections on each part of Rust's syntax. +* [Getting started][gs] - Set up your computer for Rust development. +* [Learn Rust][lr] - Learn Rust programming through small projects. +* [Effective Rust][er] - Higher-level concepts for writing excellent Rust code. +* [Syntax and Semantics][ss] - Each bit of Rust, broken down into small chunks. +* [Nightly Rust][nr] - Cutting-edge features that aren’t in stable builds yet. +* [Glossary][gl] - A reference of terms used in the book. -After reading "Basics," you will have a good foundation to learn more about -Rust, and can write very simple programs. +[gs]: getting-started.html +[lr]: learn-rust.html +[er]: effective-rust.html +[ss]: syntax-and-semantics.html +[nr]: nightly-rust.html +[gl]: glossary.html -

Intermediate

+After reading this introduction, you’ll want to dive into either ‘Learn Rust’ +or ‘Syntax and Semantics’, depending on your preference: ‘Learn Rust’ if you +want to dive in with a project, or ‘Syntax and Semantics’ if you prefer to +start small, and learn a single concept thoroughly before moving onto the next. +Copious cross-linking connects these parts together. -This section contains individual chapters, which are self-contained. They focus -on specific topics, and can be read in any order. +## A brief introduction to Rust -After reading "Intermediate," you will have a solid understanding of Rust, -and will be able to understand most Rust code and write more complex programs. +Is Rust a language you might be interested in? Let’s examine a few small code +samples to show off a few of its strengths. -

Advanced

+The main concept that makes Rust unique is called ‘ownership’. Consider this +small example: -In a similar fashion to "Intermediate," this section is full of individual, -deep-dive chapters, which stand alone and can be read in any order. These -chapters focus on Rust's most complex features. +```rust +fn main() { + let mut x = vec!["Hello", "world"]; +} +``` -

Unstable

+This program makes a [variable binding][var] named `x`. The value of this +binding is a `Vec`, a ‘vector’, that we create through a [macro][macro] +defined in the standard library. This macro is called `vec`, and we invoke +macros with a `!`. This follows a general principle of Rust: make things +explicit. Macros can do significantly more complicated things than function +calls, and so they’re visually distinct. The `!` also helps with parsing, +making tooling easier to write, which is also important. -In a similar fashion to "Intermediate," this section is full of individual, -deep-dive chapters, which stand alone and can be read in any order. +We used `mut` to make `x` mutable: bindings are immutable by default in Rust. +We’ll be mutating this vector later in the example. -This chapter contains things that are only available on the nightly channel of -Rust. +It’s also worth noting that we didn’t need a type annotation here: while Rust +is statically typed, we didn’t need to explicitly annotate the type. Rust has +type inference to balance out the power of static typing with the verbosity of +annotating types. + +Rust prefers stack allocation to heap allocation: `x` is placed directly on the +stack. However, the `Vec` type allocates space for the elements of the +vector on the heap. If you’re not familiar with this distinction, you can +ignore it for now, or check out [‘The Stack and the Heap’][heap]. As a systems +programming language, Rust gives you the ability to control how your memory is +allocated, but when we’re getting started, it’s less of a big deal. + +[var]: variable-bindings.html +[macro]: macros.html +[heap]: the-stack-and-the-heap.html + +Earlier, we mentioned that ‘ownership’ is the key new concept in Rust. In Rust +parlance, `x` is said to ‘own’ the vector. This means that when `x` goes out of +scope, the vector’s memory will be de-allocated. This is done deterministically +by the Rust compiler, rather than through a mechanism such as a garbage +collector. In other words, in Rust, you don’t call functions like `malloc` and +`free` yourself: the compiler statically determines when you need to allocate +or deallocate memory, and inserts those calls itself. To err is to be human, +but compilers never forget. + +Let’s add another line to our example: + +```rust +fn main() { + let mut x = vec!["Hello", "world"]; + + let y = &x[0]; +} +``` + +We’ve introduced another binding, `y`. In this case, `y` is a ‘reference’ to +the first element of the vector. Rust’s references are similar to pointers in +other languages, but with additional compile-time safety checks. References +interact with the ownership system by [‘borrowing’][borrowing] what they point +to, rather than owning it. The difference is, when the reference goes out of +scope, it will not deallocate the underlying memory. If it did, we’d +de-allocate twice, which is bad! + +[borrowing]: references-and-borrowing.html + +Let’s add a third line. It looks innocent enough, but causes a compiler error: + +```rust,ignore +fn main() { + let mut x = vec!["Hello", "world"]; + + let y = &x[0]; + + x.push(4); +} +``` + +`push` is a method on vectors that appends another element to the end of the +vector. When we try to compile this program, we get an error: + +```text +error: cannot borrow `x` as mutable because it is also borrowed as immutable + x.push(4); + ^ +note: previous borrow of `x` occurs here; the immutable borrow prevents +subsequent moves or mutable borrows of `x` until the borrow ends + let y = &x[0]; + ^ +note: previous borrow ends here +fn main() { + +} +^ +``` + +Whew! The Rust compiler gives quite detailed errors at times, and this is one +of those times. As the error explains, while we made our binding mutable, we +still cannot call `push`. This is because we already have a reference to an +element of the vector, `y`. Mutating something while another reference exists +is dangerous, because we may invalidate the reference. In this specific case, +when we create the vector, we may have only allocated space for three elements. +Adding a fourth would mean allocating a new chunk of memory for all those elements, +copying the old values over, and updating the internal pointer to that memory. +That all works just fine. The problem is that `y` wouldn’t get updated, and so +we’d have a ‘dangling pointer’. That’s bad. Any use of `y` would be an error in +this case, and so the compiler has caught this for us. + +So how do we solve this problem? There are two approaches we can take. The first +is making a copy rather than using a reference: + +```rust +fn main() { + let mut x = vec!["Hello", "world"]; + + let y = x[0].clone(); + + x.push(4); +} +``` + +Rust has [move semantics][move] by default, so if we want to make a copy of some +data, we call the `clone()` method. In this example, `y` is no longer a reference +to the vector stored in `x`, but a copy of its first element, `"hello"`. Now +that we don’t have a reference, our `push()` works just fine. + +[move]: move-semantics.html + +If we truly want a reference, we need the other option: ensure that our reference +goes out of scope before we try to do the mutation. That looks like this: + +```rust +fn main() { + let mut x = vec!["Hello", "world"]; + + { + let y = &x[0]; + } + + x.push(4); +} +``` + +We created an inner scope with an additional set of curly braces. `y` will go out of +scope before we call `push()`, and so we’re all good. + +This concept of ownership isn’t just good for preventing danging pointers, but an +entire set of related problems, like iterator invalidation, concurrency, and more. diff --git a/src/doc/trpl/SUMMARY.md b/src/doc/trpl/SUMMARY.md index d894e1c47253b..029facdec762b 100644 --- a/src/doc/trpl/SUMMARY.md +++ b/src/doc/trpl/SUMMARY.md @@ -17,8 +17,8 @@ * [`Deref` coercions](deref-coercions.md) * [Syntax and Semantics](syntax-and-semantics.md) * [Variable Bindings](variable-bindings.md) - * [Primitive Types](primitive-types.md) * [Functions](functions.md) + * [Primitive Types](primitive-types.md) * [Comments](comments.md) * [Structs](structs.md) * [Mutability](mutability.md) @@ -35,8 +35,6 @@ * [Move semantics](move-semantics.md) * [Drop](drop.md) * [Vectors](vectors.md) - * [Arrays](arrays.md) - * [Slices](slices.md) * [Strings](strings.md) * [Traits](traits.md) * [Operators and Overloading](operators-and-overloading.md) @@ -47,7 +45,6 @@ * [Crates and Modules](crates-and-modules.md) * [`static`](static.md) * [`const`](const.md) - * [Tuples](tuples.md) * [Tuple Structs](tuple-structs.md) * [Attributes](attributes.md) * [Conditional Compilation](conditional-compilation.md) @@ -67,3 +64,4 @@ * [Benchmark Tests](benchmark-tests.md) * [Box Syntax and Patterns](box-syntax-and-patterns.md) * [Glossary](glossary.md) +* [Academic Research](academic-research.md) diff --git a/src/doc/trpl/academic-research.md b/src/doc/trpl/academic-research.md new file mode 100644 index 0000000000000..f4f066fb3dfe3 --- /dev/null +++ b/src/doc/trpl/academic-research.md @@ -0,0 +1,46 @@ +% Academic Research + +An incomplete list of papers that have had some influence in Rust. + +Recommended for inspiration and a better understanding of Rust's background. + +### Type system + +* [Region based memory management in Cyclone](http://209.68.42.137/ucsd-pages/Courses/cse227.w03/handouts/cyclone-regions.pdf) +* [Safe manual memory management in Cyclone](http://www.cs.umd.edu/projects/PL/cyclone/scp.pdf) +* [Typeclasses: making ad-hoc polymorphism less ad hoc](http://www.ps.uni-sb.de/courses/typen-ws99/class.ps.gz) +* [Macros that work together](https://www.cs.utah.edu/plt/publications/jfp12-draft-fcdf.pdf) +* [Traits: composable units of behavior](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf) +* [Alias burying](http://www.cs.uwm.edu/faculty/boyland/papers/unique-preprint.ps) - We tried something similar and abandoned it. +* [External uniqueness is unique enough](http://www.computingscience.nl/research/techreps/repo/CS-2002/2002-048.pdf) +* [Uniqueness and Reference Immutability for Safe Parallelism](https://research.microsoft.com/pubs/170528/msr-tr-2012-79.pdf) +* [Region Based Memory Management](http://www.cs.ucla.edu/~palsberg/tba/papers/tofte-talpin-iandc97.pdf) + +### Concurrency + +* [Singularity: rethinking the software stack](https://research.microsoft.com/pubs/69431/osr2007_rethinkingsoftwarestack.pdf) +* [Language support for fast and reliable message passing in singularity OS](https://research.microsoft.com/pubs/67482/singsharp.pdf) +* [Scheduling multithreaded computations by work stealing](http://supertech.csail.mit.edu/papers/steal.pdf) +* [Thread scheduling for multiprogramming multiprocessors](http://www.eecis.udel.edu/%7Ecavazos/cisc879-spring2008/papers/arora98thread.pdf) +* [The data locality of work stealing](http://www.aladdin.cs.cmu.edu/papers/pdfs/y2000/locality_spaa00.pdf) +* [Dynamic circular work stealing deque](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf) - The Chase/Lev deque +* [Work-first and help-first scheduling policies for async-finish task parallelism](http://www.cs.rice.edu/%7Eyguo/pubs/PID824943.pdf) - More general than fully-strict work stealing +* [A Java fork/join calamity](http://www.coopsoft.com/ar/CalamityArticle.html) - critique of Java's fork/join library, particularly its application of work stealing to non-strict computation +* [Scheduling techniques for concurrent systems](http://www.ece.rutgers.edu/%7Eparashar/Classes/ece572-papers/05/ps-ousterhout.pdf) +* [Contention aware scheduling](http://www.blagodurov.net/files/a8-blagodurov.pdf) +* [Balanced work stealing for time-sharing multicores](http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-12-1.pdf) +* [Three layer cake](http://www.upcrc.illinois.edu/workshops/paraplop10/papers/paraplop10_submission_8.pdf) +* [Non-blocking steal-half work queues](http://www.cs.bgu.ac.il/%7Ehendlerd/papers/p280-hendler.pdf) +* [Reagents: expressing and composing fine-grained concurrency](http://www.mpi-sws.org/~turon/reagents.pdf) +* [Algorithms for scalable synchronization of shared-memory multiprocessors](https://www.cs.rochester.edu/u/scott/papers/1991_TOCS_synch.pdf) + +### Others + +* [Crash-only software](https://www.usenix.org/legacy/events/hotos03/tech/full_papers/candea/candea.pdf) +* [Composing High-Performance Memory Allocators](http://people.cs.umass.edu/~emery/pubs/berger-pldi2001.pdf) +* [Reconsidering Custom Memory Allocation](http://people.cs.umass.edu/~emery/pubs/berger-oopsla2002.pdf) + +### Papers *about* Rust + +* [GPU programming in Rust](http://www.cs.indiana.edu/~eholk/papers/hips2013.pdf) +* [Parallel closures: a new twist on an old idea](https://www.usenix.org/conference/hotpar12/parallel-closures-new-twist-old-idea) - not exactly about rust, but by nmatsakis diff --git a/src/doc/trpl/arrays.md b/src/doc/trpl/arrays.md deleted file mode 100644 index a6ecac962d60d..0000000000000 --- a/src/doc/trpl/arrays.md +++ /dev/null @@ -1,48 +0,0 @@ -% Arrays - -Like many programming languages, Rust has list types to represent a sequence of -things. The most basic is the *array*, a fixed-size list of elements of the -same type. By default, arrays are immutable. - -```{rust} -let a = [1, 2, 3]; // a: [i32; 3] -let mut m = [1, 2, 3]; // mut m: [i32; 3] -``` - -There's a shorthand for initializing each element of an array to the same -value. In this example, each element of `a` will be initialized to `0`: - -```{rust} -let a = [0; 20]; // a: [i32; 20] -``` - -Arrays have type `[T; N]`. We'll talk about this `T` notation later, when we -cover generics. - -You can get the number of elements in an array `a` with `a.len()`, and use -`a.iter()` to iterate over them with a for loop. This code will print each -number in order: - -```{rust} -let a = [1, 2, 3]; - -println!("a has {} elements", a.len()); -for e in a.iter() { - println!("{}", e); -} -``` - -You can access a particular element of an array with *subscript notation*: - -```{rust} -let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] - -println!("The second name is: {}", names[1]); -``` - -Subscripts start at zero, like in most programming languages, so the first name -is `names[0]` and the second name is `names[1]`. The above example prints -`The second name is: Brian`. If you try to use a subscript that is not in the -array, you will get an error: array access is bounds-checked at run-time. Such -errant access is the source of many bugs in other systems programming -languages. diff --git a/src/doc/trpl/primitive-types.md b/src/doc/trpl/primitive-types.md index 2878e7ce4754e..fcbe2b2f8bf70 100644 --- a/src/doc/trpl/primitive-types.md +++ b/src/doc/trpl/primitive-types.md @@ -1,3 +1,268 @@ % Primitive Types -Coming Soon! +The Rust language has a number of types that are considered ‘primitive’. This +means that they’re built-in to the language. Rust is structured in such a way +that the standard library also provides a number of useful types built on top +of these ones, as well, but these are the most primitive. + +# Booleans + +Rust has a built in boolean type, named `bool`. It has two values, `true` and `false`: + +```rust +let x = true; + +let y: bool = false; +``` + +A common use of booleans is in [`if` statements][if]. + +[if]: if.html + +You can find more documentation for `bool`s [in the standard library +documentation][bool]. + +[bool]: ../std/primitive.bool.html + +# `char` + +The `char` type represents a single Unicode scalar value. You can create `char`s +with a single tick: (`'`) + +```rust +let x = 'x'; +let two_hearts = '💕'; +``` + +Unlike some other languages, this means that Rust’s `char` is not a single byte, +but four. + +You can find more documentation for `char`s [in the standard library +documentation][char]. + +[char]: ../std/primitive.char.html + +# Numeric types + +Rust has a variety of numeric types in a few categories: signed and unsigned, +fixed and variable, floating-point and integer. + +These types consist of two parts: the category, and the size. For example, +`u16` is an unsigned type with sixteen bits of size. More bits lets you have +bigger numbers. + +If a number literal has nothing to cause its type to be inferred, it defaults: + +```rust +let x = 42; // x has type i32 + +let y = 1.0; // y has type f64 +``` + +Here’s a list of the different numeric types, with links to their documentation +in the standard library: + +* [i16](../std/primitive.i16.html) +* [i32](../std/primitive.i32.html) +* [i64](../std/primitive.i64.html) +* [i8](../std/primitive.i8.html) +* [u16](../std/primitive.u16.html) +* [u32](../std/primitive.u32.html) +* [u64](../std/primitive.u64.html) +* [u8](../std/primitive.u8.html) +* [isize](../std/primitive.isize.html) +* [usize](../std/primitive.usize.html) +* [f32](../std/primitive.f32.html) +* [f64](../std/primitive.f64.html) + +Let’s go over them by category: + +## Signed and Unsigned + +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 +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` +is for ‘integer’. So `u8` is an eight-bit unsigned number, and `i8` is an +eight-bit signed number. + +## Fixed size types + +Fixed size types have a specific number of bits in their representation. Valid +bit sizes are `8`, `16`, `32`, and `64`. So, `u32` is an unsigned, 32-bit integer, +and `i64` is a signed, 64-bit integer. + +## Variable sized types + +Rust also provides types whose size depends on the size of a pointer of the +underlying machine. These types have ‘size’ as the category, and come in signed +and unsigned varieties. This makes for two types: `isize` and `usize`. + +## Floating-point types + +Rust also two floating point types: `f32` and `f64`. These correspond to +IEEE-754 single and double precision numbers. + +# Arrays + +Like many programming languages, Rust has list types to represent a sequence of +things. The most basic is the *array*, a fixed-size list of elements of the +same type. By default, arrays are immutable. + +```rust +let a = [1, 2, 3]; // a: [i32; 3] +let mut m = [1, 2, 3]; // m: [i32; 3] +``` + +Arrays have type `[T; N]`. We’ll talk about this `T` notation [in the generics +section][generics]. The `N` is a compile-time constant, for the length of the +array. + +There’s a shorthand for initializing each element of an array to the same +value. In this example, each element of `a` will be initialized to `0`: + +```rust +let a = [0; 20]; // a: [i32; 20] +``` + +You can get the number of elements in an array `a` with `a.len()`: + +```rust +let a = [1, 2, 3]; + +println!("a has {} elements", a.len()); +``` + +You can access a particular element of an array with *subscript notation*: + +```rust +let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] + +println!("The second name is: {}", names[1]); +``` + +Subscripts start at zero, like in most programming languages, so the first name +is `names[0]` and the second name is `names[1]`. The above example prints +`The second name is: Brian`. If you try to use a subscript that is not in the +array, you will get an error: array access is bounds-checked at run-time. Such +errant access is the source of many bugs in other systems programming +languages. + +You can find more documentation for `array`s [in the standard library +documentation][array]. + +[array]: ../std/primitive.array.html + +# Slices + +A ‘slice’ is a reference to (or “view” into) another data structure. They are +useful for allowing safe, efficient access to a portion of an array without +copying. For example, you might want to reference just one line of a file read +into memory. By nature, a slice is not created directly, but from an existing +variable. Slices have a length, can be mutable or not, and in many ways behave +like arrays: + +```rust +let a = [0, 1, 2, 3, 4]; +let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3 +``` + +Slices have type `&[T]`. We’ll talk about that `T` when we cover +[generics][generics]. + +[generics]: generics.html + +You can find more documentation for `slices`s [in the standard library +documentation][slice]. + +[slice]: ../std/primitive.slice.html + +# `str` + +Rust’s `str` type is the most primitive string type. As an [unsized type][dst], +it’s not very useful by itself, but becomes useful when placed behind a reference, +like [`&str`][strings]. As such, we’ll just leave it at that. + +[dst]: unsized-types.html +[strings]: strings.html + +You can find more documentation for `str` [in the standard library +documentation][str]. + +[str]: ../std/primitive.str.html + +# Tuples + +A tuple is an ordered list of fixed size. Like this: + +```rust +let x = (1, "hello"); +``` + +The parentheses and commas form this two-length tuple. Here’s the same code, but +with the type annotated: + +```rust +let x: (i32, &str) = (1, "hello"); +``` + +As you can see, the type of a tuple looks just like the tuple, but with each +position having a type name rather than the value. Careful readers will also +note that tuples are heterogeneous: we have an `i32` and a `&str` in this tuple. +In systems programming languages, strings are a bit more complex than in other +languages. For now, just read `&str` as a *string slice*, and we’ll learn more +soon. + +You can access the fields in a tuple through a *destructuring let*. Here’s +an example: + +```rust +let (x, y, z) = (1, 2, 3); + +println!("x is {}", x); +``` + +Remember [before][let] when I said the left-hand side of a `let` statement was more +powerful than just assigning a binding? Here we are. We can put a pattern on +the left-hand side of the `let`, and if it matches up to the right-hand side, +we can assign multiple bindings at once. In this case, `let` "destructures," +or "breaks up," the tuple, and assigns the bits to three bindings. + +[let]: variable-bindings.html + +This pattern is very powerful, and we’ll see it repeated more later. + +There are also a few things you can do with a tuple as a whole, without +destructuring. You can assign one tuple into another, if they have the same +contained types and [arity]. Tuples have the same arity when they have the same +length. + +[arity]: glossary.html#arity + +```rust +let mut x = (1, 2); // x: (i32, i32) +let y = (2, 3); // y: (i32, i32) + +x = y; +``` + +You can find more documentation for tuples [in the standard library +documentation][tuple]. + +[tuple]: ../std/primitive.tuple.html + +# Functions + +Functions also have a type! They look like this: + +``` +fn foo(x: i32) -> i32 { x } + +let x: fn(i32) -> i32 = foo; +``` + +In this case, `x` is a ‘function pointer’ to a function that takes an `i32` and +returns an `i32`. diff --git a/src/doc/trpl/slices.md b/src/doc/trpl/slices.md deleted file mode 100644 index a31c0ac3c4e69..0000000000000 --- a/src/doc/trpl/slices.md +++ /dev/null @@ -1,21 +0,0 @@ -% Slices - -A *slice* is a reference to (or "view" into) an array. They are useful for -allowing safe, efficient access to a portion of an array without copying. For -example, you might want to reference just one line of a file read into memory. -By nature, a slice is not created directly, but from an existing variable. -Slices have a length, can be mutable or not, and in many ways behave like -arrays: - -```{rust} -let a = [0, 1, 2, 3, 4]; -let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3 - -for e in middle.iter() { - println!("{}", e); // Prints 1, 2, 3 -} -``` - -You can also take a slice of a vector, `String`, or `&str`, because they are -backed by arrays. Slices have type `&[T]`, which we'll talk about when we cover -generics. diff --git a/src/doc/trpl/tuples.md b/src/doc/trpl/tuples.md deleted file mode 100644 index dd526d05b671e..0000000000000 --- a/src/doc/trpl/tuples.md +++ /dev/null @@ -1,97 +0,0 @@ -% Tuples - -The first compound data type we're going to talk about is called the *tuple*. -A tuple is an ordered list of fixed size. Like this: - -```rust -let x = (1, "hello"); -``` - -The parentheses and commas form this two-length tuple. Here's the same code, but -with the type annotated: - -```rust -let x: (i32, &str) = (1, "hello"); -``` - -As you can see, the type of a tuple looks just like the tuple, but with each -position having a type name rather than the value. Careful readers will also -note that tuples are heterogeneous: we have an `i32` and a `&str` in this tuple. -You have briefly seen `&str` used as a type before, and we'll discuss the -details of strings later. In systems programming languages, strings are a bit -more complex than in other languages. For now, just read `&str` as a *string -slice*, and we'll learn more soon. - -You can access the fields in a tuple through a *destructuring let*. Here's -an example: - -```rust -let (x, y, z) = (1, 2, 3); - -println!("x is {}", x); -``` - -Remember before when I said the left-hand side of a `let` statement was more -powerful than just assigning a binding? Here we are. We can put a pattern on -the left-hand side of the `let`, and if it matches up to the right-hand side, -we can assign multiple bindings at once. In this case, `let` "destructures," -or "breaks up," the tuple, and assigns the bits to three bindings. - -This pattern is very powerful, and we'll see it repeated more later. - -There are also a few things you can do with a tuple as a whole, without -destructuring. You can assign one tuple into another, if they have the same -contained types and [arity]. Tuples have the same arity when they have the same -length. - -```rust -let mut x = (1, 2); // x: (i32, i32) -let y = (2, 3); // y: (i32, i32) - -x = y; -``` - -You can also check for equality with `==`. Again, this will only compile if the -tuples have the same type. - -```rust -let x = (1, 2, 3); -let y = (2, 2, 4); - -if x == y { - println!("yes"); -} else { - println!("no"); -} -``` - -This will print `no`, because some of the values aren't equal. - -Note that the order of the values is considered when checking for equality, -so the following example will also print `no`. - -```rust -let x = (1, 2, 3); -let y = (2, 1, 3); - -if x == y { - println!("yes"); -} else { - println!("no"); -} -``` - -One other use of tuples is to return multiple values from a function: - -```rust -fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) } - -fn main() { - let (x, y) = next_two(5); - println!("x, y = {}, {}", x, y); -} -``` - -Even though Rust functions can only return one value, a tuple *is* one value, -that happens to be made up of more than one value. You can also see in this -example how you can destructure a pattern returned by a function, as well. diff --git a/src/doc/trpl/variable-bindings.md b/src/doc/trpl/variable-bindings.md index 88babd8659c2b..d971e557a9a2e 100644 --- a/src/doc/trpl/variable-bindings.md +++ b/src/doc/trpl/variable-bindings.md @@ -1,44 +1,48 @@ % Variable Bindings -The first thing we'll learn about are *variable bindings*. They look like this: +Vitually every non-’Hello World’ Rust program uses *variable bindings*. They +look like this: -```{rust} +```rust fn main() { let x = 5; } ``` -Putting `fn main() {` in each example is a bit tedious, so we'll leave that out -in the future. If you're following along, make sure to edit your `main()` -function, rather than leaving it off. Otherwise, you'll get an error. +Putting `fn main() {` in each example is a bit tedious, so we’ll leave that out +in the future. If you’re following along, make sure to edit your `main()` +function, rather than leaving it off. Otherwise, you’ll get an error. -In many languages, this is called a *variable*. But Rust's variable bindings -have a few tricks up their sleeves. Rust has a very powerful feature called -*pattern matching* that we'll get into detail with later, but the left -hand side of a `let` expression is a full pattern, not just a variable name. -This means we can do things like: +In many languages, this is called a *variable*, but Rust’s variable bindings +have a few tricks up their sleeves. For example the left-hand side of a `let` +expression is a ‘[pattern][pattern]’, not just a variable name. This means we +can do things like: -```{rust} +```rust let (x, y) = (1, 2); ``` After this expression is evaluated, `x` will be one, and `y` will be two. -Patterns are really powerful, but this is about all we can do with them so far. -So let's just keep this in the back of our minds as we go forward. +Patterns are really powerful, and have [their own section][pattern] in the +book. We don’t need those features for now, so we’ll just keep this in the back +of our minds as we go forward. + +[pattern]: patterns.html Rust is a statically typed language, which means that we specify our types up -front. So why does our first example compile? Well, Rust has this thing called -*type inference*. If it can figure out what the type of something is, Rust -doesn't require you to actually type it out. +front, and they’re checked at compile time. So why does our first example +compile? Well, Rust has this thing called ‘type inference’. If it can figure +out what the type of something is, Rust doesn’t require you to actually type it +out. We can add the type if we want to, though. Types come after a colon (`:`): -```{rust} +```rust let x: i32 = 5; ``` -If I asked you to read this out loud to the rest of the class, you'd say "`x` -is a binding with the type `i32` and the value `five`." +If I asked you to read this out loud to the rest of the class, you’d say “`x` +is a binding with the type `i32` and the value `five`.” In this case we chose to represent `x` as a 32-bit signed integer. Rust has many different primitive integer types. They begin with `i` for signed integers @@ -48,19 +52,20 @@ bits. In future examples, we may annotate the type in a comment. The examples will look like this: -```{rust} +```rust fn main() { let x = 5; // x: i32 } ``` -Note the similarities between this annotation and the syntax you use with `let`. -Including these kinds of comments is not idiomatic Rust, but we'll occasionally -include them to help you understand what the types that Rust infers are. +Note the similarities between this annotation and the syntax you use with +`let`. Including these kinds of comments is not idiomatic Rust, but we'll +occasionally include them to help you understand what the types that Rust +infers are. By default, bindings are *immutable*. This code will not compile: -```{ignore} +```rust,ignore let x = 5; x = 10; ``` @@ -75,30 +80,30 @@ error: re-assignment of immutable variable `x` If you want a binding to be mutable, you can use `mut`: -```{rust} +```rust let mut x = 5; // mut x: i32 x = 10; ``` There is no single reason that bindings are immutable by default, but we can -think about it through one of Rust's primary focuses: safety. If you forget to +think about it through one of Rust’s primary focuses: safety. If you forget to say `mut`, the compiler will catch it, and let you know that you have mutated something you may not have intended to mutate. If bindings were mutable by default, the compiler would not be able to tell you this. If you _did_ intend mutation, then the solution is quite easy: add `mut`. -There are other good reasons to avoid mutable state when possible, but they're +There are other good reasons to avoid mutable state when possible, but they’re out of the scope of this guide. In general, you can often avoid explicit mutation, and so it is preferable in Rust. That said, sometimes, mutation is -what you need, so it's not verboten. +what you need, so it’s not verboten. -Let's get back to bindings. Rust variable bindings have one more aspect that +Let’s get back to bindings. Rust variable bindings have one more aspect that differs from other languages: bindings are required to be initialized with a value before you're allowed to use them. -Let's try it out. Change your `src/main.rs` file to look like this: +Let’s try it out. Change your `src/main.rs` file to look like this: -```{rust} +```rust fn main() { let x: i32; @@ -106,21 +111,22 @@ fn main() { } ``` -You can use `cargo build` on the command line to build it. You'll get a warning, -but it will still print "Hello, world!": +You can use `cargo build` on the command line to build it. You’ll get a +warning, but it will still print "Hello, world!": ```text Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) -src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] on by default +src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] + on by default src/main.rs:2 let x: i32; ^ ``` -Rust warns us that we never use the variable binding, but since we never use it, -no harm, no foul. Things change if we try to actually use this `x`, however. Let's -do that. Change your program to look like this: +Rust warns us that we never use the variable binding, but since we never use +it, no harm, no foul. Things change if we try to actually use this `x`, +however. Let’s do that. Change your program to look like this: -```{rust,ignore} +```rust,ignore fn main() { let x: i32; @@ -128,9 +134,9 @@ fn main() { } ``` -And try to build it. You'll get an error: +And try to build it. You’ll get an error: -```{bash} +```bash $ cargo build Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) src/main.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x` @@ -144,18 +150,20 @@ error: aborting due to previous error Could not compile `hello_world`. ``` -Rust will not let us use a value that has not been initialized. Next, let's +Rust will not let us use a value that has not been initialized. Next, let’s talk about this stuff we've added to `println!`. If you include two curly braces (`{}`, some call them moustaches...) in your string to print, Rust will interpret this as a request to interpolate some sort of value. *String interpolation* is a computer science term that means "stick in the middle of a string." We add a comma, and then `x`, to indicate that we -want `x` to be the value we're interpolating. The comma is used to separate -arguments we pass to functions and macros, if you're passing more than one. - -When you just use the curly braces, Rust will attempt to display the -value in a meaningful way by checking out its type. If you want to specify the -format in a more detailed manner, there are a [wide number of options -available](../std/fmt/index.html). For now, we'll just stick to the default: -integers aren't very complicated to print. +want `x` to be the value we’re interpolating. The comma is used to separate +arguments we pass to functions and macros, if you’re passing more than one. + +When you just use the curly braces, Rust will attempt to display the value in a +meaningful way by checking out its type. If you want to specify the format in a +more detailed manner, there are a [wide number of options available][format]. +For now, we'll just stick to the default: integers aren't very complicated to +print. + +[format]: ../std/fmt/index.html diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 3f85af9719778..e190fb4222615 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -362,10 +362,6 @@ fn test_arena_destructors_fail() { } /// A faster arena that can hold objects of only one type. -/// -/// Safety note: Modifying objects in the arena that have already had their -/// `drop` destructors run can cause leaks, because the destructor will not -/// run again for these objects. pub struct TypedArena { /// A pointer to the next object to be allocated. ptr: Cell<*const T>, diff --git a/src/libcollections/borrow.rs b/src/libcollections/borrow.rs index 4503ad14e92b4..7332bf4670ae5 100644 --- a/src/libcollections/borrow.rs +++ b/src/libcollections/borrow.rs @@ -39,7 +39,7 @@ use self::Cow::*; /// Borrow>` and `Vec: Borrow<[T]>`. #[stable(feature = "rust1", since = "1.0.0")] pub trait Borrow { - /// Immutably borrow from an owned value. + /// Immutably borrows from an owned value. /// /// # Examples /// @@ -67,7 +67,7 @@ pub trait Borrow { /// Similar to `Borrow`, but for mutable borrows. #[stable(feature = "rust1", since = "1.0.0")] pub trait BorrowMut : Borrow { - /// Mutably borrow from an owned value. + /// Mutably borrows from an owned value. /// /// # Examples /// @@ -126,7 +126,7 @@ impl<'a, B: ?Sized> Borrow for Cow<'a, B> where B: ToOwned, ::O } } -/// A generalization of Clone to borrowed data. +/// A generalization of `Clone` to borrowed data. /// /// Some types make it possible to go from borrowed to owned, usually by /// implementing the `Clone` trait. But `Clone` works only for going from `&T` @@ -137,7 +137,7 @@ pub trait ToOwned { #[stable(feature = "rust1", since = "1.0.0")] type Owned: Borrow; - /// Create owned data from borrowed data, usually by copying. + /// Creates owned data from borrowed data, usually by cloning. #[stable(feature = "rust1", since = "1.0.0")] fn to_owned(&self) -> Self::Owned; } @@ -155,9 +155,9 @@ impl ToOwned for T where T: Clone { /// data lazily when mutation or ownership is required. The type is designed to /// work with general borrowed data via the `Borrow` trait. /// -/// `Cow` implements both `Deref`, which means that you can call +/// `Cow` implements `Deref`, which means that you can call /// non-mutating methods directly on the data it encloses. If mutation -/// is desired, `to_mut` will obtain a mutable references to an owned +/// is desired, `to_mut` will obtain a mutable reference to an owned /// value, cloning if necessary. /// /// # Examples @@ -200,7 +200,7 @@ impl<'a, B: ?Sized> Clone for Cow<'a, B> where B: ToOwned { } impl<'a, B: ?Sized> Cow<'a, B> where B: ToOwned { - /// Acquire a mutable reference to the owned form of the data. + /// Acquires a mutable reference to the owned form of the data. /// /// Copies the data if it is not already owned. /// @@ -226,7 +226,7 @@ impl<'a, B: ?Sized> Cow<'a, B> where B: ToOwned { } } - /// Extract the owned data. + /// Extracts the owned data. /// /// Copies the data if it is not already owned. /// @@ -327,7 +327,7 @@ impl<'a, B: ?Sized> Hash for Cow<'a, B> where B: Hash + ToOwned } } -/// Trait for moving into a `Cow` +/// Trait for moving into a `Cow`. #[unstable(feature = "into_cow", reason = "may be replaced by `convert::Into`")] pub trait IntoCow<'a, B: ?Sized> where B: ToOwned { /// Moves `self` into `Cow` diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index b767a1ea054c1..c308cdfc8b82c 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -525,8 +525,7 @@ impl Vec { /// /// # Panics /// - /// Panics if `index` is not between `0` and the vector's length (both - /// bounds inclusive). + /// Panics if `index` is greater than the vector's length. /// /// # Examples /// diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 85b648bbd5980..1c1ad5fd33fb8 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -10,15 +10,35 @@ //! Traits for conversions between types. //! -//! The traits in this module provide a general way to talk about -//! conversions from one type to another. They follow the standard -//! Rust conventions of `as`/`to`/`into`/`from`. +//! The traits in this module provide a general way to talk about conversions from one type to +//! another. They follow the standard Rust conventions of `as`/`to`/`into`/`from`. +//! +//! Like many traits, these are often used as bounds for generic functions, to support arguments of +//! multiple types. +//! +//! See each trait for usage examples. #![stable(feature = "rust1", since = "1.0.0")] use marker::Sized; /// A cheap, reference-to-reference conversion. +/// +/// # Examples +/// +/// Both `String` and `&str` implement `AsRef`: +/// +/// ``` +/// fn is_hello>(s: T) { +/// assert_eq!("hello", s.as_ref()); +/// } +/// +/// let s = "hello"; +/// is_hello(s); +/// +/// let s = "hello".to_string(); +/// is_hello(s); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRef { /// Performs the conversion. @@ -34,8 +54,21 @@ pub trait AsMut { fn as_mut(&mut self) -> &mut T; } -/// A conversion that consumes `self`, which may or may not be -/// expensive. +/// A conversion that consumes `self`, which may or may not be expensive. +/// +/// # Examples +/// +/// `String` implements `Into>`: +/// +/// ``` +/// fn is_hello>>(s: T) { +/// let bytes = b"hello".to_vec(); +/// assert_eq!(bytes, s.into()); +/// } +/// +/// let s = "hello".to_string(); +/// is_hello(s); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait Into: Sized { /// Performs the conversion. @@ -44,6 +77,19 @@ pub trait Into: Sized { } /// Construct `Self` via a conversion. +/// +/// # Examples +/// +/// `String` implements `From<&str>`: +/// +/// ``` +/// let s = "hello"; +/// let string = "hello".to_string(); +/// +/// let other_string: String = From::from(s); +/// +/// assert_eq!(string, other_string); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait From { /// Performs the conversion. diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 9007a5142c3c7..6c6b3acd93697 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -217,7 +217,7 @@ pub trait Int reason = "pending integer conventions")] fn trailing_zeros(self) -> u32; - /// Shifts the bits to the left by a specified amount amount, `n`, wrapping + /// Shifts the bits to the left by a specified amount, `n`, wrapping /// the truncated bits to the end of the resulting integer. /// /// # Examples @@ -235,7 +235,7 @@ pub trait Int reason = "pending integer conventions")] fn rotate_left(self, n: u32) -> Self; - /// Shifts the bits to the right by a specified amount amount, `n`, wrapping + /// Shifts the bits to the right by a specified amount, `n`, wrapping /// the truncated bits to the beginning of the resulting integer. /// /// # Examples @@ -937,7 +937,7 @@ macro_rules! int_impl { (self as $UnsignedT).trailing_zeros() } - /// Shifts the bits to the left by a specified amount amount, `n`, + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// /// # Examples @@ -957,7 +957,7 @@ macro_rules! int_impl { (self as $UnsignedT).rotate_left(n) as $T } - /// Shifts the bits to the right by a specified amount amount, `n`, + /// Shifts the bits to the right by a specified amount, `n`, /// wrapping the truncated bits to the beginning of the resulting /// integer. /// @@ -1224,11 +1224,10 @@ macro_rules! int_impl { /// /// # Examples /// - /// ```rust - /// # #![feature(core)] - /// use std::num::Int; + /// ``` + /// let x: i32 = 2; // or any other integer type /// - /// assert_eq!(2.pow(4), 16); + /// assert_eq!(x.pow(4), 16); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1457,7 +1456,7 @@ macro_rules! uint_impl { unsafe { $cttz(self as $ActualT) as u32 } } - /// Shifts the bits to the left by a specified amount amount, `n`, + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// /// # Examples @@ -1479,7 +1478,7 @@ macro_rules! uint_impl { (self << n) | (self >> (($BITS - n) % $BITS)) } - /// Shifts the bits to the right by a specified amount amount, `n`, + /// Shifts the bits to the right by a specified amount, `n`, /// wrapping the truncated bits to the beginning of the resulting /// integer. /// diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 2f2db8f38bd87..d21891ab23f1c 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -26,7 +26,7 @@ use syntax::codemap::{DUMMY_SP, Span}; use util::ppaux::Repr; #[derive(Copy, Clone)] -struct ParamIsLocal(bool); +struct InferIsLocal(bool); /// True if there exist types that satisfy both of the two given impls. pub fn overlapping_impls(infcx: &InferCtxt, @@ -60,7 +60,7 @@ fn overlap(selcx: &mut SelectionContext, let (a_trait_ref, a_obligations) = impl_trait_ref_and_oblig(selcx, a_def_id, - util::free_substs_for_impl); + util::fresh_type_vars_for_impl); let (b_trait_ref, b_obligations) = impl_trait_ref_and_oblig(selcx, b_def_id, @@ -104,7 +104,7 @@ pub fn trait_ref_is_knowable<'tcx>(tcx: &ty::ctxt<'tcx>, trait_ref: &ty::TraitRe // if the orphan rules pass, that means that no ancestor crate can // impl this, so it's up to us. - if orphan_check_trait_ref(tcx, trait_ref, ParamIsLocal(false)).is_ok() { + if orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(false)).is_ok() { debug!("trait_ref_is_knowable: orphan check passed"); return true; } @@ -126,7 +126,7 @@ pub fn trait_ref_is_knowable<'tcx>(tcx: &ty::ctxt<'tcx>, trait_ref: &ty::TraitRe // implemented by an upstream crate, which means that the impl // must be visible to us, and -- since the trait is fundamental // -- we can test. - orphan_check_trait_ref(tcx, trait_ref, ParamIsLocal(true)).is_err() + orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(true)).is_err() } type SubstsFn = for<'a,'tcx> fn(infcx: &InferCtxt<'a, 'tcx>, @@ -196,16 +196,16 @@ pub fn orphan_check<'tcx>(tcx: &ty::ctxt<'tcx>, return Ok(()); } - orphan_check_trait_ref(tcx, &trait_ref, ParamIsLocal(false)) + orphan_check_trait_ref(tcx, &trait_ref, InferIsLocal(false)) } fn orphan_check_trait_ref<'tcx>(tcx: &ty::ctxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>, - param_is_local: ParamIsLocal) + infer_is_local: InferIsLocal) -> Result<(), OrphanCheckErr<'tcx>> { - debug!("orphan_check_trait_ref(trait_ref={}, param_is_local={})", - trait_ref.repr(tcx), param_is_local.0); + debug!("orphan_check_trait_ref(trait_ref={}, infer_is_local={})", + trait_ref.repr(tcx), infer_is_local.0); // First, create an ordered iterator over all the type parameters to the trait, with the self // type appearing first. @@ -215,12 +215,12 @@ fn orphan_check_trait_ref<'tcx>(tcx: &ty::ctxt<'tcx>, // Find the first input type that either references a type parameter OR // some local type. for input_ty in input_tys { - if ty_is_local(tcx, input_ty, param_is_local) { + if ty_is_local(tcx, input_ty, infer_is_local) { debug!("orphan_check_trait_ref: ty_is_local `{}`", input_ty.repr(tcx)); // First local input type. Check that there are no // uncovered type parameters. - let uncovered_tys = uncovered_tys(tcx, input_ty, param_is_local); + let uncovered_tys = uncovered_tys(tcx, input_ty, infer_is_local); for uncovered_ty in uncovered_tys { if let Some(param) = uncovered_ty.walk().find(|t| is_type_parameter(t)) { debug!("orphan_check_trait_ref: uncovered type `{}`", param.repr(tcx)); @@ -234,7 +234,7 @@ fn orphan_check_trait_ref<'tcx>(tcx: &ty::ctxt<'tcx>, // Otherwise, enforce invariant that there are no type // parameters reachable. - if !param_is_local.0 { + if !infer_is_local.0 { if let Some(param) = input_ty.walk().find(|t| is_type_parameter(t)) { debug!("orphan_check_trait_ref: uncovered type `{}`", param.repr(tcx)); return Err(OrphanCheckErr::UncoveredTy(param)); @@ -249,14 +249,14 @@ fn orphan_check_trait_ref<'tcx>(tcx: &ty::ctxt<'tcx>, fn uncovered_tys<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, - param_is_local: ParamIsLocal) + infer_is_local: InferIsLocal) -> Vec> { - if ty_is_local_constructor(tcx, ty, param_is_local) { + if ty_is_local_constructor(tcx, ty, infer_is_local) { vec![] } else if fundamental_ty(tcx, ty) { ty.walk_shallow() - .flat_map(|t| uncovered_tys(tcx, t, param_is_local).into_iter()) + .flat_map(|t| uncovered_tys(tcx, t, infer_is_local).into_iter()) .collect() } else { vec![ty] @@ -271,10 +271,10 @@ fn is_type_parameter<'tcx>(ty: Ty<'tcx>) -> bool { } } -fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, param_is_local: ParamIsLocal) -> bool +fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, infer_is_local: InferIsLocal) -> bool { - ty_is_local_constructor(tcx, ty, param_is_local) || - fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, param_is_local)) + ty_is_local_constructor(tcx, ty, infer_is_local) || + fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, infer_is_local)) } fn fundamental_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool @@ -293,7 +293,7 @@ fn fundamental_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool fn ty_is_local_constructor<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, - param_is_local: ParamIsLocal) + infer_is_local: InferIsLocal) -> bool { debug!("ty_is_local_constructor({})", ty.repr(tcx)); @@ -310,13 +310,13 @@ fn ty_is_local_constructor<'tcx>(tcx: &ty::ctxt<'tcx>, ty::ty_ptr(..) | ty::ty_rptr(..) | ty::ty_tup(..) | - ty::ty_infer(..) | + ty::ty_param(..) | ty::ty_projection(..) => { false } - ty::ty_param(..) => { - param_is_local.0 + ty::ty_infer(..) => { + infer_is_local.0 } ty::ty_enum(def_id, _) | diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 297cea13207e5..3a1be785580af 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::region; use middle::subst::{Substs, VecPerParamSpace}; use middle::infer::InferCtxt; use middle::ty::{self, Ty, AsPredicate, ToPolyTraitRef}; @@ -304,34 +303,6 @@ pub fn fresh_type_vars_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, infcx.fresh_substs_for_generics(span, &impl_generics) } -// determine the `self` type, using fresh variables for all variables -// declared on the impl declaration e.g., `impl for Box<[(A,B)]>` -// would return ($0, $1) where $0 and $1 are freshly instantiated type -// variables. -pub fn free_substs_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, - _span: Span, - impl_def_id: ast::DefId) - -> Substs<'tcx> -{ - let tcx = infcx.tcx; - let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics; - - let some_types = impl_generics.types.map(|def| { - ty::mk_param_from_def(tcx, def) - }); - - let some_regions = impl_generics.regions.map(|def| { - // FIXME. This destruction scope information is pretty darn - // bogus; after all, the impl might not even be in this crate! - // But given what we do in coherence, it is harmless enough - // for now I think. -nmatsakis - let extent = region::DestructionScopeData::new(ast::DUMMY_NODE_ID); - ty::free_region_from_def(extent, def) - }); - - Substs::new(some_types, some_regions) -} - impl<'tcx, N> fmt::Debug for VtableImplData<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "VtableImpl({:?})", self.impl_def_id) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 148f484b0ed5c..88faf1cb68ae4 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -143,6 +143,13 @@ impl Session { pub fn span_end_note(&self, sp: Span, msg: &str) { self.diagnostic().span_end_note(sp, msg) } + + /// Prints out a message with a suggested edit of the code. + /// + /// See `diagnostic::RenderSpan::Suggestion` for more information. + pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) { + self.diagnostic().span_suggestion(sp, msg, suggestion) + } pub fn span_help(&self, sp: Span, msg: &str) { self.diagnostic().span_help(sp, msg) } diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index c57cbcb929fbc..db947a27472ad 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -522,6 +522,16 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } pub fn report(&self, err: BckError<'tcx>) { + // Catch and handle some particular cases. + match (&err.code, &err.cause) { + (&err_out_of_scope(ty::ReScope(_), ty::ReStatic), &euv::ClosureCapture(span)) | + (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)), &euv::ClosureCapture(span)) => { + return self.report_out_of_scope_escaping_closure_capture(&err, span); + } + _ => { } + } + + // General fallback. self.span_err( err.span, &self.bckerr_to_string(&err)); @@ -796,16 +806,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { format!("{} does not live long enough", msg) } err_borrowed_pointer_too_short(..) => { - let descr = match opt_loan_path(&err.cmt) { - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&*lp)) - } - None => self.cmt_to_string(&*err.cmt), - }; - + let descr = self.cmt_to_path_or_string(&err.cmt); format!("lifetime of {} is too short to guarantee \ - its contents can be safely reborrowed", - descr) + its contents can be safely reborrowed", + descr) } } } @@ -888,6 +892,39 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } + fn report_out_of_scope_escaping_closure_capture(&self, + err: &BckError<'tcx>, + capture_span: Span) + { + let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); + + span_err!( + self.tcx.sess, err.span, E0373, + "closure may outlive the current function, \ + but it borrows {}, \ + which is owned by the current function", + cmt_path_or_string); + + self.tcx.sess.span_note( + capture_span, + &format!("{} is borrowed here", + cmt_path_or_string)); + + let suggestion = + match self.tcx.sess.codemap().span_to_snippet(err.span) { + Ok(string) => format!("move {}", string), + Err(_) => format!("move || ") + }; + + self.tcx.sess.span_suggestion( + err.span, + &format!("to force the closure to take ownership of {} \ + (and any other referenced variables), \ + use the `move` keyword, as shown:", + cmt_path_or_string), + suggestion); + } + pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) { let code = err.code; match code { @@ -1035,6 +1072,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { cmt.descriptive_string(self.tcx) } + + pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String { + match opt_loan_path(cmt) { + Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), + None => self.cmt_to_string(cmt), + } + } } fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool { diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs new file mode 100644 index 0000000000000..981b28593f9a4 --- /dev/null +++ b/src/librustc_borrowck/diagnostics.rs @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +#![allow(non_snake_case)] + +register_diagnostics! { + E0373 // closure may outlive current fn, but it borrows {}, which is owned by current fn +} + +__build_diagnostic_array! { DIAGNOSTICS } diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 54feed930a80d..647ea3555ba91 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -40,6 +40,10 @@ pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; pub use borrowck::FnPartsWithCFG; +// NB: This module needs to be declared first so diagnostics are +// registered before they are used. +pub mod diagnostics; + mod borrowck; pub mod graphviz; diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index e544484351600..93b27b6ce9efd 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -345,8 +345,8 @@ pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); #[stable(feature = "rust1", since = "1.0.0")] pub struct RecvError; -/// This enumeration is the list of the possible reasons that try_recv could not -/// return data when called. +/// This enumeration is the list of the possible reasons that `try_recv` could +/// not return data when called. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum TryRecvError { diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 10c79671c0c94..f893ae243bbb6 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -367,7 +367,7 @@ impl Builder { /// /// # Panics /// -/// Panicks if the OS fails to create a thread; use `Builder::spawn` +/// Panics if the OS fails to create a thread; use `Builder::spawn` /// to recover from such errors. #[stable(feature = "rust1", since = "1.0.0")] pub fn spawn(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static { @@ -385,7 +385,7 @@ pub fn spawn(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static { /// /// # Panics /// -/// Panicks if the OS fails to create a thread; use `Builder::scoped` +/// Panics if the OS fails to create a thread; use `Builder::scoped` /// to recover from such errors. #[stable(feature = "rust1", since = "1.0.0")] pub fn scoped<'a, T, F>(f: F) -> JoinGuard<'a, T> where diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 56af43474a615..7635c8eadc26e 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -49,7 +49,7 @@ pub struct BytePos(pub u32); /// A character offset. Because of multibyte utf8 characters, a byte offset /// is not equivalent to a character offset. The CodeMap will convert BytePos /// values to CharPos values as necessary. -#[derive(Copy, Clone, PartialEq, Hash, PartialOrd, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Debug)] pub struct CharPos(pub usize); // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix @@ -305,9 +305,21 @@ impl ExpnId { pub type FileName = String; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineInfo { + /// Index of line, starting from 0. + pub line_index: usize, + + /// Column in line where span begins, starting from 0. + pub start_col: CharPos, + + /// Column in line where span ends, starting from 0, exclusive. + pub end_col: CharPos, +} + pub struct FileLines { pub file: Rc, - pub lines: Vec + pub lines: Vec } /// Identifies an offset of a multi-byte character in a FileMap @@ -479,9 +491,9 @@ impl FileMap { lines.push(pos); } - /// get a line from the list of pre-computed line-beginnings - /// - pub fn get_line(&self, line_number: usize) -> Option { + /// get a line from the list of pre-computed line-beginnings. + /// line-number here is 0-based. + pub fn get_line(&self, line_number: usize) -> Option<&str> { match self.src { Some(ref src) => { let lines = self.lines.borrow(); @@ -492,7 +504,7 @@ impl FileMap { match slice.find('\n') { Some(e) => &slice[..e], None => slice - }.to_string() + } }) } None => None @@ -661,10 +673,29 @@ impl CodeMap { pub fn span_to_lines(&self, sp: Span) -> FileLines { let lo = self.lookup_char_pos(sp.lo); let hi = self.lookup_char_pos(sp.hi); - let mut lines = Vec::new(); - for i in lo.line - 1..hi.line { - lines.push(i); - }; + let mut lines = Vec::with_capacity(hi.line - lo.line + 1); + + // The span starts partway through the first line, + // but after that it starts from offset 0. + let mut start_col = lo.col; + + // For every line but the last, it extends from `start_col` + // and to the end of the line. Be careful because the line + // numbers in Loc are 1-based, so we subtract 1 to get 0-based + // lines. + for line_index in lo.line-1 .. hi.line-1 { + let line_len = lo.file.get_line(line_index).map(|s| s.len()).unwrap_or(0); + lines.push(LineInfo { line_index: line_index, + start_col: start_col, + end_col: CharPos::from_usize(line_len) }); + start_col = CharPos::from_usize(0); + } + + // For the last line, it extends from `start_col` to `hi.col`: + lines.push(LineInfo { line_index: hi.line - 1, + start_col: start_col, + end_col: hi.col }); + FileLines {file: lo.file, lines: lines} } @@ -919,6 +950,7 @@ pub struct MalformedCodemapPositions { #[cfg(test)] mod test { use super::*; + use std::rc::Rc; #[test] fn t1 () { @@ -926,10 +958,10 @@ mod test { let fm = cm.new_filemap("blork.rs".to_string(), "first line.\nsecond line".to_string()); fm.next_line(BytePos(0)); - assert_eq!(fm.get_line(0), Some("first line.".to_string())); + assert_eq!(fm.get_line(0), Some("first line.")); // TESTING BROKEN BEHAVIOR: fm.next_line(BytePos(10)); - assert_eq!(fm.get_line(1), Some(".".to_string())); + assert_eq!(fm.get_line(1), Some(".")); } #[test] @@ -1057,7 +1089,54 @@ mod test { assert_eq!(file_lines.file.name, "blork.rs"); assert_eq!(file_lines.lines.len(), 1); - assert_eq!(file_lines.lines[0], 1); + assert_eq!(file_lines.lines[0].line_index, 1); + } + + /// Given a string like " ^~~~~~~~~~~~ ", produces a span + /// coverting that range. The idea is that the string has the same + /// length as the input, and we uncover the byte positions. Note + /// that this can span lines and so on. + fn span_from_selection(input: &str, selection: &str) -> Span { + assert_eq!(input.len(), selection.len()); + let left_index = selection.find('^').unwrap() as u32; + let right_index = selection.rfind('~').unwrap() as u32; + Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } + } + + fn new_filemap_and_lines(cm: &CodeMap, filename: &str, input: &str) -> Rc { + let fm = cm.new_filemap(filename.to_string(), input.to_string()); + let mut byte_pos: u32 = 0; + for line in input.lines() { + // register the start of this line + fm.next_line(BytePos(byte_pos)); + + // update byte_pos to include this line and the \n at the end + byte_pos += line.len() as u32 + 1; + } + fm + } + + /// Test span_to_snippet and span_to_lines for a span coverting 3 + /// lines in the middle of a file. + #[test] + fn span_to_snippet_and_lines_spanning_multiple_lines() { + let cm = CodeMap::new(); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection = " \n ^~\n~~~\n~~~~~ \n \n"; + new_filemap_and_lines(&cm, "blork.rs", inputtext); + let span = span_from_selection(inputtext, selection); + + // check that we are extracting the text we thought we were extracting + assert_eq!(&cm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD"); + + // check that span_to_lines gives us the complete result with the lines/cols we expected + let lines = cm.span_to_lines(span); + let expected = vec![ + LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) }, + LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) }, + LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) } + ]; + assert_eq!(lines.lines, expected); } #[test] diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 8fe23a3c8e826..ed7bdcd898e93 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -18,6 +18,7 @@ use codemap; use diagnostics; use std::cell::{RefCell, Cell}; +use std::cmp; use std::fmt; use std::io::prelude::*; use std::io; @@ -28,28 +29,39 @@ use libc; /// maximum number of lines we will print for each error; arbitrary. const MAX_LINES: usize = 6; -#[derive(Clone, Copy)] +#[derive(Clone)] pub enum RenderSpan { /// A FullSpan renders with both with an initial line for the /// message, prefixed by file:linenum, followed by a summary of /// the source code covered by the span. FullSpan(Span), + /// Similar to a FullSpan, but the cited position is the end of + /// the span, instead of the start. Used, at least, for telling + /// compiletest/runtest to look at the last line of the span + /// (since `end_highlight_lines` displays an arrow to the end + /// of the span). + EndSpan(Span), + + /// A suggestion renders with both with an initial line for the + /// message, prefixed by file:linenum, followed by a summary + /// of hypothetical source code, where the `String` is spliced + /// into the lines in place of the code covered by the span. + Suggestion(Span, String), + /// A FileLine renders with just a line for the message prefixed /// by file:linenum. FileLine(Span), } impl RenderSpan { - fn span(self) -> Span { - match self { - FullSpan(s) | FileLine(s) => s - } - } - fn is_full_span(&self) -> bool { - match self { - &FullSpan(..) => true, - &FileLine(..) => false, + fn span(&self) -> Span { + match *self { + FullSpan(s) | + Suggestion(s, _) | + EndSpan(s) | + FileLine(s) => + s } } } @@ -115,11 +127,17 @@ impl SpanHandler { self.handler.emit(Some((&self.cm, sp)), msg, Note); } pub fn span_end_note(&self, sp: Span, msg: &str) { - self.handler.custom_emit(&self.cm, FullSpan(sp), msg, Note); + self.handler.custom_emit(&self.cm, EndSpan(sp), msg, Note); } pub fn span_help(&self, sp: Span, msg: &str) { self.handler.emit(Some((&self.cm, sp)), msg, Help); } + /// Prints out a message with a suggested edit of the code. + /// + /// See `diagnostic::RenderSpan::Suggestion` for more information. + pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) { + self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help); + } pub fn fileline_note(&self, sp: Span, msg: &str) { self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note); } @@ -407,8 +425,8 @@ impl Emitter for EmitterWriter { let error = match cmsp { Some((cm, COMMAND_LINE_SP)) => emit(self, cm, FileLine(COMMAND_LINE_SP), - msg, code, lvl, false), - Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl, false), + msg, code, lvl), + Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl), None => print_diagnostic(self, "", lvl, msg, code), }; @@ -420,7 +438,7 @@ impl Emitter for EmitterWriter { fn custom_emit(&mut self, cm: &codemap::CodeMap, sp: RenderSpan, msg: &str, lvl: Level) { - match emit(self, cm, sp, msg, None, lvl, true) { + match emit(self, cm, sp, msg, None, lvl) { Ok(()) => {} Err(e) => panic!("failed to print diagnostics: {:?}", e), } @@ -428,35 +446,41 @@ impl Emitter for EmitterWriter { } fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan, - msg: &str, code: Option<&str>, lvl: Level, custom: bool) -> io::Result<()> { + msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> { let sp = rsp.span(); // We cannot check equality directly with COMMAND_LINE_SP // since PartialEq is manually implemented to ignore the ExpnId let ss = if sp.expn_id == COMMAND_LINE_EXPN { "".to_string() + } else if let EndSpan(_) = rsp { + let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}; + cm.span_to_string(span_end) } else { cm.span_to_string(sp) }; - if custom { - // we want to tell compiletest/runtest to look at the last line of the - // span (since `custom_highlight_lines` displays an arrow to the end of - // the span) - let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}; - let ses = cm.span_to_string(span_end); - try!(print_diagnostic(dst, &ses[..], lvl, msg, code)); - if rsp.is_full_span() { - try!(custom_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp))); - } - } else { - try!(print_diagnostic(dst, &ss[..], lvl, msg, code)); - if rsp.is_full_span() { + + try!(print_diagnostic(dst, &ss[..], lvl, msg, code)); + + match rsp { + FullSpan(_) => { try!(highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp))); } + EndSpan(_) => { + try!(end_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp))); + } + Suggestion(_, ref suggestion) => { + try!(highlight_suggestion(dst, cm, sp, suggestion)); + } + FileLine(..) => { + // no source text in this case! + } } + if sp != COMMAND_LINE_SP { try!(print_macro_backtrace(dst, cm, sp)); } + match code { Some(code) => match dst.registry.as_ref().and_then(|registry| registry.find_description(code)) { @@ -472,29 +496,90 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan, Ok(()) } +fn highlight_suggestion(err: &mut EmitterWriter, + cm: &codemap::CodeMap, + sp: Span, + suggestion: &str) + -> io::Result<()> +{ + let lines = cm.span_to_lines(sp); + assert!(!lines.lines.is_empty()); + + // To build up the result, we want to take the snippet from the first + // line that precedes the span, prepend that with the suggestion, and + // then append the snippet from the last line that trails the span. + let fm = &lines.file; + + let first_line = &lines.lines[0]; + let prefix = fm.get_line(first_line.line_index) + .map(|l| &l[..first_line.start_col.0]) + .unwrap_or(""); + + let last_line = lines.lines.last().unwrap(); + let suffix = fm.get_line(last_line.line_index) + .map(|l| &l[last_line.end_col.0..]) + .unwrap_or(""); + + let complete = format!("{}{}{}", prefix, suggestion, suffix); + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let fm = &*lines.file; + let mut lines = complete.lines(); + for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) { + let elided_line_num = format!("{}", line_index+1); + try!(write!(&mut err.dst, "{0}:{1:2$} {3}\n", + fm.name, "", elided_line_num.len(), line)); + } + + // if we elided some lines, add an ellipsis + if lines.next().is_some() { + let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1); + try!(write!(&mut err.dst, "{0:1$} {0:2$} ...\n", + "", fm.name.len(), elided_line_num.len())); + } + + Ok(()) +} + fn highlight_lines(err: &mut EmitterWriter, cm: &codemap::CodeMap, sp: Span, lvl: Level, - lines: codemap::FileLines) -> io::Result<()> { + lines: codemap::FileLines) + -> io::Result<()> +{ let fm = &*lines.file; - let mut elided = false; - let mut display_lines = &lines.lines[..]; - if display_lines.len() > MAX_LINES { - display_lines = &display_lines[0..MAX_LINES]; - elided = true; - } + let line_strings: Option> = + lines.lines.iter() + .map(|info| fm.get_line(info.line_index)) + .collect(); + + let line_strings = match line_strings { + None => { return Ok(()); } + Some(line_strings) => line_strings + }; + + // Display only the first MAX_LINES lines. + let all_lines = lines.lines.len(); + let display_lines = cmp::min(all_lines, MAX_LINES); + let display_line_infos = &lines.lines[..display_lines]; + let display_line_strings = &line_strings[..display_lines]; + // Print the offending lines - for &line_number in display_lines { - if let Some(line) = fm.get_line(line_number) { - try!(write!(&mut err.dst, "{}:{} {}\n", fm.name, - line_number + 1, line)); - } + for (line_info, line) in display_line_infos.iter().zip(display_line_strings.iter()) { + try!(write!(&mut err.dst, "{}:{} {}\n", + fm.name, + line_info.line_index + 1, + line)); } - if elided { - let last_line = display_lines[display_lines.len() - 1]; - let s = format!("{}:{} ", fm.name, last_line + 1); + + // If we elided something, put an ellipsis. + if display_lines < all_lines { + let last_line_index = display_line_infos.last().unwrap().line_index; + let s = format!("{}:{} ", fm.name, last_line_index + 1); try!(write!(&mut err.dst, "{0:1$}...\n", "", s.len())); } @@ -503,7 +588,7 @@ fn highlight_lines(err: &mut EmitterWriter, if lines.lines.len() == 1 { let lo = cm.lookup_char_pos(sp.lo); let mut digits = 0; - let mut num = (lines.lines[0] + 1) / 10; + let mut num = (lines.lines[0].line_index + 1) / 10; // how many digits must be indent past? while num > 0 { num /= 10; digits += 1; } @@ -515,7 +600,7 @@ fn highlight_lines(err: &mut EmitterWriter, for _ in 0..skip { s.push(' '); } - if let Some(orig) = fm.get_line(lines.lines[0]) { + if let Some(orig) = fm.get_line(lines.lines[0].line_index) { let mut col = skip; let mut lastc = ' '; let mut iter = orig.chars().enumerate(); @@ -575,12 +660,12 @@ fn highlight_lines(err: &mut EmitterWriter, } /// Here are the differences between this and the normal `highlight_lines`: -/// `custom_highlight_lines` will always put arrow on the last byte of the +/// `end_highlight_lines` will always put arrow on the last byte of the /// span (instead of the first byte). Also, when the span is too long (more -/// than 6 lines), `custom_highlight_lines` will print the first line, then +/// than 6 lines), `end_highlight_lines` will print the first line, then /// dot dot dot, then last line, whereas `highlight_lines` prints the first /// six lines. -fn custom_highlight_lines(w: &mut EmitterWriter, +fn end_highlight_lines(w: &mut EmitterWriter, cm: &codemap::CodeMap, sp: Span, lvl: Level, @@ -590,32 +675,32 @@ fn custom_highlight_lines(w: &mut EmitterWriter, let lines = &lines.lines[..]; if lines.len() > MAX_LINES { - if let Some(line) = fm.get_line(lines[0]) { + if let Some(line) = fm.get_line(lines[0].line_index) { try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - lines[0] + 1, line)); + lines[0].line_index + 1, line)); } try!(write!(&mut w.dst, "...\n")); - let last_line_number = lines[lines.len() - 1]; - if let Some(last_line) = fm.get_line(last_line_number) { + let last_line_index = lines[lines.len() - 1].line_index; + if let Some(last_line) = fm.get_line(last_line_index) { try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - last_line_number + 1, last_line)); + last_line_index + 1, last_line)); } } else { - for &line_number in lines { - if let Some(line) = fm.get_line(line_number) { + for line_info in lines { + if let Some(line) = fm.get_line(line_info.line_index) { try!(write!(&mut w.dst, "{}:{} {}\n", fm.name, - line_number + 1, line)); + line_info.line_index + 1, line)); } } } - let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1]+1); + let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1); let hi = cm.lookup_char_pos(sp.hi); let skip = last_line_start.width(false); let mut s = String::new(); for _ in 0..skip { s.push(' '); } - if let Some(orig) = fm.get_line(lines[0]) { + if let Some(orig) = fm.get_line(lines[0].line_index) { let iter = orig.chars().enumerate(); for (pos, ch) in iter { // Span seems to use half-opened interval, so subtract 1 diff --git a/src/test/compile-fail/borrowck-escaping-closure-error-1.rs b/src/test/compile-fail/borrowck-escaping-closure-error-1.rs new file mode 100644 index 0000000000000..87e40df7663ba --- /dev/null +++ b/src/test/compile-fail/borrowck-escaping-closure-error-1.rs @@ -0,0 +1,25 @@ +// 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. + +use std::thread::spawn; + +// Test that we give a custom error (E0373) for the case where a +// closure is escaping current frame, and offer a suggested code edit. +// I refrained from including the precise message here, but the +// original text as of the time of this writing is: +// +// closure may outlive the current function, but it borrows `books`, +// which is owned by the current function + +fn main() { + let mut books = vec![1,2,3]; + spawn(|| books.push(4)); + //~^ ERROR E0373 +} diff --git a/src/test/compile-fail/borrowck-escaping-closure-error-2.rs b/src/test/compile-fail/borrowck-escaping-closure-error-2.rs new file mode 100644 index 0000000000000..67700be890b1f --- /dev/null +++ b/src/test/compile-fail/borrowck-escaping-closure-error-2.rs @@ -0,0 +1,25 @@ +// 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. + +// Test that we give a custom error (E0373) for the case where a +// closure is escaping current frame, and offer a suggested code edit. +// I refrained from including the precise message here, but the +// original text as of the time of this writing is: +// +// closure may outlive the current function, but it borrows `books`, +// which is owned by the current function + +fn foo<'a>(x: &'a i32) -> Box { + let mut books = vec![1,2,3]; + Box::new(|| books.push(4)) + //~^ ERROR E0373 +} + +fn main() { } diff --git a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs new file mode 100644 index 0000000000000..3fd635b3d616f --- /dev/null +++ b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs @@ -0,0 +1,28 @@ +// 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. + +// Check that we detect an overlap here in the case where: +// +// for some type X: +// T = (X,) +// T11 = X, U11 = X +// +// Seems pretty basic, but then there was issue #24241. :) + +trait From { +} + +impl From for T { //~ ERROR E0119 +} + +impl From<(U11,)> for (T11,) { +} + +fn main() { } diff --git a/src/test/compile-fail/issue-16747.rs b/src/test/compile-fail/issue-16747.rs index 64334fe4392f8..b4abef0bd280b 100644 --- a/src/test/compile-fail/issue-16747.rs +++ b/src/test/compile-fail/issue-16747.rs @@ -18,10 +18,10 @@ trait Collection { fn len(&self) -> usize; } struct List<'a, T: ListItem<'a>> { //~^ ERROR the parameter type `T` may not live long enough -//~^^ NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at +//~| HELP consider adding an explicit lifetime bound +//~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at slice: &'a [T] } -//~^ HELP consider adding an explicit lifetime bound impl<'a, T: ListItem<'a>> Collection for List<'a, T> { fn len(&self) -> usize { 0 diff --git a/src/test/compile-fail/issue-20772.rs b/src/test/compile-fail/issue-20772.rs new file mode 100644 index 0000000000000..44c92f946f03d --- /dev/null +++ b/src/test/compile-fail/issue-20772.rs @@ -0,0 +1,15 @@ +// 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. + +trait T : Iterator +//~^ ERROR unsupported cyclic reference between types/traits detected +{} + +fn main() {} diff --git a/src/test/compile-fail/issue-20939.rs b/src/test/compile-fail/issue-20939.rs new file mode 100644 index 0000000000000..88197166ee082 --- /dev/null +++ b/src/test/compile-fail/issue-20939.rs @@ -0,0 +1,16 @@ +// 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. + +trait Foo {} + +impl<'a> Foo for Foo+'a {} +//~^ ERROR the object type `Foo + 'a` automatically implements the trait `Foo` + +fn main() {} diff --git a/src/test/compile-fail/issue-21950.rs b/src/test/compile-fail/issue-21950.rs new file mode 100644 index 0000000000000..315a4cd90c5f2 --- /dev/null +++ b/src/test/compile-fail/issue-21950.rs @@ -0,0 +1,20 @@ +// 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. + +// ignore-tidy-linelength + +use std::ops::Add; + +fn main() { + let x = &10 as + //~^ ERROR the value of the associated type `Output` (from the trait `core::ops::Add`) must be specified + &Add; + //~^ ERROR the type parameter `RHS` must be explicitly specified in an object type because its default value `Self` references the type `Self` +} diff --git a/src/test/compile-fail/issue-22034.rs b/src/test/compile-fail/issue-22034.rs new file mode 100644 index 0000000000000..004e33b76a9fe --- /dev/null +++ b/src/test/compile-fail/issue-22034.rs @@ -0,0 +1,19 @@ +// 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. + +extern crate libc; + +fn main() { + let foo: *mut libc::c_void; + let cb: &mut Fn() = unsafe { + &mut *(foo as *mut Fn()) + //~^ ERROR use of possibly uninitialized variable: `foo` + }; +} diff --git a/src/test/compile-fail/issue-4335.rs b/src/test/compile-fail/issue-4335.rs index 55a793f7480a4..0089bff3e8fd8 100644 --- a/src/test/compile-fail/issue-4335.rs +++ b/src/test/compile-fail/issue-4335.rs @@ -15,7 +15,7 @@ fn id(t: T) -> T { t } fn f<'r, T>(v: &'r T) -> Box T + 'r> { // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. id(Box::new(|| *v)) - //~^ ERROR `v` does not live long enough + //~^ ERROR E0373 //~| ERROR cannot move out of borrowed content } diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs index bdebadb2832ca..948dc8cd21968 100644 --- a/src/test/compile-fail/regions-nested-fns-2.rs +++ b/src/test/compile-fail/regions-nested-fns-2.rs @@ -13,7 +13,7 @@ fn ignore(_f: F) where F: for<'z> FnOnce(&'z isize) -> &'z isize {} fn nested() { let y = 3; ignore( - |z| { //~ ERROR `y` does not live long enough + |z| { //~ ERROR E0373 if false { &y } else { z } }); } diff --git a/src/test/run-pass/issue-19097.rs b/src/test/run-pass/issue-19097.rs new file mode 100644 index 0000000000000..ca4b72f9e5bd0 --- /dev/null +++ b/src/test/run-pass/issue-19097.rs @@ -0,0 +1,22 @@ +// Copyright 2014 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. + +// regression test for #19097 + +struct Foo(T); + +impl<'a, T> Foo<&'a T> { + fn foo(&self) {} +} +impl<'a, T> Foo<&'a mut T> { + fn foo(&self) {} +} + +fn main() {}