Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Elide array size #2545
Conversation
Centril
added
the
T-lang
label
Sep 17, 2018
Centril
reviewed
Sep 17, 2018
text/0000-elide-array-size.md Outdated
Centril
reviewed
Sep 17, 2018
text/0000-elide-array-size.md Outdated
This comment has been minimized.
This comment has been minimized.
|
I'm not opposed to value inference of this kind - in fact it excites me a lot. |
This comment has been minimized.
This comment has been minimized.
|
I can see that there is interest in having more daring inference here. However, as I've pointed out, this can lead to errors from a distance, if one static contains a const down the line whose type changes. Those errors could be hard to track down. At the very least, they'd be surprising. However, we may be able to tweak the error reporting in such a way that such errors are quickly resolved, and that'd be awesome. For now, similar to the const lifetimes RFC I wrote, let's take a smaller step and elide the length only where it is obvious from the same line of code. This will already give us very real ergonomic wins with very little cost both in readability and teachability (It just occurred to me that I should add a paragraph about error messages), and it doesn't preclude us from aiming for stronger inference later. |
SimonSapin
reviewed
Sep 18, 2018
| For example, you might write: | ||
|
|
||
| ```rust | ||
| const CONST_CHARS: [u8; _] = b"This is really a byte array"; |
This comment has been minimized.
This comment has been minimized.
SimonSapin
Sep 18, 2018
Contributor
Nit: the expression here needs to be *b"…" (with a star added) because the type of byte string literals is &'static [u8; N]. (Maybe it should have been plain [u8; N] but oh well.)
This comment has been minimized.
This comment has been minimized.
So with this RFC as-is, the following would be an error, right? fn foo() -> [u8; 9] { /* details elided */ }
fn main() {
let bar: [u8; _] = foo();
}If so, this feels even more ad-hoc, non-uniform, and makes me wary of the teachability aspect.
I am specifically concerned with taking small steps; I wouldn't want to stabilize this, leave the language in an inconsistent state in the interim and do the difficult extensions possibly later. I want to be sure that the extensions both can and will happen. In particular, I expect that the following should work: struct Matrix<T, const N: usize, const M: usize> {
data: [[T; N]; M]
}
fn main() {
let foo: Matrix<u8, _, _> = Matrix { data: [[1, 2, 3], [4, 5, 6]] };
} |
This comment has been minimized.
This comment has been minimized.
|
That's an interesting case. As for your first example, As such I worry, that your proposal could lead to errors that are extremely hard to track down. So unless you can show me an algorithm to produce error messages for that case that makes those errors easy to fix, I remain unconvinced. I don't agree with your teachability argument either. If the size is trivially inferrable from the initializer, wildcards are allowed. Otherwise insert the size. |
This comment has been minimized.
This comment has been minimized.
This is no different from using // crate A:
pub fn producer() -> [u8; 9] { /* details elided */ }
// crate B:
pub fn consumer(arr: [u8; 9]) { /* details elided */ }
// crate C:
fn main() {
let bar = producer(); // No type annotations.
consumer(bar);
}Thus, by writing
cc @varkor As far as I know, most dependently typed languages can do this sort of in-function inference (or the languages would be intolerable to write in) and so it should not be novel in any way.
It is the "otherwise insert the size" that is surprising, compared to how inference currently works for types coupled with the fact that you are allowed to write |
This comment has been minimized.
This comment has been minimized.
|
To continue with my tradition of macro implementations of RFCs (except in this case I wrote the macro two years ago)... https://crates.io/crates/counted-array Clearly, I think this is a good idea. :) |
This comment has been minimized.
This comment has been minimized.
|
|
glandium
added a commit
to glandium/sccache
that referenced
this pull request
Sep 19, 2018
glandium
referenced this pull request
Sep 19, 2018
Merged
Use the counted_array macro for the ARGS arrays #298
This comment has been minimized.
This comment has been minimized.
|
I like this RFC a lot. Having to maintain the array length manually is boilerplate causing unnecessary work -- counting things is something computers are good at -- I know this would have samed me some edit-compile cycles. |
This comment has been minimized.
This comment has been minimized.
|
@Centril I am sympathetic to your interest in uniformity and wouldn't be against inference for partial type ascription in I'm still defending the principle of locality for |
This comment has been minimized.
This comment has been minimized.
earthengine
commented
Sep 20, 2018
•
|
I would like to have this. For the concerns @Centril have, I propose the following rule:
so fn foo() -> [u8; 9] { /* details elided */ }
fn main() {
let bar: [u8; _] = foo();
}is not legal as We need the rule for |
joshtriplett
referenced this pull request
Sep 21, 2018
Open
FFI mechanism to declare a symbol for an array #54450
This comment has been minimized.
This comment has been minimized.
|
I don't believe this RFC is necessary - it is subsumed by const generics, perhaps other than the surface syntax. The ability to infer constants in types is essential to const generics, particularly when used in rust-lang/rust#53645 has already been open for just over a month now, and it already contains the needed implementation work (minus the surface syntax of Nominating for @rust-lang/lang meeting, with a preference to close. |
eddyb
added
the
I-nominated
label
Sep 26, 2018
This comment has been minimized.
This comment has been minimized.
|
To be clear, my comment above is about inference wherever we accept inference today. For the types of Note that said decision interacts with this RFC two-fold:
That former shows why this is not as pressing of a problem, while the latter talks about pretty much the same strategy taken by this RFC. OTOH, the original RFC stands some chance of being revived, with perhaps some restrictions around integer fallback, and ignoring "the closure problem" (#2010 (comment)). |
joshtriplett
removed
the
I-nominated
label
Sep 27, 2018
This comment has been minimized.
This comment has been minimized.
|
Based on discussion in @rust-lang/lang, we agreed that (based on @eddyb's comments above) const generics addresses the implementation of this, so the RFC as written is not quite what we're looking for. However, we do think that there's an RFC needed to explicitly allow inferring the const values in the types of const values, in specified circumstances, including array sizes. @llogiq, would you be open to restructuring this RFC into that, and referencing the const generics work as how this will be implemented? |
This comment has been minimized.
This comment has been minimized.
I think perhaps the problem you are getting at @llogiq with local reasoning is in particular with inference for items defined elsewhere. A more precise mechanism to get at that problem could be to require explicit type ascription for const values (and perhaps types) that arise from |
This comment has been minimized.
This comment has been minimized.
|
@joshtriplett will do. I'll just have to understand #2000 and #2010 first |
oli-obk
reviewed
Oct 2, 2018
| [drawbacks]: #drawbacks | ||
|
|
||
| There is a modicum of complexity to extend the parser and AST which needs to be | ||
| done for every Rust parser in existenct. However, the feature is minor, so the |
This comment has been minimized.
This comment has been minimized.
| [summary]: #summary | ||
|
|
||
| In arrays, allow to elide the size in the type and put an underscore there | ||
| instead if it can be deduced from the initializer. For example: `static |
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 2, 2018
Contributor
I think the summary should already mention that we only want this inference if the length can be trivially obtained by either counting the elements of the array initializer or taking it verbatim from a repeat expression.
| needs to count the elements of an array manually to determine its size. Letting | ||
| the compiler find the size reduces the potential for error. It also allows us | ||
| to ascribe the array component type without requiring the size (which is – | ||
| perhaps surprisingly – not currently allowed). |
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 2, 2018
Contributor
I have seen many situations where users just gave up and used const X: &[u8] = &[4, 5, 6]; in order to not need to specify the length.
luser
added a commit
to glandium/sccache
that referenced
this pull request
Oct 9, 2018
llogiq
force-pushed the
llogiq:elide-array-size
branch
from
ad1e870
to
f1d1d4e
Nov 3, 2018
llogiq
force-pushed the
llogiq:elide-array-size
branch
from
f1d1d4e
to
e205d75
Nov 13, 2018
This comment has been minimized.
This comment has been minimized.
|
I've rewritten some parts of the RFC to hopefully observe the changes @eddyb and others have suggested, In short:
|
This comment has been minimized.
This comment has been minimized.
|
The new version of this seems much clearer. @rust-lang/lang, should we discuss this in the next meeting, or do we potentially have consensus to P-FCP this? |
joshtriplett
added
the
I-nominated
label
Nov 13, 2018
Centril
added
A-typesystem
A-inference
A-array
A-const-generics
labels
Nov 22, 2018
This comment has been minimized.
This comment has been minimized.
|
@rfcbot merge |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Dec 20, 2018
•
|
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:
Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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. |
rfcbot
added
proposed-final-comment-period
disposition-merge
labels
Dec 20, 2018
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern rfc-is-vague-or-maybe-niko-is-just-lazy The reference-level text states:
I felt a bit unclear what was really being proposed here. Are we proposing that we will type the body of the static/const and then infer the resulting What happens in the event of cycles? Can this be used in more complex types, e.g., Sorry if these questions are all answered in the RFC, I don't have time this second for a detailed reading. |
This comment has been minimized.
This comment has been minimized.
|
To add to @nikomatsakis's point, can I write @rfcbot concern general-value-inference |
This comment has been minimized.
This comment has been minimized.
|
One thing that occurred to me that I hadn't considered before is that this sets a precedence for something we currently don't have (much of) in Rust-- expressions changing the public type signature of an item. For example, adding an element to a |
This comment has been minimized.
This comment has been minimized.
This reminds me of the |
This comment has been minimized.
This comment has been minimized.
|
@cramertj To solve that concern, we could limit the inference to
At least not with the syntax
If Adding special syntax for this doesn't seem well motivated imo in terms of complexity. |
Centril
removed
the
I-nominated
label
Jan 3, 2019
This comment has been minimized.
This comment has been minimized.
|
I have to say I'm torn here. I like the |
This comment has been minimized.
This comment has been minimized.
I think these two can be mutually compatible; i.e. permit |
This comment has been minimized.
This comment has been minimized.
That's already the case, you can't have |
This comment has been minimized.
This comment has been minimized.
What I'm suggesting is that things like the following could be legal: const X: _ = {
const Y: _ = 1;
Y
};since |
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern state-contextual-constraints-up-front As I was reading the RFC, I was asking myself "is one allowed to write The current RFC text says in the reference section "In its proposed form, there are no corner cases, because the only thing it enables that is currently disallowed is to put a _ wildcard in place of the number of elements in array types in lets, consts and statics." This was the first occurrence of text mentioning the constraint that this is only allowed for array types in lets, consts, and statics. The phrase "allow to elide the size in the type and put an underscore there instead if it can be inferred" could lead one to think that the In my opinion, I should not have had to wait until the reference section to discover the answer to my question. I'd point it out sooner; even just replace "In array types, ..." with "In array types used in |
This comment has been minimized.
This comment has been minimized.
@pnkfelix I agree with the need for clarity. At the same time, would it be so bad (see #2545 (comment)) if non-public things may be permitted more global inference? Seems like a great productivity and ergonomics boon without too many pitfalls wrt. semver concerns? A have-your-cake-and-eat-it approach? |
This comment has been minimized.
This comment has been minimized.
|
Ever since https://internals.rust-lang.org/t/making-certain-annotations-optional-for-non-public-items/4770/2?u=ixrec I've been iffy on whether we should introduce any "non-public lenience" behaviors like that. I can't decide if it'd be a net win in this specific case or not. |
This comment has been minimized.
This comment has been minimized.
|
As update to my earlier comment (#2545 (comment)) about #![feature(impl_trait_in_bindings)]
#![feature(unsize)]
use std::marker::Unsize;
// Unsize bound for convenience
trait Array<T>: Unsize<[T]> {
const LEN: usize;
}
// length 2 and 3 implemented for demo
impl<T> Array<T> for [T; 3] {
const LEN: usize = 3;
}
impl<T> Array<T> for [T; 2] {
const LEN: usize = 2;
}
fn foo() -> impl Array<u8> {
*b"foo"
}
const FOO: impl Array<u8> = *b"foo";
const AB: impl Array<u8> = *b"ab"; |
This comment has been minimized.
This comment has been minimized.
|
So... my idea doesn't actually work in a usable way. While it works out of the box as shown above, one has to always convert it to |
llogiq commentedSep 17, 2018
•
edited
This is a small-ish RFC for array size elision wherever it can be taken directly from the initializer. It is deliberately restricted like this to uphold locality and avoid errors at a distance.
Rendered