-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Remove markers and replace with a default rule for unused type/lifetime parameters #12
Remove markers and replace with a default rule for unused type/lifetime parameters #12
Conversation
Could also make unused types and lifetimes a compiler error, and suggest to the user to apply a variance marker in the message. |
Then the marker types themselves become illegal. |
The marker types have lang items, and those lang items would count as usage of the parameter. |
code that may encode uses for types that are not immediately evident. | ||
For example, one idiom is to attach a lifetime to a struct that | ||
contains unsafe pointers, to prevent that struct from being used | ||
outside of a certain scope. For example, at some point the iterator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is just me, but from an engineering point of view, this seems like a really horrible idiom and we should not encourage it. If there is a need for this we should have some sort of explicit visibility thing in the language. I don't think we should change our type system to help its use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not obvious to me why this is horrible or what the alternatives would be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, that was strong language to use without explanation.
I meant it is horrible because it feels like a hack - you add an unused parameter to enforce using the struct in a certain scope - there is no indication of intent there. Someone reading the code would not know that that was what the lifetime parameter was for if they didn't know about the idiom. I would prefer something explicit to limit the usage, rather than implicitly relying on lifetime checking where there is not really a lifetime (from the perspective of inside the strucure). I also have no idea what such a thing would look like though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we have an existing design that gives a hint at what it looks like -- marker::ContravariantLifetime<'a>
@bill-myers yes, you're right, I should at least list "error" as an alternative. It's also appealing to force explicitness. I think my hope was that we could remove -- or almost remove -- markers entirely. In fact, given the rules here, I think we could remove them entirely, since they could be defined "in language". For example,
Similar tricks should work for lifetimes. |
continue to be safe. In other words, if I have `&Wrap<int>`, that is | ||
interchangable with `&Wrap<uint>`. The algorithm is not wrong. Since | ||
`Wrap` has no fields, there is nothing you can do with this `T` that | ||
is incorrect per se. But the result is certainly surprising. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't a lint (warning you about bivariant type parameters or lifetime parameters) deal with this? The lint could then point the programmer at the std::kinds::marker
module, and then the programmer who had some particular intuition about what that phantom type was for could actually explicitly write out their intent.
(You have of course mentioned std::kinds::marker
below. I just want to understand if the purpose of this RFC is to reduce risk, which I think a lint will be better at, or for programmer convenience, which is where choosing the defaults to match expectations (or perhaps to match actual frequency of occurrence) would be relevant.)
((ugh, this is exactly what bill-myers said above. Sorry for the noise.. :( ))
(Ah, niko's response to bill-myers implies to me that there are indeed goals here beyond just reducing risk. That is fine, though I think it may make the RFC stronger to list those other goals explicitly, if possible.) |
On Tue, Mar 18, 2014 at 09:07:33AM -0700, Felix S Klock II wrote:
I released the RFC prematurely, I think. I updated it. I'm also |
I just pushed a revised version of the RFC. |
I agree with @bill-myers here. Unused type/lifetime parameters should be an error, or at the very least a deny-by-default lint. See https://botbot.me/mozilla/rust/msg/12337250/ for confusion in the wild. Especially since this is such a sublte issue (at least IMO) it's better to have compiler help than just documentation. |
Actually I take that back. I re-read the RFC and am in favor. +1 |
There is one complication -- I hadn't considered cycles among types, (I'm a bit torn about this myself; I definitely prefer error to the |
@cmr Unused lifetime parameters are sometimes useful, as in https://github.com/mozilla/rust/blob/ce62032/src/libstd/fmt/mod.rs#L1167-L1179 which allows for macros with optionally used lifetime parameters. |
cc rust-lang/rust#5922 (@lifthrasiir, that macro is actually incorrect if we ever get hygienic lifetimes; and if we don't then one should be able to disable the warning lint (assuming it's a lint...).) |
I have bad feelings about the proposed behavior in this RFC. My impression is that ML languages have under-estimated the importance of bivariance for too long (e.g. there is no syntax for that in the OCaml surface syntax), and that the discussed change makes Rust take the same road. I think it is important, for the language to be "complete" and robust to unplanned scenarios, that bivariance of parameters (type or lifetime) be supported. Given that std::kind::marker does not have a BivariantType marker (which is also a sign of Rust's design neglecting bivariance), how would one specify that a type is bivariant if the variance inference is tweaked not to detect it? It is very likely that I misunderstood the proposed change and that there is a way to opt-in to bivariance -- in which case I think the RFC could be debated for taste reasons, but is acceptable. An alternative proposal that I think would solve the problem of user surprise at bivariant parameters is the following: warn on "unused" type and lifetime parameters, with a warning message that mentions the problem and prompt people to explicitly add a variance annotation (or marker type or, better in most cases, In ML, bivariant type parameters are very useful to build "phantom types" (which rely on module abstraction). The idea is to have a type definition One could also consider scenarios where users may want to remove fields of a The last argument would be that Niko's proposed change to the current inference algorithms makes the logic sensibly more complex; simple is always better, especially in a type system. (Another experience I had in ML-land is that variance have very different meanings for the author and the users of a library, that people tend to conflate. There are two interpretations for |
This is helpful feedback. I have indeed intentionally neglected
I don't think Rust has an equivalent to this ML separation between I think the Rust-y way to do what you are describing would be to have Right now, at least as much as I understand how bivariance plays out, |
On Tue, Mar 25, 2014 at 09:19:10AM -0700, gasche wrote:
Maybe my view is blurred, but I don't really understand this point. |
Sorry for being unclear. I guess my general point is that not being able to express bivariance could become, not a major blocker, but at least an occasional pain, if combined with other type-system features that are not present in Rust today. Formulated as is, you see that it is a rather weak objection from a pragmatic point of view. It would not be worth a design change if the present design naturally omitted bivariance; but it does suggest that willingly making your system slightly less regular and more complex to remove bivariance (as proposed in this RFC) may be a cause of regret in a (far) future. The two features I considered were:
|
On Wed, Mar 26, 2014 at 05:57:45AM -0700, gasche wrote:
It is also plausible to imagine a future way to explicitly "opt in" to
I don't quite follow this part. While I don't know why we would want |
I'm not an expert on this field but based on my understanding, I like this proposal. I like the fact that it clears out the inference algorithm by removing possible weird cases that users may actually ignore (which I think makes the type-system safer) and it removes the variance markers. That said, I think having a way to opt-in to bivariance is also important. The whole point of this RFC should be to make the type inference safer without forbidding other things - which follows pretty much what is being done for kinds. As a final note, I think the lint that has been mentioned could be implemented now and warn the user about unused types and lifetimes. |
Just a brief update: I am not sure if I still like this plan. In weekly meeting we decided to do some measurements (which I still haven't done) to try and understand how often this comes up. |
struct CovariantType<T>; | ||
struct ContravariantType<T> { m: CovariantType<fn(T)> } | ||
struct InvariantType<T> { m: CovariantType<fn(T) -> T> } | ||
struct CovariantLifetime<'a> { m: CovariantType<fn(&'a int)> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was really worried about this, but now I like the idea, seeing how ContravariantType
and CovariantLifetime
can be emulated.
Add option for JSON output to help command
Inspired by @edwardw's PR rust-lang/rust#12828, this RFC proposes a small change to the behavior of variance inference if a type or lifetime parameter is unused. The goal is to more accurately capture user's intended behavior.