Stream: t-lang/wg-unsafe-code-guidelines

Topic: need for arrow operator (or similar)


Gankro (May 06 2019 at 16:06, on Zulip):

https://internals.rust-lang.org/t/need-for-operator-for-unsafe-code-guidelines/10022

Gankro (May 06 2019 at 16:06, on Zulip):

@RalfJ ^

RalfJ (May 06 2019 at 21:19, on Zulip):

Yes yes please!

RalfJ (May 06 2019 at 21:20, on Zulip):

I am too tired right now to think about whether this changes the story for &raw [const|mut] <expr>... but on first sight I think it would not.

nikomatsakis (May 06 2019 at 21:24, on Zulip):

I don't 100% understand what this arrow operator would do

nikomatsakis (May 06 2019 at 21:25, on Zulip):

oh, like, it is literally the -> from C

nikomatsakis (May 06 2019 at 21:25, on Zulip):

this is just because (*foo).bar is annoying to write?

RalfJ (May 06 2019 at 21:25, on Zulip):

ptr->field would be either (*ptr).field, or maybe a raw ptr to that.

RalfJ (May 06 2019 at 21:25, on Zulip):

this is just because (*foo).bar is annoying to write?

write and read

nikomatsakis (May 06 2019 at 21:25, on Zulip):

could we just define -> only on raw pointers?

RalfJ (May 06 2019 at 21:26, on Zulip):

like, (*x.foo().bar()).field1.field2

nikomatsakis (May 06 2019 at 21:26, on Zulip):

in which case, one can think of them as "C pointers"

nikomatsakis (May 06 2019 at 21:26, on Zulip):

which...seems like a pretty accurate mental model?

nikomatsakis (May 06 2019 at 21:26, on Zulip):

i.e., just as in C you would do foo->bar...

nikomatsakis (May 06 2019 at 21:26, on Zulip):

but also, pointers in C code that you link to are literally also treated like raw pointers

nikomatsakis (May 06 2019 at 21:26, on Zulip):

(from UCG perspective)

nikomatsakis (May 06 2019 at 21:27, on Zulip):

anyway, seems..ok to me

RalfJ (May 06 2019 at 21:32, on Zulip):

could we just define -> only on raw pointers?

yeah, there's probably no good reason to have it on references

Vadim Petrochenkov (May 06 2019 at 21:54, on Zulip):

Can't raw pointers just use . like references? (Still requiring unsafe.)
IIRC, something like this was planned eventually when unsafety checking was moved to MIR.

Gankro (May 06 2019 at 23:39, on Zulip):

This is counter to my memory, in which it was generally understood that . on raw pointers (especially when they're nullable) is waaay too dangerous.

Gankro (May 06 2019 at 23:40, on Zulip):

Specifically, I was able to land the new ptr methods specifically under the rationale that raw pointers will never implement Deref-style . (and also, we already had a few methods so it was too late to stop it)

centril (May 06 2019 at 23:42, on Zulip):

I think familiarity of foo->bar in C++ et. al works against us here; I think it might encourage overuse of raw pointers where references would otherwise suffice

Gankro (May 06 2019 at 23:42, on Zulip):

no that's the point, we have discouraged raw pointers too much, and it's literally making rust programs unsound

Gankro (May 06 2019 at 23:43, on Zulip):

you literally should avoid using references as much as possible once you're using raw pointers

centril (May 06 2019 at 23:43, on Zulip):

@Gankro I mean for normal uses where you don't need raw pointers

Gankro (May 06 2019 at 23:43, on Zulip):

@RalfJ I think if we did proposal 3, it would eliminate most uses for &raw? Most obvious exception being indexing results, but it wouldn't be that unreasonable to make *const [T] indexing work like my proposed raw pointer dot operator

centril (May 06 2019 at 23:44, on Zulip):

foo.*bar seems better if we're going with new syntax tbh

Gankro (May 06 2019 at 23:45, on Zulip):

I am super dubious of people considering raw pointers more convenient than references

centril (May 06 2019 at 23:46, on Zulip):

@Gankro I feel we already have such overuse by people coming from C & C++

Gankro (May 06 2019 at 23:46, on Zulip):

got any citations on that..?

centril (May 06 2019 at 23:47, on Zulip):

not on hand

centril (May 06 2019 at 23:47, on Zulip):

imo this also doesn't belong on the roadmap for 2019

Gankro (May 06 2019 at 23:49, on Zulip):

is https://github.com/rust-lang/rfcs/pull/2582 also similarly not going to be considered for 2019?

centril (May 06 2019 at 23:50, on Zulip):

No that one will because it's both preexisting and makes something possible which otherwise isn't and moreover facilitates closing soundness holes.

Nicole Mazzuca (May 07 2019 at 00:04, on Zulip):

I really don't like adding -> without adding a way to overload it

Nicole Mazzuca (May 07 2019 at 00:04, on Zulip):

but *. seems reasonable, imo

Gankro (May 07 2019 at 00:05, on Zulip):

what if my proposal is an effective alternative to #2582 ?

Nicole Mazzuca (May 07 2019 at 00:05, on Zulip):

I also don't like .* b/c it seems out of order

Gankro (May 07 2019 at 00:08, on Zulip):

Also TIL C++ has .*, and I believe it means what I was proposing raw_ptr.field is

Nicole Mazzuca (May 07 2019 at 00:08, on Zulip):

it's very different

centril (May 07 2019 at 00:09, on Zulip):

ah; foo*.bar then -- seems better than ->

Nicole Mazzuca (May 07 2019 at 00:09, on Zulip):

x.*y, by default, takes cls* x; T cls::* y;, and does a member-pointer lookup by y on x

Gankro (May 07 2019 at 00:10, on Zulip):

what's the solution to *.'s ambiguity?

Nicole Mazzuca (May 07 2019 at 00:10, on Zulip):

i.e:

struct Foo {
  int x;
  int y;
};

int main() {
  int Foo::* x = &Foo::x;
  Foo f{};
  return f.*x;
}
Gankro (May 07 2019 at 00:10, on Zulip):

0.o

Nicole Mazzuca (May 07 2019 at 00:11, on Zulip):

but also, you can overload it for any LHS and RHS types

Nicole Mazzuca (May 07 2019 at 00:11, on Zulip):

like other binary operators

Nicole Mazzuca (May 07 2019 at 00:12, on Zulip):

unlike ->, which is overloaded as a unary operator

Nicole Mazzuca (May 07 2019 at 00:14, on Zulip):

it's not especially useful for fields, but it's really nice for methods

Gankro (May 07 2019 at 00:18, on Zulip):

it's possibly postfix deref would also be ambgious with foo * [0] (is this (*foo)[0] or mul(foo, [0])?

Nicole Mazzuca (May 07 2019 at 00:22, on Zulip):

hmm, point

Nicole Mazzuca (May 07 2019 at 00:23, on Zulip):

I was thinking specifically *. as an operator tho

Gankro (May 07 2019 at 00:25, on Zulip):

how can you parse that out of foo*.0, though?

Nicole Mazzuca (May 07 2019 at 00:42, on Zulip):

you'd parse *. as an operator

Nicole Mazzuca (May 07 2019 at 00:42, on Zulip):

foo *. 0

Nicole Mazzuca (May 07 2019 at 00:43, on Zulip):

it's a breaking change, but not one that should break anybody who isn't making two really poor style decisions :P

Gankro (May 07 2019 at 01:09, on Zulip):

seems unecessarily brutal

RalfJ (May 07 2019 at 08:40, on Zulip):

I think familiarity of foo->bar in C++ et. al works against us here; I think it might encourage overuse of raw pointers where references would otherwise suffice

all current data points towards people using references when they should use raw ptrs (and causing unsoundness along the way). So I doubt this. References are still more convenient than raw ptrs even if we got ->.

gnzlbg (May 07 2019 at 15:45, on Zulip):

all current data points towards people using references when they should use raw ptrs (and causing unsoundness along the way).

Do we have any data on people using pointers where they should actually be using references ? IIRC, many of the soundness bugs in actix where due to this.

gnzlbg (May 07 2019 at 15:45, on Zulip):

While I feel @Gankro 's pain, my first thought was "Could we fix this with a lint ?"

gnzlbg (May 07 2019 at 15:46, on Zulip):

As opposed to adding an operator for this.

gnzlbg (May 07 2019 at 15:47, on Zulip):

Also, IMO, the main reason people don't write (*x.foo().bar()).field1.field2 is because they don't know they need to.
I don't see how -> fixes that.

gnzlbg (May 07 2019 at 15:47, on Zulip):

The problem that people don't know that they have to use -> instead of references would still be there.

RalfJ (May 07 2019 at 20:35, on Zulip):

Also, IMO, the main reason people don't write (*x.foo().bar()).field1.field2 is because they don't know they need to.
I don't see how -> fixes that.

it's annoying as hell to write and doesnt nest the right way

RalfJ (May 07 2019 at 20:37, on Zulip):

postfix compsotionality is awesome, it seems like it'll even dominate the await discussion. not having it for raw ptrs is a huge pain and I sure can see that affecting people's decisions when weighing raw ptr against ref. it also makes fixing such bugs much more annoying (and much less of a search-and-replace as you need to add parentheses).
but yes, we also have tons of teaching to do.

Nicole Mazzuca (May 07 2019 at 21:06, on Zulip):

the only reason I'm against the arrow operator is because it's not overloadable -- I really dislike adding new operators that can't be overloaded

Nicole Mazzuca (May 07 2019 at 21:07, on Zulip):

The only operators that shouldn't be overloadable are those which exist automatically for all types (i.e., &)

Gankro (May 08 2019 at 15:22, on Zulip):

What is the overloadable signature of -> ? (&self) -> *mut|const T?

Nicole Mazzuca (May 08 2019 at 16:16, on Zulip):

yeah

gnzlbg (May 08 2019 at 17:18, on Zulip):

Is the method generic ?

gnzlbg (May 08 2019 at 17:19, on Zulip):

e.g. trait Arrow { fn arrow<T>(&self) -> *const T; } ?

gnzlbg (May 08 2019 at 17:19, on Zulip):

or the trait, e.g., trait Arrow<RHS> { fn arrow(&self) -> *const RHS; } ?

RalfJ (May 08 2019 at 17:21, on Zulip):

I guess it'd be like Deref, with an associated type

gnzlbg (May 08 2019 at 17:22, on Zulip):

or should the type be an input parameter, e.g., trait Arrow { type Output; fn arrow(&self) -> *const Self::Output; } that one has to implement for all relevant output params, e.g., impl<O> Arrow for MyType { type Output = O; ... }

gnzlbg (May 08 2019 at 17:23, on Zulip):

@RalfJ with Deref, dereferencing a type always gives you some other constant type, but here value->field0 and value->field1 give you two different types

RalfJ (May 08 2019 at 17:24, on Zulip):

Deref does the part before the field

RalfJ (May 08 2019 at 17:24, on Zulip):

so I guess Arrow would just the same give you the ptr to get the field from

RalfJ (May 08 2019 at 17:25, on Zulip):

I mean otherwise we need ptr-to-member

RalfJ (May 08 2019 at 17:25, on Zulip):

(which also seems fine and could help with some other things but is a much bigger endeavor)

gnzlbg (May 08 2019 at 17:29, on Zulip):

I don't think I follow, I thought the internals thread proposed a value->field that expands to (*value).field

gnzlbg (May 08 2019 at 17:30, on Zulip):

and that the whole point of providing a way to overload it is to be able to map (input: typeof(value)) -> typeof(field) or similar

RalfJ (May 08 2019 at 17:31, on Zulip):

yes. and then to overload -> you could have a fn arrow(&self) -> *const Self::Out or so, so it really desugars to (*Arrow::arrow(&value)).field.

RalfJ (May 08 2019 at 17:31, on Zulip):

that would mirror the situation for Deref

gnzlbg (May 08 2019 at 17:31, on Zulip):

why can't we use Deref for that ?

RalfJ (May 08 2019 at 17:31, on Zulip):

because Deref returns a reference

RalfJ (May 08 2019 at 17:31, on Zulip):

and the entire point is that we need to make it a raw ptr

gnzlbg (May 08 2019 at 17:31, on Zulip):

Ah, duh, yes

gnzlbg (May 08 2019 at 17:32, on Zulip):

so arrow is just trait Arrow { type Out; fn arrow(&self) -> *const Self::Out; }, so that one can implement arrow, e.g., on Box to get a pointer to a field?

gnzlbg (May 08 2019 at 17:34, on Zulip):

that is, it would be possible to, e.g., implement Deref and Arrow, with different input parameters, for the same type ?

gnzlbg (May 08 2019 at 17:37, on Zulip):

struct A(u32); struct B(i32); struct C(A, B); impl Deref<Output = A> for C { ... } impl Arrow<Output = B> for C { ... }; ?

RalfJ (May 08 2019 at 18:26, on Zulip):

hm that's not nice. but if people really want to shoot themselves in the foot...

Last update: Nov 20 2019 at 11:30UTC