clean up some things

I double checked that everything is here and in the correct order; this fixes things up
This commit is contained in:
Steve Klabnik 2017-02-15 17:55:02 -05:00
parent c937254357
commit 287a3457bc
4 changed files with 381 additions and 44 deletions

View File

@ -630,7 +630,7 @@ unsafe fn bump_levels_unsafe2() -> u32 {
Mutable statics have the same restrictions as normal statics, except that the
type of the value is not required to ascribe to `Sync`.
### `'static` lifetime elision
#### `'static` lifetime elision
[Unstable] Both constant and static declarations of reference types have
*implicit* `'static` lifetimes unless an explicit lifetime is specified. As
@ -676,3 +676,372 @@ const RESOLVED_MULTIPLE: Fn(&Foo, &Bar, &Baz) -> usize = ..
// `Fn(&'static Foo, &'static Bar) -> &'static Baz`.
const RESOLVED_STATIC: Fn(&Foo, &Bar) -> &Baz = ..
```
### Traits
A _trait_ describes an abstract interface that types can
implement. This interface consists of associated items, which come in
three varieties:
- functions
- constants
- types
Associated functions whose first parameter is named `self` are called
methods and may be invoked using `.` notation (e.g., `x.foo()`).
All traits define an implicit type parameter `Self` that refers to
"the type that is implementing this interface". Traits may also
contain additional type parameters. These type parameters (including
`Self`) may be constrained by other traits and so forth as usual.
Trait bounds on `Self` are considered "supertraits". These are
required to be acyclic. Supertraits are somewhat different from other
constraints in that they affect what methods are available in the
vtable when the trait is used as a [trait object](#trait-objects).
Traits are implemented for specific types through separate
[implementations](#implementations).
Consider the following trait:
```
# type Surface = i32;
# type BoundingBox = i32;
trait Shape {
fn draw(&self, Surface);
fn bounding_box(&self) -> BoundingBox;
}
```
This defines a trait with two methods. All values that have
[implementations](#implementations) of this trait in scope can have their
`draw` and `bounding_box` methods called, using `value.bounding_box()`
[syntax](#method-call-expressions).
Traits can include default implementations of methods, as in:
```
trait Foo {
fn bar(&self);
fn baz(&self) { println!("We called baz."); }
}
```
Here the `baz` method has a default implementation, so types that implement
`Foo` need only implement `bar`. It is also possible for implementing types
to override a method that has a default implementation.
Type parameters can be specified for a trait to make it generic. These appear
after the trait name, using the same syntax used in [generic
functions](#generic-functions).
```
trait Seq<T> {
fn len(&self) -> u32;
fn elt_at(&self, n: u32) -> T;
fn iter<F>(&self, F) where F: Fn(T);
}
```
It is also possible to define associated types for a trait. Consider the
following example of a `Container` trait. Notice how the type is available
for use in the method signatures:
```
trait Container {
type E;
fn empty() -> Self;
fn insert(&mut self, Self::E);
}
```
In order for a type to implement this trait, it must not only provide
implementations for every method, but it must specify the type `E`. Here's
an implementation of `Container` for the standard library type `Vec`:
```
# trait Container {
# type E;
# fn empty() -> Self;
# fn insert(&mut self, Self::E);
# }
impl<T> Container for Vec<T> {
type E = T;
fn empty() -> Vec<T> { Vec::new() }
fn insert(&mut self, x: T) { self.push(x); }
}
```
Generic functions may use traits as _bounds_ on their type parameters. This
will have two effects:
- Only types that have the trait may instantiate the parameter.
- Within the generic function, the methods of the trait can be
called on values that have the parameter's type.
For example:
```
# type Surface = i32;
# trait Shape { fn draw(&self, Surface); }
fn draw_twice<T: Shape>(surface: Surface, sh: T) {
sh.draw(surface);
sh.draw(surface);
}
```
Traits also define a [trait object](#trait-objects) with the same
name as the trait. Values of this type are created by coercing from a
pointer of some specific type to a pointer of trait type. For example,
`&T` could be coerced to `&Shape` if `T: Shape` holds (and similarly
for `Box<T>`). This coercion can either be implicit or
[explicit](#type-cast-expressions). Here is an example of an explicit
coercion:
```
trait Shape { }
impl Shape for i32 { }
let mycircle = 0i32;
let myshape: Box<Shape> = Box::new(mycircle) as Box<Shape>;
```
The resulting value is a box containing the value that was cast, along with
information that identifies the methods of the implementation that was used.
Values with a trait type can have [methods called](#method-call-expressions) on
them, for any method in the trait, and can be used to instantiate type
parameters that are bounded by the trait.
Trait methods may be static, which means that they lack a `self` argument.
This means that they can only be called with function call syntax (`f(x)`) and
not method call syntax (`obj.f()`). The way to refer to the name of a static
method is to qualify it with the trait name, treating the trait name like a
module. For example:
```
trait Num {
fn from_i32(n: i32) -> Self;
}
impl Num for f64 {
fn from_i32(n: i32) -> f64 { n as f64 }
}
let x: f64 = Num::from_i32(42);
```
Traits may inherit from other traits. Consider the following example:
```
trait Shape { fn area(&self) -> f64; }
trait Circle : Shape { fn radius(&self) -> f64; }
```
The syntax `Circle : Shape` means that types that implement `Circle` must also
have an implementation for `Shape`. Multiple supertraits are separated by `+`,
`trait Circle : Shape + PartialEq { }`. In an implementation of `Circle` for a
given type `T`, methods can refer to `Shape` methods, since the typechecker
checks that any type with an implementation of `Circle` also has an
implementation of `Shape`:
```rust
struct Foo;
trait Shape { fn area(&self) -> f64; }
trait Circle : Shape { fn radius(&self) -> f64; }
impl Shape for Foo {
fn area(&self) -> f64 {
0.0
}
}
impl Circle for Foo {
fn radius(&self) -> f64 {
println!("calling area: {}", self.area());
0.0
}
}
let c = Foo;
c.radius();
```
In type-parameterized functions, methods of the supertrait may be called on
values of subtrait-bound type parameters. Referring to the previous example of
`trait Circle : Shape`:
```
# trait Shape { fn area(&self) -> f64; }
# trait Circle : Shape { fn radius(&self) -> f64; }
fn radius_times_area<T: Circle>(c: T) -> f64 {
// `c` is both a Circle and a Shape
c.radius() * c.area()
}
```
Likewise, supertrait methods may also be called on trait objects.
```{.ignore}
# trait Shape { fn area(&self) -> f64; }
# trait Circle : Shape { fn radius(&self) -> f64; }
# impl Shape for i32 { fn area(&self) -> f64 { 0.0 } }
# impl Circle for i32 { fn radius(&self) -> f64 { 0.0 } }
# let mycircle = 0i32;
let mycircle = Box::new(mycircle) as Box<Circle>;
let nonsense = mycircle.radius() * mycircle.area();
```
### Implementations
An _implementation_ is an item that implements a [trait](#traits) for a
specific type.
Implementations are defined with the keyword `impl`.
```
# #[derive(Copy, Clone)]
# struct Point {x: f64, y: f64};
# type Surface = i32;
# struct BoundingBox {x: f64, y: f64, width: f64, height: f64};
# trait Shape { fn draw(&self, Surface); fn bounding_box(&self) -> BoundingBox; }
# fn do_draw_circle(s: Surface, c: Circle) { }
struct Circle {
radius: f64,
center: Point,
}
impl Copy for Circle {}
impl Clone for Circle {
fn clone(&self) -> Circle { *self }
}
impl Shape for Circle {
fn draw(&self, s: Surface) { do_draw_circle(s, *self); }
fn bounding_box(&self) -> BoundingBox {
let r = self.radius;
BoundingBox {
x: self.center.x - r,
y: self.center.y - r,
width: 2.0 * r,
height: 2.0 * r,
}
}
}
```
It is possible to define an implementation without referring to a trait. The
methods in such an implementation can only be used as direct calls on the values
of the type that the implementation targets. In such an implementation, the
trait type and `for` after `impl` are omitted. Such implementations are limited
to nominal types (enums, structs, trait objects), and the implementation must
appear in the same crate as the `self` type:
```
struct Point {x: i32, y: i32}
impl Point {
fn log(&self) {
println!("Point is at ({}, {})", self.x, self.y);
}
}
let my_point = Point {x: 10, y:11};
my_point.log();
```
When a trait _is_ specified in an `impl`, all methods declared as part of the
trait must be implemented, with matching types and type parameter counts.
An implementation can take type parameters, which can be different from the
type parameters taken by the trait it implements. Implementation parameters
are written after the `impl` keyword.
```
# trait Seq<T> { fn dummy(&self, _: T) { } }
impl<T> Seq<T> for Vec<T> {
/* ... */
}
impl Seq<bool> for u32 {
/* Treat the integer as a sequence of bits */
}
```
### External blocks
External blocks form the basis for Rust's foreign function interface.
Declarations in an external block describe symbols in external, non-Rust
libraries.
Functions within external blocks are declared in the same way as other Rust
functions, with the exception that they may not have a body and are instead
terminated by a semicolon.
Functions within external blocks may be called by Rust code, just like
functions defined in Rust. The Rust compiler automatically translates between
the Rust ABI and the foreign ABI.
Functions within external blocks may be variadic by specifying `...` after one
or more named arguments in the argument list:
```ignore
extern {
fn foo(x: i32, ...);
}
```
A number of [attributes](#ffi-attributes) control the behavior of external blocks.
By default external blocks assume that the library they are calling uses the
standard C ABI on the specific platform. Other ABIs may be specified using an
`abi` string, as shown here:
```ignore
// Interface to the Windows API
extern "stdcall" { }
```
There are three ABI strings which are cross-platform, and which all compilers
are guaranteed to support:
* `extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
Rust code.
* `extern "C"` -- This is the same as `extern fn foo()`; whatever the default
your C compiler supports.
* `extern "system"` -- Usually the same as `extern "C"`, except on Win32, in
which case it's `"stdcall"`, or what you should use to link to the Windows API
itself
There are also some platform-specific ABI strings:
* `extern "cdecl"` -- The default for x86\_32 C code.
* `extern "stdcall"` -- The default for the Win32 API on x86\_32.
* `extern "win64"` -- The default for C code on x86\_64 Windows.
* `extern "sysv64"` -- The default for C code on non-Windows x86\_64.
* `extern "aapcs"` -- The default for ARM.
* `extern "fastcall"` -- The `fastcall` ABI -- corresponds to MSVC's
`__fastcall` and GCC and clang's `__attribute__((fastcall))`
* `extern "vectorcall"` -- The `vectorcall` ABI -- corresponds to MSVC's
`__vectorcall` and clang's `__attribute__((vectorcall))`
Finally, there are some rustc-specific ABI strings:
* `extern "rust-intrinsic"` -- The ABI of rustc intrinsics.
* `extern "rust-call"` -- The ABI of the Fn::call trait functions.
* `extern "platform-intrinsic"` -- Specific platform intrinsics -- like, for
example, `sqrt` -- have this ABI. You should never have to deal with it.
The `link` attribute allows the name of the library to be specified. When
specified the compiler will attempt to link against the native library of the
specified name.
```{.ignore}
#[link(name = "crypto")]
extern { }
```
The type of a function declared in an extern block is `extern "abi" fn(A1, ...,
An) -> R`, where `A1...An` are the declared types of its arguments and `R` is
the declared return type.
It is valid to add the `link` attribute on an empty extern block. You can use
this to satisfy the linking requirements of extern blocks elsewhere in your code
(including upstream crates) instead of adding the attribute to each extern block.

View File

@ -8,17 +8,3 @@ discipline, exist in the standard library.
Allocations in the stack consist of *variables*, and allocations in the heap
consist of *boxes*.
## Memory allocation and lifetime
The _items_ of a program are those functions, modules and types that have their
value calculated at compile-time and stored uniquely in the memory image of the
rust process. Items are neither dynamically allocated nor freed.
The _heap_ is a general term that describes boxes. The lifetime of an
allocation in the heap depends on the lifetime of the box values pointing to
it. Since box values may themselves be passed in and out of frames, or stored
in the heap, heap allocations may outlive the frame they are allocated within.
An allocation in the heap is guaranteed to reside at a single location in the
heap for the whole lifetime of the allocation - it will never be relocated as
a result of moving a box value.

View File

@ -303,3 +303,14 @@ The representation semantics of floating-point numbers are described in
### Boolean literals
The two values of the boolean type are written `true` and `false`.
## Symbols
Symbols are a general class of printable [tokens](#tokens) that play structural
roles in a variety of grammar productions. They are a
set of remaining miscellaneous printable tokens that do not
otherwise appear as [unary operators](#unary-operator-expressions), [binary
operators](#binary-operator-expressions), or [keywords][keywords].
They are catalogued in [the Symbols section][symbols] of the Grammar document.
[symbols]: grammar.html#symbols

View File

@ -9,32 +9,3 @@ Rust:
- Dereferencing a [raw pointer](#pointer-types).
- Reading or writing a [mutable static variable](#mutable-statics).
- Calling an unsafe function (including an intrinsic or foreign function).
## Unsafe functions
Unsafe functions are functions that are not safe in all contexts and/or for all
possible inputs. Such a function must be prefixed with the keyword `unsafe` and
can only be called from an `unsafe` block or another `unsafe` function.
## Unsafe blocks
A block of code can be prefixed with the `unsafe` keyword, to permit calling
`unsafe` functions or dereferencing raw pointers within a safe function.
When a programmer has sufficient conviction that a sequence of potentially
unsafe operations is actually safe, they can encapsulate that sequence (taken
as a whole) within an `unsafe` block. The compiler will consider uses of such
code safe, in the surrounding context.
Unsafe blocks are used to wrap foreign libraries, make direct use of hardware
or implement features not directly present in the language. For example, Rust
provides the language features necessary to implement memory-safe concurrency
in the language but the implementation of threads and message passing is in the
standard library.
Rust's type system is a conservative approximation of the dynamic safety
requirements, so in some cases there is a performance cost to using safe code.
For example, a doubly-linked list is not a tree structure and can only be
represented with reference-counted pointers in safe code. By using `unsafe`
blocks to represent the reverse links as raw pointers, it can be implemented
with only boxes.