Stream: t-lang

Topic: Extending indexing to more types


scottmcm (Apr 27 2020 at 20:41, on Zulip):

AFAIK there's appetite to do this, so I wanted to start a conversation to try to find a way.

Here's a stab at requirements:

Non-goals:

Precedents:

Known issues:

nikomatsakis (Apr 27 2020 at 20:57, on Zulip):

I don't really think a different inference fallback is very viable

nikomatsakis (Apr 27 2020 at 20:57, on Zulip):

Or at least the barrier to arguing for that solution seems pretty high to me

nikomatsakis (Apr 27 2020 at 20:57, on Zulip):

Especially because I feel like i32 indicates aren't totally impossible to imagine anyway

nikomatsakis (Apr 27 2020 at 20:57, on Zulip):

you can imagine some computation that yields a valid index but where intermediate values may become negative easily enough

scottmcm (Apr 27 2020 at 21:51, on Zulip):

Yeah, I prefer the "just allow signed" solution. Because, to me, a[i-1] wrapping to usize::MAX is actually worse than a[i-1] giving a panic message mentioning -1.

scottmcm (Apr 27 2020 at 21:52, on Zulip):

(Which is also what contributes to me putting the "off-by-one needs to fail" requirement in the OP.)

simulacrum (Apr 27 2020 at 21:53, on Zulip):

I'd like to throw in a requirement that the inference (or whatever) doesn't interact poorly with the desire for implicit promotion (e.g. u8 -> usize), even if I think it's not 100% that we want that yet

Josh Triplett (Apr 27 2020 at 22:00, on Zulip):

nikomatsakis said:

Especially because I feel like i32 indicates aren't totally impossible to imagine anyway

How incredibly disruptive would it be to question the default i32 inference for integers?

Josh Triplett (Apr 27 2020 at 22:01, on Zulip):

nikomatsakis said:

I don't really think a different inference fallback is very viable

Even just "default to usize if used in indexing and no other constraints"?

scottmcm (Apr 27 2020 at 22:02, on Zulip):

@simulacrum Hmm, if we support _every_ integral type then there'd never be implicit promotion opportunities, right? (I'm also not sure what kind of promotion you're thinking about. Maybe start another topic about it? Personally the last it came up on IRLO I ended up convinced that we should never do even implicit widening.)

scottmcm (Apr 27 2020 at 22:03, on Zulip):

@Josh Triplett I think that it would need to work for things like .get(1) as well -- not just [1] -- for consistency, which might make it harder to focus.

Josh Triplett (Apr 27 2020 at 22:03, on Zulip):

@scottmcm Yeah, implicit widening is not something I'd want to see. That's separate from "let's make operations smarter".

scottmcm (Apr 27 2020 at 22:04, on Zulip):

Right; I also want to allow comparison operators between all pairs of integral types.

Josh Triplett (Apr 27 2020 at 22:05, on Zulip):

I will freely admit being a little uncomfortable with that, especially between signed and unsigned types.

Josh Triplett (Apr 27 2020 at 22:05, on Zulip):

I do appreciate that Rust could do the right thing, numerically.

Josh Triplett (Apr 27 2020 at 22:05, on Zulip):

But "numerically" is not always the right thing.

Josh Triplett (Apr 27 2020 at 22:06, on Zulip):

I appreciate that Rust helps me not mix integer types without thought, and I'm currently writing code that would be much harder to write if Rust didn't help me in that way.

Josh Triplett (Apr 27 2020 at 22:08, on Zulip):

(Perhaps we should split this into a separate topic about comparison operators, to avoid derailing indexing?)

Josh Triplett (Apr 27 2020 at 22:26, on Zulip):

So, I just dug into the status of default type parameter fallback. That led me to https://github.com/rust-lang/rust/issues/27336 , which led to https://internals.rust-lang.org/t/interaction-of-user-defined-and-integral-fallbacks-with-inference/2496/1 .

Josh Triplett (Apr 27 2020 at 22:27, on Zulip):

It looks like that got stalled on, effectively, the case of integer default versus user default.

Josh Triplett (Apr 27 2020 at 22:28, on Zulip):

If we made a call on that (e.g. "always use the user default if there is one, error if that conflicts with the integer default"), would that be enough to unblock that, which would then unblock indexing, comparison operators, and so on?

Josh Triplett (Apr 27 2020 at 22:30, on Zulip):

That would be enough to say, for instance, that an operator between T and u64 should default to u64, but would allow another type.

Josh Triplett (Apr 27 2020 at 22:33, on Zulip):

That would also make it easier to make API improvements (e.g. switching from a concrete type to a generic type defaulting to that same concrete type) without breaking API compatibility, which seems like an overall win for the ecosystem.

Sebastian Malton (Apr 28 2020 at 02:28, on Zulip):

It would be very nice if indexing be customizable for new types (like making a cyclic-vec that indexes with WrappingIndex). I could see something like this happening, but it would require the indexing trait to know about the "size" of the container.

nikomatsakis (Apr 28 2020 at 17:02, on Zulip):

Josh Triplett said:

How incredibly disruptive would it be to question the default i32 inference for integers?

Past experiences shows that the fallback is "limited" in real code but very common in test code. Probably "limited" by now means effectively "everywhere" anyway. (We actually did the work to remove fallback entirely at one point, and then added it back because of the impact on test code.)

nikomatsakis (Apr 28 2020 at 17:04, on Zulip):

Josh Triplett said:

It looks like that got stalled on, effectively, the case of integer default versus user default.

I think that's not the only case where you can have conflicting defaults. Consider:

fn foo<A = u32, B = i32>(x: Option<A>, y: Option<B>) { }

fn main() {
  let z: Option<_> = None;
  foo(z, z); // now both the fallback for A and B apply to `z`
}
Josh Triplett (Apr 28 2020 at 18:54, on Zulip):

I feel like it'd be reasonable to just error out in that case.

Josh Triplett (Apr 28 2020 at 18:55, on Zulip):

Conflict between type parameter default and integer default: type parameter default wins.
Conflict between multiple type parameter defaults that aren't the same type: error.

nikomatsakis (Apr 28 2020 at 19:05, on Zulip):

Josh Triplett said:

I feel like it'd be reasonable to just error out in that case.

one downside is that it means that adding defaults is not backwards compatible

nikomatsakis (Apr 28 2020 at 19:05, on Zulip):

because it may conflict with some other user-provided default

nikomatsakis (Apr 28 2020 at 19:05, on Zulip):

this is what led to the interest in a "minimal" set of defaults that cannot cause conflicts by construction

Josh Triplett (Apr 28 2020 at 19:28, on Zulip):

Good point.

nikomatsakis (Apr 28 2020 at 19:37, on Zulip):

in particular, the main motivator for these defaults (or one of them) is to enable people to migrate from a specific type to a more general type parameter, and it's "not completely met" by this design, since it may still cause conflicts

scottmcm (Apr 28 2020 at 22:42, on Zulip):

+1 to not really needing defaults in "real code", but them being essential for peripheral code like println!("{}", 1); in demos and such

Last update: Jun 05 2020 at 23:15UTC