Skip to content

librustc: Make &const [T] mean the same thing as &[const T] #5110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -1610,11 +1610,11 @@ The following are examples of structure expressions:
~~~~
# struct Point { x: float, y: float }
# struct TuplePoint(float, float);
# mod game { pub struct User { name: &str, age: uint, mut score: uint } }
# mod game { pub struct User { name: &str, age: uint, score: uint } }
# use game;
Point {x: 10f, y: 20f};
TuplePoint(10f, 20f);
let u = game::User {name: "Joe", age: 35u, mut score: 100_000};
let u = game::User {name: "Joe", age: 35u, score: 100_000};
~~~~

A structure expression forms a new value of the named structure type.
Expand Down
161 changes: 4 additions & 157 deletions doc/tutorial-borrowed-ptr.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,12 @@ mutations:
~~~ {.xfail-test}
fn example3() -> int {
struct R { g: int }
struct S { mut f: ~R }
struct S { f: ~R }

let mut x = ~S {mut f: ~R {g: 3}};
let mut x = ~S {f: ~R {g: 3}};
let y = &x.f.g;
x = ~S {mut f: ~R {g: 4}}; // Error reported here.
x.f = ~R {g: 5}; // Error reported here.
x = ~S {f: ~R {g: 4}}; // Error reported here.
x.f = ~R {g: 5}; // Error reported here.
*y
}
~~~
Expand All @@ -362,91 +362,6 @@ In this case, two errors are reported, one when the variable `x` is
modified and another when `x.f` is modified. Either modification would
invalidate the pointer `y`.

Things get trickier when the unique box is not uniquely owned by the
stack frame, or when there is no way for the compiler to determine the
box's owner. Consider a program like this:

~~~ {.xfail-test}
struct R { g: int }
struct S { mut f: ~R }
fn example5a(x: @S, callback: @fn()) -> int {
let y = &x.f.g; // Error reported here.
...
callback();
...
# return 0;
}
~~~

Here the heap looks something like:

~~~ {.notrust}
Stack Managed Heap Exchange Heap

x +------+ +-------------+ +------+
| @... | ----> | mut f: ~... | --+-> | g: 3 |
y +------+ +-------------+ | +------+
| &int | -------------------------+
+------+
~~~

In this case, the owning reference to the value being borrowed is
`x.f`. Moreover, `x.f` is both mutable and *aliasable*. Aliasable
means that there may be other pointers to that same managed box, so
even if the compiler were to prove an absence of mutations to `x.f`,
code could mutate `x.f` indirectly by changing an alias of
`x`. Therefore, to be safe, the compiler only accepts *pure* actions
during the lifetime of `y`. We define what "pure" means in the section
on [purity](#purity).

Besides ensuring purity, the only way to borrow the interior of a
unique found in aliasable memory is to ensure that the borrowed field
itself is also unique, as in the following example:

~~~
struct R { g: int }
struct S { f: ~R }
fn example5b(x: @S) -> int {
let y = &x.f.g;
...
# return 0;
}
~~~

Here, the field `f` is not declared as mutable. But that is enough for
the compiler to know that, even if aliases to `x` exist, the field `f`
cannot be changed and hence the unique box `g` will remain valid.

If you do have a unique box in a mutable field, and you wish to borrow
it, one option is to use the swap operator to move that unique box
onto your stack:

~~~
struct R { g: int }
struct S { mut f: ~R }
fn example5c(x: @S) -> int {
let mut v = ~R {g: 0};
v <-> x.f; // Swap v and x.f
{ // Block constrains the scope of `y`:
let y = &v.g;
...
}
x.f = v; // Replace x.f
...
# return 0;
}
~~~

Of course, this has the side effect of modifying your managed box for
the duration of the borrow, so it only works when you know that you
won't be accessing that same box for the duration of the loan. Also,
it is sometimes necessary to introduce additional blocks to constrain
the scope of the loan. In this example, the borrowed pointer `y`
would still be in scope when you moved the value `v` back into `x.f`,
and hence moving `v` would be considered illegal. You cannot move
values if they are the targets of valid outstanding loans. Introducing
the block restricts the scope of `y`, making the move legal.

# Borrowing and enums

The previous example showed that the type system forbids any borrowing
Expand Down Expand Up @@ -558,11 +473,6 @@ permit `ref` bindings into data owned by the stack frame even if the
data are mutable, but otherwise it requires that the data reside in
immutable memory.

> ***Note:*** Right now, pattern bindings not explicitly annotated
> with `ref` or `copy` use a special mode of "implicit by reference".
> This is changing as soon as we finish updating all the existing code
> in the compiler that relies on the current settings.

# Returning borrowed pointers

So far, all of the examples we've looked at use borrowed pointers in a
Expand Down Expand Up @@ -745,69 +655,6 @@ fn select<T>(shape: &Shape, threshold: float,

This is equivalent to the previous definition.

# Purity

As mentioned before, the Rust compiler offers a kind of escape hatch
that permits borrowing of any data, as long as the actions that occur
during the lifetime of the borrow are pure. Pure actions are those
that only modify data owned by the current stack frame. The compiler
can therefore permit arbitrary pointers into the heap, secure in the
knowledge that no pure action will ever cause them to become
invalidated (the compiler must still track data on the stack which is
borrowed and enforce those rules normally, of course). A pure function
in Rust is referentially transparent: it returns the same results
given the same (observably equivalent) inputs. That is because while
pure functions are allowed to modify data, they may only modify
*stack-local* data, which cannot be observed outside the scope of the
function itself. (Using an `unsafe` block invalidates this guarantee.)

Let’s revisit a previous example and show how purity can affect
typechecking. Here is `example5a()`, which borrows the interior of a
unique box found in an aliasable, mutable location, only now we’ve
replaced the `...` with some specific code:

~~~
struct R { g: int }
struct S { mut f: ~R }
fn example5a(x: @S ...) -> int {
let y = &x.f.g; // Unsafe
*y + 1
}
~~~

The new code simply returns an incremented version of `y`. This code
clearly doesn't mutate the heap, so the compiler is satisfied.

But suppose we wanted to pull the increment code into a helper, like
this:

~~~
fn add_one(x: &int) -> int { *x + 1 }
~~~

We can now update `example5a()` to use `add_one()`:

~~~
# struct R { g: int }
# struct S { mut f: ~R }
# pure fn add_one(x: &int) -> int { *x + 1 }
fn example5a(x: @S ...) -> int {
let y = &x.f.g;
add_one(y) // Error reported here
}
~~~

But now the compiler will report an error again. The reason is that it
only considers one function at a time (like most typecheckers), and
so it does not know that `add_one()` consists of pure code. We can
help the compiler by labeling `add_one()` as pure:

~~~
pure fn add_one(x: &int) -> int { *x + 1 }
~~~

With this change, the modified version of `example5a()` will again compile.

# Conclusion

So there you have it: a (relatively) brief tour of the borrowed pointer
Expand Down
14 changes: 7 additions & 7 deletions doc/tutorial-ffi.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,21 +220,21 @@ extern mod std;
use libc::c_ulonglong;

struct timeval {
mut tv_sec: c_ulonglong,
mut tv_usec: c_ulonglong
tv_sec: c_ulonglong,
tv_usec: c_ulonglong
}

#[nolink]
extern mod lib_c {
fn gettimeofday(tv: *timeval, tz: *()) -> i32;
fn gettimeofday(tv: *mut timeval, tz: *()) -> i32;
}
fn unix_time_in_microseconds() -> u64 {
unsafe {
let x = timeval {
mut tv_sec: 0 as c_ulonglong,
mut tv_usec: 0 as c_ulonglong
let mut x = timeval {
tv_sec: 0 as c_ulonglong,
tv_usec: 0 as c_ulonglong
};
lib_c::gettimeofday(ptr::addr_of(&x), ptr::null());
lib_c::gettimeofday(&mut x, ptr::null());
return (x.tv_sec as u64) * 1000_000_u64 + (x.tv_usec as u64);
}
}
Expand Down
27 changes: 12 additions & 15 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,19 +583,16 @@ Inherited mutability means that any field of a struct may be mutable, if the
struct is in a mutable slot (or a field of a struct in a mutable slot, and
so forth).

A struct that is not mutable due to inherited mutability may declare some
of its fields nevertheless mutable, using the `mut` keyword.

~~~~
struct Stack {
content: ~[int],
mut head: uint
head: uint
}
~~~~

With a value of such a type, you can do `mystack.head += 1`. If `mut` were
omitted from the type, such an assignment to a struct without inherited
mutability would result in a type error.
With a value (say, `mystack`) of such a type in a mutable location, you can do
`mystack.head += 1`. But in an immutable location, such an assignment to a
struct without inherited mutability would result in a type error.

`match` patterns destructure structs. The basic syntax is
`Name { fieldname: pattern, ... }`:
Expand Down Expand Up @@ -938,19 +935,19 @@ type that contains managed boxes or other managed types.
~~~
// A linked list node
struct Node {
mut next: MaybeNode,
mut prev: MaybeNode,
next: MaybeNode,
prev: MaybeNode,
payload: int
}

enum MaybeNode {
SomeNode(@Node),
SomeNode(@mut Node),
NoNode
}

let node1 = @Node { next: NoNode, prev: NoNode, payload: 1 };
let node2 = @Node { next: NoNode, prev: NoNode, payload: 2 };
let node3 = @Node { next: NoNode, prev: NoNode, payload: 3 };
let node1 = @mut Node { next: NoNode, prev: NoNode, payload: 1 };
let node2 = @mut Node { next: NoNode, prev: NoNode, payload: 2 };
let node3 = @mut Node { next: NoNode, prev: NoNode, payload: 3 };

// Link the three list nodes together
node1.next = SomeNode(node2);
Expand Down Expand Up @@ -2300,8 +2297,8 @@ mod farm {
# impl Human { fn rest(&self) { } }
# pub fn make_me_a_farm() -> farm::Farm { farm::Farm { chickens: ~[], cows: ~[], farmer: Human(0) } }
pub struct Farm {
priv mut chickens: ~[Chicken],
priv mut cows: ~[Cow],
priv chickens: ~[Chicken],
priv cows: ~[Cow],
farmer: Human
}

Expand Down
4 changes: 2 additions & 2 deletions src/libstd/cell.rs → src/libcore/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::option;
use core::prelude::*;
use option;
use prelude::*;

/// A dynamic, mutable location.
///
Expand Down
2 changes: 2 additions & 0 deletions src/libcore/core.rc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Implicitly, all crates behave as if they included the following prologue:
#[warn(vecs_implicitly_copyable)];
#[deny(non_camel_case_types)];
#[allow(deprecated_self)];
#[allow(deprecated_mutable_fields)];

/* The Prelude. */

Expand Down Expand Up @@ -142,6 +143,7 @@ pub mod dlist;
#[path="iter-trait.rs"] #[merge = "iter-trait/dlist.rs"]
pub mod dlist_iter;
pub mod hashmap;
pub mod cell;


/* Tasks and communication */
Expand Down
17 changes: 7 additions & 10 deletions src/libcore/pipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ bounded and unbounded protocols allows for less code duplication.

use cmp::Eq;
use cast::{forget, reinterpret_cast, transmute};
use cell::Cell;
use either::{Either, Left, Right};
use kinds::Owned;
use libc;
Expand Down Expand Up @@ -917,11 +918,9 @@ pub fn spawn_service<T:Owned,Tb:Owned>(

// This is some nasty gymnastics required to safely move the pipe
// into a new task.
let server = ~mut Some(server);
do task::spawn || {
let mut server_ = None;
server_ <-> *server;
service(option::unwrap(server_))
let server = Cell(server);
do task::spawn {
service(server.take());
}

client
Expand All @@ -941,11 +940,9 @@ pub fn spawn_service_recv<T:Owned,Tb:Owned>(

// This is some nasty gymnastics required to safely move the pipe
// into a new task.
let server = ~mut Some(server);
do task::spawn || {
let mut server_ = None;
server_ <-> *server;
service(option::unwrap(server_))
let server = Cell(server);
do task::spawn {
service(server.take())
}

client
Expand Down
Loading