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

Introduce variance for type parameters, not just region parameters #233

Closed
wants to merge 2 commits into from

Conversation

nikomatsakis
Copy link
Contributor

In short, Option<&'static T> ought to be usable where Option<&'a T> is expected. These changes have been fully implemented.

Rendered view.

@sfackler
Copy link
Member

Another potential alternative to the lint would be to to forbid unused type and lifetime parameters completely and add BivariantType and BivariantLifetime markers. I think it'd be a bit more consistent than the situation of using markers unless you want bivariance, in which case you use #[allow(bivariance)] or whatever the lint would be called.

@nikomatsakis
Copy link
Contributor Author

Good point. I meant to include that as an option. I agree it's
cleaner that way. In fact that is what I began to implement,
but I found it mildly annoying to mess up the pretty inference
code to account for a distinction between "implicit" bivariance
and "explicit" bivariance. So then I made it an error. Later I
relented and decided a lint might be better, but I'm not
actually sure why in retrospect (there are no known types that
would want to allow bivariance -- I think I used it in a test
or two where I was too lazy to patch it up). Perhaps I'll just
modify the RFC/code to make it a hard error again and avoid the
question for now.

@reem
Copy link

reem commented Sep 11, 2014

Perhaps I am misunderstanding, but this would forbid things like struct Phantom<T>; and friends, (basically most uses of phantom types), right? That strikes me as not perfectly ideal, as phantom types enable some particularly nice type-directed patterns.

@nikomatsakis
Copy link
Contributor Author

Ah, I remember now why I opted for the lint -- because in some
cases, such as traits, there is no place to put an explicit
marker:

trait Foo<T> { } // <-- if I want a bivariant T, where do I

put a marker?

Of course, this could also be interpreted as an argument
against markers in the first place, since not only are they
kind of annoying, they can't express the desired variance of
T here (one must instead add methods that reference T).

@nikomatsakis
Copy link
Contributor Author

It would require that phantom types be written with a marker,
yes. This is not the same as forbidding them, but they do
become less attractive:

struct Phantom<T>(marker::InvariantType<T>)

This could be avoided by having some sort of default choice (as
I proposed previously).

On Wed, Sep 10, 2014, at 05:40 PM, Jonathan Reem wrote:

Perhaps I am misunderstanding, but this would forbid things
like struct Phantom; and friends, (basically most uses of
phantom types), right? That strikes me as not perfectly
ideal, as phantom types enable some particularly nice
type-directed patterns.


Reply to this email directly or [1]view it on GitHub.

References

  1. Introduce variance for type parameters, not just region parameters #233 (comment)

@reem
Copy link

reem commented Sep 11, 2014

That's an important use-case too, rust-typemap, for instance, has a phantom type in the Assoc trait, which is just trait Assoc<Value> {} and it would be very inconvenient if that was just an error and there was no way around it.

@nikomatsakis
Copy link
Contributor Author

Yes, the workaround for traits is particularly bad:

trait Assoc<Value> { fn invariant(Value) -> Value; }

On Wed, Sep 10, 2014, at 05:52 PM, Jonathan Reem wrote:

That's an important use-case too, rust-typemap, for
instance, has a phantom type in the Assoc trait, which is
just trait Assoc {} and it would be very inconvenient
if that was just an error and there was no way around it.


Reply to this email directly or [1]view it on GitHub.

References

  1. Introduce variance for type parameters, not just region parameters #233 (comment)

@pcwalton
Copy link
Contributor

Given the choice between phantom lifetimes in traits work and having Option<&'static T> work right I'd much rather have the latter.

@reem
Copy link

reem commented Sep 11, 2014

Are phantom lifetimes in traits even possible with this proposal?

@nikomatsakis
Copy link
Contributor Author

So, @pnkfelix and I were chatting over IRC, and we realize that we can use where clauses to specify variance in all cases in a much nicer way than markers, without building things into the syntax. The idea is basically to use marker traits. These traits would be implemented for all types, and would be known to the variance analysis, just as marker types are today. We could either choose to make them an exception -- that is, the only traits for which Self is not invariant -- or else make Self variant like other type parameters.

Anyway, long story short you would write things like the following:

struct Phantom<T> where T : Invariant;
trait Phantom<T> where T : Invariant { ... }

Since traits only apply to types, you can specify the variance of a lifetime parameter by "packaging it up" in a synthetic reference type:

struct Items<'a, T>
    where &'a T : Covariant // 'a will be contravariant
{ ... }

This is arguably more natural, since you don't have to think about the fact that lifetimes are naturally "contravariant".

Plausibly we could rename these traits to something else. I was thinking Input and Output, and then invariant would just be Input+Output, except that those as traditionally used correspond to contra- and co-variant here, and I'm not sure the intuition holds. It works for method types and so forth but not so well for the type definitions themselves, which don't have a natural "input/output" distinction.

I'm going to close this RFC, explore that idea, and open a new one.

withoutboats pushed a commit to withoutboats/rfcs that referenced this pull request Jan 15, 2017
Add a BiLock<T> type for two-way ownership
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants