-
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
Polymorphic Numeric Constants #1945
Conversation
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
* One alternative is to use macro constants, which involves defining a macro for each constant, and invoking the constant via `FOO!()` instead of `FOO`. It is verbose and ugly, clutters the global macro namespace, and will probably uncover some performance regression in rustc. |
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.
@cuviper has suggested another alternative over at irlo: making it possible to drop the ()
at the macro invocation, requiring you only to type FOO!
. Ofc still one character lost, and until macros 2.0 the global namespace problem still persists.
So there are two problems here:
const INTEGER = 273;
// is actually, under the hood:
const INTEGER<T: FromIntLiteral>: T = 273; Now whether we want that syntax is another discussion. It's also plausible to treat uninferred literals as either an error or leave them polymorphic, instead of defaulting to IMO we should have something like #143, if not for custom types, at least for builtin ones. |
Maybe a contextual keyword like I definitely want to support const expressions for their initializers. |
Oh and I vehemently disagree with the idea of calling these "untyped". Rust doesn't have such a concept and if it we try to squeeze it in, it might even be unsound, you need to represent this as polymorphism. |
FWIW, C++'s equivalent-in-power feature is |
@retep998: The |
A simple @TimNN Fixed |
bindgen could also benefit from this. It translates typedef int sfBool;
#define sfFalse 0
#define sfTrue 1 to pub const sfFalse: ::std::os::raw::c_uint = 0;
pub const sfTrue: ::std::os::raw::c_uint = 1;
pub type sfBool = ::std::os::raw::c_int; causing type mismatches between |
Rust already has this feature for literals: fn main() {
let x:&str = 1;
}
So if the magic |
@pornel That's an inference variable which is local to one constant initializer or function body, it's useless across them, and has to be fully resolved locally just like the more general |
@@ -0,0 +1,80 @@ | |||
- Feature Name: polymorphic_numeric_constant | |||
- Start Date: 2017-04-06 |
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.
Let's hope we have a time machine built in Rust :-)
It's a bit more complicated than that. You have two features: variable templates (N3651) and inline variables (P0386). You really need both of them if you want to be able to use generic globals without ODR/linkage issues. I also like the syntax: const INTEGER<T: FromIntLiteral>: T = 273; But I have two questions that do not seem to be addressed by the RFC:
Given both these questions, I think we could really just phrase this as "syntax sugar" for associated constants. That is: const INTEGER<T: FromIntLiteral>: T = 273; would just desugar to: pub trait INTEGER<T: FromIntLiteral> {
const VALUE: T = 273; // default constant value
} where This way, we could override the default value by implementing the Or, in other words, the following two features:
I am worried about 2., because I would really like that if we add this kind of desugaring, it should work for all traits with a |
@gnzlbg Why do you think you need a trait? We can add generic parameters to regular |
@eddyb Mainly because it opens the door of using specialization. A |
Oh, did you mean const FOO<T: Foo>: T = T::VALUE; |
Yes, sorry about that, I just fixed it :) |
So I guess the TL;DR: of my post is that |
I like the idea of using traits and generics for this. At a minimum, that seems worth mentioning as an alternative, but it seems to me like the preferable approach. We'd need syntax for generics on consts, but that seems like a reasonable thing to have. That way, you could declare a const that has the same property as just writing a numeric literal: it'll work as many different possible types. |
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
* One alternative is to use macro constants, which involves defining a macro for each constant, and invoking the constant via `FOO!()` instead of `FOO`. It is verbose and ugly, clutters the global macro namespace, and will probably uncover some performance regression in rustc. Macros 2.0 may get rid of the global namespace pollution, and an RFC could make calling the macro as simple as `FOO!` but it is still far from ideal. |
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.
Macros would be my favoured solution since it doesn't require extending the type system in any way. We would make these much less verbose to declare and scopable as part of macros 2.0. We could try and handle not requiring the ()
. It doesn't seem far from ideal at that point.
@nrc Why do you prefer macros instead of traits/generics? |
@joshtriplett macros already exist and we plan to make the changes anyway. Generics require extending the type system. |
@nrc We wouldn't need to extend the type system to allow const generics; it's the same kind of inference we already use in other places, like functions returning a generic type. I wonder if we could split this into two separate issues: 1) const generics, and 2) some kind of trait for numeric literals. The former seems really straightforward; the latter seems like the complicated bit here. Does that sound plausible? |
Potential macro syntax:
and if we can't make the lack of
|
@joshtriplett part 1 is an extension, because it doesn't work today. It is however a small extension (probably, there is some risk still). part 2 is more worrying - it is a tricky part of type inference already and adding a trait to the mix seems non-trivial. I'm not sure either is worth doing, unless there is motivation beyond that given here (because this use case can be addressed with macros). |
@nrc I don't the inference aspects of 2 are too hard. To my knowledge, we don't have any sort of let-polymorphism in rust ATM, and this would not change that (and thus our The hard part would be generalizing to allow user-definable implementations supporting arbitrary precision. We'd need |
@rfcbot fcp postpone We discussed this RFC in the @rust-lang/lang meeting yesterday and decided to move to postpone the RFC. This is with some regret, since I think that we all felt the motivation was fairly strong. In particular, it'd be nice to make numeric constants ergonomic, and it's true that FFI (and Windows integration!) is an important application. I'm particularly keen on finding ways to improve FFI ergonomics and approachability. However, ultimately we felt like the approach outlined in this RFC probably wasn't the approach we would want, and the proposal presents a number of complications. The first is that the syntax it proposes, If we wanted to put this on some kind of solid foundation, there would be perhaps three ways to do it, both of which expose non-trivial complications. The first is, as @eddyb suggested, to use generics, so we could understand const Foo<T: Integer>: T = 22 To do this would also require some sort of special trait (here I called it Another complication is that the handling of integer literals in rustc today is kind of tricky in some ways. For example, we currently use a distinct kind of type variable to represent integer types. Unfortunately, the type-checker takes advantage of the fact that it knows that Finally there are the complications that others mentioned around precision and so forth. None of these points are blockers, but they are all substantial enough to suggest that the time is not ripe for this RFC, and that if we were to pursue it as a kind of "on the side" endeavor, it has a high probability of requiring a lot of time and effort which would distract from other roadmap goals. Hence the decision to move to postpone. |
Team member @nikomatsakis has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Maybe in the meantime we can just get plain old polymorphic constants?
The first part ought to be trivial. e.g. given that the first works, what's so hard about the second? const fn asdf<T>() -> [T; 0] { [] }
const asdf<T>: [T; 0] = [];
Perhaps there should be an issue for replacing the special inference variables with an (unnameable for now) trait internally? i.e. it would be a shame for backwards-incompatability to bite us here and perhaps somebody (not on the core time, not bound to the roadmap) could investigate? Also, mostly off-topic, I've been musing being able to pun between bounds and subkinds. Seems useful, and related to #1733. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period is now complete. |
Closing as postponed, as per the summary comment. |
@gnzlbg (random) I'd just like to point out that templated things are automatically |
Just for whenever this idea returns : If the syntax should be roughly |
I would rather just have a |
Seems like the shortcut |
I noticed that polymorphic constants, even without polymorphic numeric literals, would make returning from
|
Even if we went with |
rendered
Note that I don't particularly mind what the syntax to define a polymorphic constant is, as long as the user can still use the constant the same as any other constant.