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 upChange `&` to be a borrow operator. #248
Conversation
nrc
self-assigned this
Sep 19, 2014
This comment has been minimized.
This comment has been minimized.
|
Repurposing It also breaks the following symmetry: If value There are too many surprises. If we do need a borrow operator, it should be another sigil. But I still believe a semi-explicit coercion operator is a better solution. I'll try to address the |
This comment has been minimized.
This comment has been minimized.
|
Actually I think it is doable with my
|
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 19, 2014
|
I think that this additional complexity may be worth it. I recently spent some time explaining Rust's ownership semantics, borrowing rules, and syntax to a relatively new user. The trickiest thing to explain was not the rules of ownership, but what is going on with Having |
This comment has been minimized.
This comment has been minimized.
|
Even better, we can enable both prefix and postfix variants of
We can in fact adopt both proposal. |
This comment has been minimized.
This comment has been minimized.
|
@reem, And having Let's use |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 19, 2014
|
It wouldn't just confuse C / C++ programmers. It would make Rust less usable as a systems language and would break generics. This is the kind of operator overloading trickery that people look down on C++ for. |
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 19, 2014
|
With & as the borrow operator, the symmetry is now not syntactic, but semantic, and I think that's better. Rather than & always being the address of something, it becomes always the borrowed form of something, which is rather more significant in Rust. For instance, in implementing Hyper we have needed a way to represent an immutable view into Headers. Normally, this can be done through This is a general problem - often times you do not want the address of something, you want an immutable or mutable borrow of it. @thestinger, you could always just use &(thing) to get the actual address, so I'm not sure why this makes Rust less usable as a systems language. The interaction with generics needs to be explored further. |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 19, 2014
|
Needing to use |
This comment has been minimized.
This comment has been minimized.
|
This does make conversions from |
This comment has been minimized.
This comment has been minimized.
|
Sounds potentially unsafe in combination with FFI code #[repr(C)]
struct X {
i: i16,
j: i8,
k: i8,
}
impl Deref<i8> for X {
fn deref(&self) -> &i8 {
&self.k
}
}
fn main() {
let x = X { i: 0, j: 0, k: 0 };
let _: *const i16 = &x as *const _ as *const _;
} |
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 19, 2014
|
@mahkoh You'd use |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 19, 2014
|
@reem: The need to use |
This comment has been minimized.
This comment has been minimized.
|
I'd say just the fact that Also, the borrow operator is actually a syntax sugar, so it should not supersede the more fundamental operation that is taking the address. |
This comment has been minimized.
This comment has been minimized.
|
@reem: If the code above compiles then it stores an invalid pointer in _. |
This comment has been minimized.
This comment has been minimized.
|
@mahkoh could you explain why please? The |
This comment has been minimized.
This comment has been minimized.
|
@CloudiDust the The principle here is that in Rust, borrowing is actually a more fundamental operation than taking the address. (I'm not sure I agree 100% with this principle, but I am warming to it). |
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 19, 2014
|
@mahkoh It doesn't seem like that is a new issue. I understand that this makes it slightly more error prone and I dislike that, but I think that the advantages in ergonomics for many extremely common types possibly outweighs the downsides. |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 19, 2014
|
|
This comment has been minimized.
This comment has been minimized.
|
@nick29581 Maybe I'm misunderstanding something but
|
This comment has been minimized.
This comment has been minimized.
|
@mahkoh it seems the problem here is the cast, not the & operator |
This comment has been minimized.
This comment has been minimized.
|
@thestinger would you feel better about using a new operator? Leave |
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 19, 2014
|
A possibility if we were to use |
This comment has been minimized.
This comment has been minimized.
|
@reem with |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 19, 2014
|
@nick29581: I'm happy with how it works today... :P I'm strongly against changing it because I think it's already the sanest solution and I don't think we need another operator. Obscure operators are really syntactic salt rather than sugar; most people don't like sigils. |
This comment has been minimized.
This comment has been minimized.
The downsides are undefined behavior and segfaults (best case.) If you want to make both things safe at the same time you have to disallow the wildcard in How would this work with types that deref to themselves? |
This comment has been minimized.
This comment has been minimized.
|
@thestinger fair enough. There does seem to be motivation for sugaring ref/deref somewhat though - |
This comment has been minimized.
This comment has been minimized.
|
@mahkoh I don't think having a borrow operator will lead to any more segfaults/undefined behaviour than the existing set of operators, in particular for your example, I don't see things getting worse just because you type one less The question about types which deref to themselves is an interesting one! I expect we would have to detect that statically and forbid using the borrow operator with such types. |
This comment has been minimized.
This comment has been minimized.
|
@nick29581 I think every piece of FFI code would have to use |
This comment has been minimized.
This comment has been minimized.
|
@thestinger I think the plan for unification would be:
@reem The problem is that "borrow" implies only one level of indirection, it simply means "borrow/take the address of the argument", and it doesn't mean "borrow the content in the most often used way". Though tempting, for different containers, the ways would be different, and they are hard to generalize into a single unary operator. But this operator will work for pointer-like types as those types have the same use pattern. |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 20, 2014
I don't see the need for another operator. Why does everyone want to make the language into a complicated mess? There's no appreciation for orthogonality and simplicity at all in the discussions on all of these proposals. There's only the desire to keep adding every feature under the sun. Language design is as much about which features are left out but Rust's community doesn't seem to get that. It's headed full force ahead to being a more complicated beast than both C++ and Scala and I seem to be the only person who cares. |
This comment has been minimized.
This comment has been minimized.
|
@thestinger Well personally I don't mind writing Anyway this is personal preference, and syntax sugar can either be added now or later, or never. |
This comment has been minimized.
This comment has been minimized.
buster
commented
Sep 20, 2014
|
Absolutely like the proposal. I don't care for confusion of C/C++ programmers. A good C/C++ programmer typically has enough technical background and understanding to get a new concept. But the goal should be to lower the barriers of entry for everyone. I very much prefer more convenience around borrowing to "non-confusion of group X". |
This comment has been minimized.
This comment has been minimized.
|
@buster Even if we don't care about C/C++ programmers (we should, the principle of least surprise and all that, let alone the fact they are our main target audience), using And I don't see how the concept of "magic borrow" (not "borrow" in the "take a reference to" sense, but the magic borrow proposed in this RFC proper) is easier to grasp for a programmer who has never been exposed to Rust or other systems programming language, as "magic borrow" or not, we'll have to tell him/her what a reference is first, anyway. And just saying "use the borrow operator" without an explanation doesn't seem like a good way to teach Rust, a systems language. |
This comment has been minimized.
This comment has been minimized.
thestinger
commented
Sep 20, 2014
|
@buster: Adding more complexity is not reducing the barrier to entry. The issue isn't that it would cause confusion for C++ programmers. It would make low-level code harder to write and would add more confusing magic to the language. It wouldn't result in any significant improvements to convenience. |
This comment has been minimized.
This comment has been minimized.
phaylon
commented
Sep 20, 2014
|
I do agree that with the proposed changes writing borrowing code can be much easier. However, I'd say that reading code with explicit dereferences for borrows is much more informative about what the code is actually working on. For method calls, implicit dereference is great, but there you have the context of the method name you call. With a borrow operator, you have much less context. |
This comment has been minimized.
This comment has been minimized.
|
With the borrow operator here and
Honestly I think the situation is sub-optimal. We need a coherent solution. |
This comment has been minimized.
This comment has been minimized.
I don't think |
mahkoh
reviewed
Sep 22, 2014
| I believe the advantages of this approach vs an implicit coercion are: | ||
|
|
||
| * better integration with type inference (note no explicit type in the above | ||
| example); |
This comment has been minimized.
This comment has been minimized.
mahkoh
Sep 22, 2014
Contributor
Your proposal doesn't seem to do any inference at all because it simply derefs as much as possible without looking at the context in which the reference is use. Proposal #241 on the other hand does look like it's doing type inference and the only reason you have to use y: &T in the example above is that y isn't constrained further. I'd expect let y = x; to create a &Rc<T> as expected and
fn foo(x: &Rc<T>) {
bar(x);
}
fn bar(x: &T) { }
to just work.
This comment has been minimized.
This comment has been minimized.
nrc
Sep 22, 2014
Author
Member
That's correct, as long as there is some more context to infer the type from. In practice you sometimes need to give the inferencer some hints
mahkoh
reviewed
Sep 22, 2014
| example); | ||
| * more easily predictable and explainable behaviour (because we always do | ||
| as many dereferences as possible, c.f. a coercion which does _some_ number of | ||
| dereferences, dependent on the expected type); |
This comment has been minimized.
This comment has been minimized.
mahkoh
Sep 22, 2014
Contributor
I think we've already seen that this proposal together with FFI creates unpredictable results. With proposal #241 the following should always work as expected:
&x as *const _ as *const _
because there are no constraints that force &x to "coerce" to another type.
#241 is still possibly bad because of similar FFI reasons.
This comment has been minimized.
This comment has been minimized.
|
Rust is a safe language and sometimes more verbose than other languages to make this possible. One of the few places where Rust is not safe is FFI code and every change that makes unsafe code even less safe and even less predictable should have a very good justification. The rust repo contains over 452k lines of Rust code (including comments) and only 1.7k lines (0.38%) contain "&", over 1k of them in librustc (111k LOC.)⁂ This "borrow problem" seems to be nothing but a mild inconvenience. &** looks a bit ridiculous but that's really all there is to it. ⁂ When you remove librustc and only leave the libraries you can see that &* occurs about once every 430 lines. |
This comment has been minimized.
This comment has been minimized.
|
@mahkoh, thanks for the statistics. It seems that the need of the borrow operator is not that urgent. And After I noticed that #235 has yet another different take on the meaning of On the other hand, the autoderef problem in |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
|
@nick29581: I'm using &mut Vec quite often to have multiple functions push into the same vector, but maybe that's just me. |
This comment has been minimized.
This comment has been minimized.
45ujksy5l03jcvf
commented
Sep 22, 2014
Three definitions of borrow, Deref for String and Vec, and thestinger seems to be the only person who cares.. |
This comment has been minimized.
This comment has been minimized.
ben0x539
commented
Sep 22, 2014
|
I'm also not a fan of this plan and would rather just have cross-borrowing an similar magical behavior go away at least for 1.0, but I don't really have any better arguments than thestinger, and me grouching on every RFC that I don't think adds more convenience than complexity doesn't help anyone. If the core team wanted an opinion poll on RFCs they'd probably use a more appropriate platform for that than pull request comments. |
This comment has been minimized.
This comment has been minimized.
|
@45ujksy5l03jcvf, well I think many cares. Reading through the recent RFCs and having some thoughts makes me wonder whether we are increasing language complexity for questionable gain, and whether we are dealing with complexity by piling up more complexity, because we asked the wrong question. And I'll admit maybe I've fallen victim to the temptation of feature creep. I'll try to get out. :) |
This comment has been minimized.
This comment has been minimized.
|
I think the following three questions are related:
#245, this, the coercion proposals and the autoderef problem of |
This comment has been minimized.
This comment has been minimized.
45ujksy5l03jcvf
commented
Sep 23, 2014
|
@CloudiDust |
This comment has been minimized.
This comment has been minimized.
|
@45ujksy5l03jcvf, in case it's not clear, I was agreeing with @thestinger too. :) I think your definition of I think part of the problem is, we cannot come up with a better name for the new concept so |
This comment has been minimized.
This comment has been minimized.
|
Some data and a little discussion: https://gist.github.com/nrc/809614adb2bbb38232b7 |
nrc
added some commits
Oct 15, 2014
This comment has been minimized.
This comment has been minimized.
|
Closing - we don't have time to do this pre-1.0 (and we're not sure if we even want to) and it would be too big a breaking change for post-1.0. |
nrc commentedSep 19, 2014
Change the address-of operator (
&) to a borrow operator. This is analternative to #241 and #226 (cross-borrowing coercions). The borrow operator
would perform as many dereferences as possible and then take the address of the
result. The result of
&exprwould always have type&TwhereTdoes notimplement
Deref.