Closed
Description
Currently, if I have a function like this:
fn hello(world: &World) {}
I can call it like this:
hello(~World{})
But not like this:
hello(World{})
// I would have to do
hello(&World{})
According to @alexcrichton, this is because the first example automatically coerces the ~World into a &~World, but the second example does not.
The lack of symmetry here is somewhat confusing, and makes it hard to understand (at least at first), how exactly borrowed pointers work with different callers.
Metadata
Metadata
Assignees
Labels
No labels
Type
Projects
Relationships
Development
No branches or pull requests
Activity
alexcrichton commentedon Nov 15, 2013
I have also found this annoying for locally understanding a function
Does that function call move
a
or does it not? Turns out it depends on the declaration offoo
.I think that an interesting exercise for this issue would be removing autoref/autoderef for function arguments. I have a feeling that the change would be incredibly painful and probably not worth it, but this is probably something that should at least be considered.
The alternative would be
Which does look a bit silly, but it is clear to all who read it that the argument is not moved, but rather borrowed. I'm not personally a big fan of this syntax.
Another idea of how to go about this would be to very clearly document this in the tutorial/manual. This may be done already, but I'm not sure how well autoref/autoderef is covered overall.
tomdale commentedon Nov 15, 2013
👍
wycats commentedon Nov 15, 2013
@alexcrichton I would be ok with either always requiring
&
or never requiring it, but not the current mix, even if covered better in the docs.The problem is that in order to explain the current behavior (which is fundamental, even at an early stage), you have to explain borrowing, then the
&T
syntax, and then explain that the compiler automatically coerces~T
, so you don't have to do&~T
.In contrast, if the behavior symmetrically required
&
, you would teach borrowing, and then say that you need to do&T
to pass any pointer to the function that wants to borrow it.If the behavior symmetrically did not require
&
, you would teach borrowing, and then say that you can pass any pointer to a function that wants to borrow it.Either of those are better than the current sometimes-coerces approach, I think.
nikomatsakis commentedon Nov 15, 2013
I would like to remove argument coercion -- well, specifically "autoborrowing." Nominating.
nikomatsakis commentedon Nov 15, 2013
Reading the comments and discussing with @wycats, I hadn't considered the possibility of extending the autoborrow to encompass
T -> &T
(what we call "autoref" in a method call). Hmm. I think I'd rather err on the side of less magic at the moment, but that is an interesting possibility that wouldn't be too hard to do, and maybe it means less annoying little&
.Still, what I dislike most about the autoborrowing is that I can't tell from a call site when I am passing by value and when I am not. That is, I dislike seeing this:
because it looks to me like
x
has been given away. But in fact then I look atfoo
and find:and so I see that in fact
x
was only borrowed. I think I find it easy to understand the flow of ownership if I don't have to look at the declarations of the fns being called. YMMV.nikomatsakis commentedon Nov 15, 2013
Something else to consider if we extended autoref: what about
&mut
?It seems odd to me that
foo(x)
might mutatex
.And would we allow
foo(rvalue())
? We currently do permit rvalues to be implicitly autoref'd in a mutable way because of iterators. That implies one could callinc(3)
which would allocate a temporary, put 3 in it, callinc
which mutates the temporary (to no visible effect). Maybe that's ok.alexcrichton commentedon Nov 15, 2013
I've added this to the meeting agenda.
wycats commentedon Nov 17, 2013
Here's another similar issue I ran into today when trying to explain things to someone:
In this case, the use of
self.first_name
inside ofget_first_name
was a compiler error:The solution was to do:
Since I have a
~str
here and I need a&str
, which has strictly more restrictions, I would expect Rust to automatically demote the~str
to a&str
.As a side note, orthogonal to this point,
as_slice()
is a somewhat leaky abstraction, because it exposes details about the implementation of~str
and&str
that aren't necessary to reason about the ownership semantics (and the ownership semantics tell you everything you need to know about the relative cost of &str vs ~str).huonw commentedon Nov 17, 2013
I imagine it's just a typo, but
in the original bug is incorrect,
~World
is being borrowed to a&World
(i.e. it's pointer -> pointer, not auto-ref).nikomatsakis commentedon Nov 18, 2013
Currently we coerce for arguments and struct field initializers. We ought to coerce in return expressions too. I'm not sure if there are other missing places. Worth opening a separate bug for return expression coercion, if one doesn't already exist.
asb commentedon Nov 19, 2013
@nikomatsakis I rather liked the idea of the invariant you stated in a blog post a while back. Namely "you don’t have allocation unless you see a sigil". The example you gave, and the automatic coercion of arguments in general seems to break this which is unfortunate, and I think a good argument against the coercion.
huonw commentedon Nov 19, 2013
@asb I assume @nikomatsakis is only talking about coercion to a reference (
&
), which is never an allocation (the quoted example would be putting3
on the stack).lilac commentedon Nov 20, 2013
@alexcrichton @nikomatsakis The problem you raised, whether a function call moves a unique pointer or not is not explicit, is annoying. But I think the source of this problem is the implicit move semantic, auto-borrowing just intensifies it. If you want easy understandings of the flow of ownerships, then maybe we should make the move semantic explicit, just like C++'s std::move.
I dislike the &* way, since it just makes the language more ugly, and borrowing a heavy burden.
lilac commentedon Nov 20, 2013
@wycats I try to explain why ~T can be automatically cast to &T but not T. Rust distinguishes between values and pointers, thus it is reasonable to cast a ~T (unique pointer) to a &T (borrowed pointer). In contrast, the cast from a T (value) to a pointer (&T) isn't that consistent in design, although we could consider this.
In C++, a &T actually is an alias, so it makes sense to accept a T when requires a &T.
thestinger commentedon Nov 20, 2013
Rust doesn't have copy constructors, so the alternative to not moving by default is not having the ability to copy types with destructors.
2 remaining items
pnkfelix commentedon Nov 21, 2013
We need to decide what we're going to do for 1.0. P-backcompat-lang.
pcwalton commentedon Jun 9, 2014
I propose taking RFC rust-lang/rfcs#112 and doing nothing else.
Box
es to&mut
. #15171pcwalton commentedon Jun 25, 2014
PR #15171 removes the mutable
Box
to&mut
borrow. But I believe, based on @huonw's statistics, that we should remove the borrow fromBox
to&
as well, as it will break little code. So I would like to propose another RFC before we mark this as closed.auto merge of #15171 : pcwalton/rust/remove-cross-borrowing, r=brson
pcwalton commentedon Jun 26, 2014
New RFC: rust-lang/rfcs#139
pcwalton commentedon Jul 2, 2014
Nominating for closure in favor of #15349, which I believe adequately captures what we'd like to do here for 1.0.
pnkfelix commentedon Jul 3, 2014
Closing in favor of #15349 as suggested by @pcwalton
Fix rust-lang#10504, don't lint on derived code
Auto merge of rust-lang#10894 - Centri3:type_repetition_in_bounds, r=…