Skip to content
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

Generalize object and type parameter bounds #192

Closed
wants to merge 2 commits into from

Conversation

Projects
None yet
9 participants
@nikomatsakis
Copy link
Contributor

commented Aug 7, 2014

An RFC summarizing type system changes that close up rust-lang/rust#5723 and related bugs.

region parameters as follows. Within a function or method, we apply
the wellformedness function `WF` to each function or parameter type.
This yields up a set of relations that must hold. The idea here is
that the caller could have type checked unless the types of the

This comment has been minimized.

Copy link
@lilyball

lilyball Aug 7, 2014

Contributor

s/could have/could not have/

// Is<'a> 'a
// IsStatic+Is<'a> 'static+'a
// IsStatic+'a 'static+'a
// IsStatic+Is<'a>+'b 'static,'a,'b

This comment has been minimized.

Copy link
@lilyball

lilyball Aug 7, 2014

Contributor

You defined IsNothing above but don't use it in this list.

@lilyball

This comment has been minimized.

Copy link
Contributor

commented Aug 7, 2014

👍


Currently, the type system is not supposed to allow references to
escape into object types. However, there are various bugs where it
fails to prevent this from hapenning. Moreover, it is very useful (and

This comment has been minimized.

Copy link
@andrew-d

andrew-d Aug 7, 2014

s/hapenning/happening/

You might think that we should simply provide some kind of defaults
that are sensitive to where the `Trait` appears. The same is probably
true of struct type parameters (in other words, `&'a SomeStruct<'a>`
is a very comon pattern).

This comment has been minimized.

Copy link
@andrew-d

andrew-d Aug 7, 2014

s/comon/common/

@Valloric

This comment has been minimized.

Copy link

commented Aug 7, 2014

Related bug: rust-lang/rust#13703. I hit that one all the freaking time. I'm ecstatic to see people are thinking about solutions to this issue.

A *lifetime bound* is written `'a:'b` and it means that "`'a` outlives
`'b`". For example, if `foo` were declared like so:

fn foo<'a, 'b:'a>(...) { ... }

This comment has been minimized.

Copy link
@pnkfelix

pnkfelix Aug 7, 2014

Member

Presentation suggestion: If you write the definition of -:- as 'a:'b (i.e. with formals 'a and 'b), do not immediately follow it with an example that uses 'b:'a (i.e. swapping the arguments), since it is rather easy for someone to overlook that swap and then get very confused by the sentence "the lifetime 'a was shorter than (or equal to) 'b" that immediately follows "'a outlives 'b" above.

Instead, either use 'b:'a in both places, or choose fresh variable names for the concrete example, like

foo<'x, 'y:'x>(...) { ... }

(the latter is my preference).

### At most one explicit lifetime bound is permitted

For simplicity, we permit at most one *explicit* lifetime bound on any
given parameter type. That means that the following function is illegal:

This comment has been minimized.

Copy link
@nrc

nrc Aug 7, 2014

Member

Given that internally the compiler will handle multiple bounds and the syntax must allow for multiple bounds (one lifetime and multiple trait), this restriction seems artificial and not really a simplification at all (in the sense that you are adding a case to a rule).

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Aug 7, 2014

Author Contributor

On Thu, Aug 07, 2014 at 12:53:14AM -0700, Nick Cameron wrote:

Given that internally the compiler will handle multiple bounds and
the syntax must allow for multiple bounds (one lifetime and multiple
trait), this restriction seems artificial and not really a
simplification at all (in the sense that you are adding a case to a
rule).

In the case of parameter types, the restriction is artificial, I
agree. I was also pondering removing it, particularly since -- as you
point out -- inference may infer multiple bounds, so clearly we are
already handling this case.

In the case of object types, however, the story is different, and
there I strongly wish to retain a single "master" bound. the
motivation is that handling multiple bounds was overwhelmingly
complicated to reason about and implement, and I would not want to
remove that. The problem is that in various places you wind up with
constraints of the form all i. exists j. R[i] <= R[j], where R is
a list of regions. This is very challenging for region inference,
since there are many options for it to choose from, and thus inference
is no longer a fixed-point iteration. Moreover, it doesn't seem to add
any particular expressiveness.

The places where this becomes important are:

  • Checking region bounds when data is closed over into an object type
  • Subtyping between object types, which would most naturally be
    contravariant in the region bound

I will add a note to this effect to the RFC.

}
fn foo(a: Box<Writer>, // OK: Sugar for Box<Writer+'a> where 'a fresh
b: &Writer) // OK: Sugar for &'a (Writer+'b) where 'a, 'b fresh

This comment has been minimized.

Copy link
@nrc

nrc Aug 7, 2014

Member

I'm unclear about the scope of the fresh variables here - are the two 'as the same variable or different?

This comment has been minimized.

Copy link
@pnkfelix

pnkfelix Aug 7, 2014

Member

i was assuming this is just like the lifetime elision RFC (i.e. the "normal rules" niko refers to above), no? (And thus the two 'a's are distinct.)

But of course Niko is the one who knows the true answer. :)

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Aug 7, 2014

Author Contributor

On Thu, Aug 07, 2014 at 12:57:10AM -0700, Nick Cameron wrote:

I'm unclear about the scope of the fresh variables here - are the two 'as the same variable or different?

Different. I will clarify.

arena: &'global Arena
}

struct LocalConenxt<'local, 'global:'local> {

This comment has been minimized.

Copy link
@nrc

nrc Aug 7, 2014

Member

typo - LocalConenxt

becomes unclear *why* a particular constraint exists if one must
traverse the type hierarchy deeply to find its origin. This could
potentially be addressed with better error messages, though our track
record for lifetime error messages is not very good so far.

This comment has been minimized.

Copy link
@nrc

nrc Aug 7, 2014

Member

I would have thought that the lifetime restriction given by the type &'a T (i.e., that 'a must outlive T) is so fundamental to Rust that it is always safe to infer, even from type definitions.

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Aug 7, 2014

Author Contributor

On Thu, Aug 07, 2014 at 01:05:47AM -0700, Nick Cameron wrote:

I would have thought that the lifetime restriction given by the type
&'a T (i.e., that 'a must outlive T) is so fundamental to Rust
that it is always safe to infer, even from type definitions.

There is a good case to be made that we should infer these, yes.
There is also a good case to be made that we should provide some
default behavior so that people can write Box<Object> and &'a Object in type definitions and get Box<Object+'static> and &'a Object+'a. These two things are potentially in conflict. Therefore, I
listed them both in the unresolved questions section. I will add a
note also to describe why I see them as potentially in conflict.

However, there are complications:

- What about a type like `struct Ref<'a, T> { x: &'a T }`? `Ref<'a, Trait>`
should really work the same way as `&'a Trait`.

This comment has been minimized.

Copy link
@nrc

nrc Aug 7, 2014

Member

I guess I think that we can special case & at least because it is a special case in general (it even has special syntax), I wouldn't necessarily expect to apply the same rules to Ref. I agree Box is tricky since it should behave like a regular struct. (Although having to rarely have a redundant lifetime seems a price worth paying for simplifying the common case, unless I misunderstand the complications here).

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Aug 7, 2014

Author Contributor

On Thu, Aug 07, 2014 at 04:08:45AM -0700, Nick Cameron wrote:

I guess I think that we can special case & at least because it is a special case in general (it even has special syntax), I wouldn't necessarily expect to apply the same rules to Ref. I agree Box is tricky since it should behave like a regular struct. (Although having to rarely have a redundant lifetime seems a price worth paying for simplifying the common case, unless I misunderstand the complications here).

I have been using Box as a standin for "a regular struct that does
not implicitly contain borrowed data", basically. So I would expect
e.g. Box and Rc (and Vec) to behave the same with respect to
their type arguments. My bigger point here was that if you said, for
example, that we would apply a 'static default as the lifetime bound
for object types that appear in a type argument, it would do an
annoying (and almost certainly wrong) thing for Ref.

It is also mildly unclear to me whether it makes sense for there to be
one default for the lifetime bound on an object type and a distinct
set of defaults for lifetime parameters. In other words, should people
have to write &'a SomeStruct<'a> or should the latter 'a also be a
default (presuming we did do defaulting for traits)?

trait Writer { /* no derivable bounds */ }
struct Foo<'a> {
Box<Writer>, // Error: try Box<Writer+'static> or Box<Writer+'a>
Box<Writer+Send>, // OK: Send implies 'static

This comment has been minimized.

Copy link
@Kimundi

Kimundi Aug 7, 2014

Member

Would this work by having Send inherit from 'static?
Related, would something like like Box<Any> with trait Any : 'static {} imply Box<Any+'static>, or would that still have to be written explicitly?

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Aug 7, 2014

Author Contributor

On Thu, Aug 07, 2014 at 08:02:38AM -0700, Marvin Löbel wrote:

Would this work by having Send inherit from 'static?

Yes.

Related, would something like like Box<Any> with trait Any : 'static {} imply Box<Any+'static>, or would that still have to be written explicitly?

Yes, if trait Any : 'static, then Box<Any> is sufficient.

@brson

This comment has been minimized.

Copy link
Contributor

commented Aug 12, 2014

@brson brson closed this Aug 12, 2014

@glaebhoerl

This comment has been minimized.

Copy link
Contributor

commented Aug 13, 2014

Man, this was fast. I hadn't even read it yet. But now that I have I don't have anything to add, other than +1. (I haven't fully understood the tension between the various forms of inference the RFC doesn't propose doing, but not inferring is safely forwards-compatible and can be liberalized later, so I agree with this as well, even without understanding it yet.)

There's one thing I've been thinking about however, and this seems like an appropriate place to ask about it:

Is there ever a use case for lifetime parameters on types to be anything other than contravariant? As a subcase: is it ever preferable to have the lifetimes on (higher-order) function parameters be fixed from outside, instead of higher-rank?

E.g., do you ever want something like:

struct Foo<'a> {
    f: fn(&'a Bar) -> ...;
}

instead of

struct Foo {
    f: <'a> fn(&'a Bar) -> ...;
}

?

The reason I'm wondering is that if we can declare that lifetime parameters on types are always contravariant, then we can avoid the need for any kind of variance inference or annotations, which would be very nice. (This would also nicely plug the hole in lifetime elision w.r.t. covariant lifetimes: if covariant lifetimes on types are impossible, then avoiding their elision is trivial.)

My "grand plan" would also involve making type parameters be always invariant , and using Transmute/Coercible any time you want to safely change the types or lifetimes of inner types (type arguments). With the new 'a: 'b bounds the relevant impls could even be written in the language itself, instead of being magically wired-in: impl<'a, 'b: 'a, T> Transmute<&'a T> for &'b T { } (and likewise for &mut).

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

commented Aug 14, 2014

On Wed, Aug 13, 2014 at 09:11:05AM -0700, Gábor Lehel wrote:

E.g., do you ever want something like:

struct Foo<'a> {
    f: fn(&'a Bar) -> ...;
}

instead of

struct Foo {
    f: <'a> fn(&'a Bar) -> ...;
}

This is supported and will be inferred to be covariant. However, this has seen very little use.

The reason I'm wondering is that if we can declare that lifetime parameters on types are always contravariant, then we can avoid the need for any kind of variance inference or annotations, which would be very nice. (This would also nicely plug the hole in lifetime elision w.r.t. covariant lifetimes: if covariant lifetimes on types are impossible then avoiding their elision is trivial.)

No, we cannot do this, there are definitely times when lifetime
parameters must be invariant. For example, 'b in a struct definition
like:

struct Foo<'a,'b:'a> {
    foo: &'a mut Foo<'b>
}

However, I have been contemplating a plan to make:

  • lifetime parameters contravariant by default
  • type parameters covariant by default in types
  • a mut prefix to declare invariance

I wanted to gather some data and was hoping to do it in the next few days.

Obviously this plan loses some expressiveness, though nothing that
couldn't be regained with some more potential annotations. One problem
with this plan is what to do about associated types (today: trait type
parameters -- which reminds me, I need to make a note on the RFC).
With the move to unboxed closures, fn types generally become object
types, and hence we may be unsatisfied without contravariance and
covariance. And it's not clear to me that the transmute plan below can
address this.

My "grand plan" would also involve making type parameters be always invariant , and using Transmute/Coercible any time you want to safely change the types or lifetimes of inner types (type arguments). With the new 'a: 'b bounds the relevant impls could even be written in the language itself, instead of being magically wired-in: impl<'a, 'b: 'a, T> Transmute<&'a T> for &'b T { } (and likewise for &mut).

I guess it'd be worth experimenting a bit. The last few times I
investigated "removing subtyping" it wound up either breaking a lot of
code or essentially being reintroduced through increasingly elaborate
coercion rules. I will note that I frequently encounter problems
because Option<&'a T> is (currently) invariant with respect to &'a T. Aggressive use of the Transmute relation would likely help here
in many cases, though it adds its own complications around inference.

bors added a commit to rust-lang/rust that referenced this pull request Aug 15, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 16, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 17, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 23, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 23, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 23, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 24, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 25, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 25, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 26, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 26, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 28, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 28, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 28, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.

bors added a commit to rust-lang/rust that referenced this pull request Aug 28, 2014

auto merge of #16453 : nikomatsakis/rust/type-bounds-3, r=pcwalton
Implements rust-lang/rfcs#192.

In particular:

1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds.
2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound.
3. all types must be well-formed.
4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long".

This is a [breaking-change]. The most common things you have to fix after this change are:

1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`.
2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`.
2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.)

The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on.

Closes #5723.
@pnkfelix

This comment has been minimized.

@gereeter gereeter referenced this pull request Sep 6, 2014

Closed

Trait based inheritance #223

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.