In general, I’m not a fan of &*, and I like auto-borrowing (autoref sounds like it turns T into &T, not ~T into &T). I understand the arguments to get rid of it though.
BTW, you said that the current proposal still includes autoref for function invocation. That sounds to me like autoref would still apply to x in foo(x). Is there something else you mean by that? As I see it, the two interesting cases to consider for removing it are: 1. let x = ~Foo; foo(x); // looks like it moves x when it just borrows it 2. let mut a = ~[ … ]; sort(a); // mutates a To me, the second case seems pretty clear that it shouldn’t auto-borrow a ~T to a &mut T, and instead require sort(&mut *a). It’s pretty ugly, but it makes it clear what’s going on. This case isn’t particularly common, which is why losing autoref isn’t very tragic. The first case is the most common case, and it seems a shame to require &*x everywhere. I understand the argument that it looks like x is moved until you read the function signature of foo(), but I’m not convinced that’s important enough to uglify the code. Arguably, you already have to know what foo() does to understand what the code is doing. The basic argument, as I see it, is that the type signature of foo() shouldn’t change how the calling code treats its argument. If foo() changed from `fn foo(_: &T)` to `fn foo(_: ~T)` then `x` would start being moved instead of borrowed, which could cause errors in subsequent code in the caller. But the problem with this argument is that the type signature of foo() already changes how the calling code treats its argument, in a fashion that will surface errors in later code rather than at the call site. Notably, if the argument is not fully constrained to a specific type by the time foo(x) is called, it will further constrain the type that may not cause a problem until later. For example: fn foo(_: uint); fn bar(_: uint); let x = 1; foo(x); bar(x); This compiles just fine, but if foo() changes its argument to `int`, then the call to bar() will fail with a type error. More complex situations can be created using generic functions as well. In the case of autoref, the question is whether the value will be moved, rather than the type of the value, which is a slightly different issue but I’m not convinced it’s different enough that it needs to be specially addressed. The other problem with this is that, even with autoref removed, you can still hit the same problem of a seemingly-benign change elsewhere causing a compiler error in your code. Namely, if a struct has no destructor, and you rely on the ability to implicitly copy it (e.g. calling bar(x) where foo is fn bar(_: T)), then adding a destructor to the struct will cause your code to fail to compile, in just the same way that foo changing from `fn foo(_: &T)` to `fn foo(_: ~T)` will today. Overall, there just doesn’t seem to me to be a compelling reason to remove the ability to auto-borrow ~T into &T. Removing the ability to auto-borrow ~T into &mut T is more sensible though. FWIW, a lot of why I like auto-borrowing is actually due to auto-slicing of ~[T] into &[T] and ~str into &str. But I assume that when (if) DST happens, auto-slicing will just be a case of auto-borrowing. And until that happens I’d really like to avoiding having to say foo(v.as_slice()). -Kevin On Nov 19, 2013, at 2:08 PM, Alex Crichton <[email protected]> wrote: > Hello rust-dev! > > Everyone's had their fair share of issues with autoref and autoderef, > and it's worth considering removing certain portions of it from the > compiler. The discussion around this has been rooted in the past, but > has recently been brought up as part of > https://github.com/mozilla/rust/issues/10504. > > The current proposal is to remove all autoref except for function > invocations and indexing operations. The method of creating &T from ~T > would be `let foo: &T = foo` or `&*foo`. Vectors and strings can't > currently benefit from the `&*foo` syntax, but they will hopefully be > able to do such once DST lands. In the meantime coercion via type > ascription will work and they also have `as_slice` methods. > > There are a few reasons backing this proposal: > > 1. It's inconsistent to have magical autoref in some places, but not > in other places. > 2. The camp of "less compiler magic is better" can fly their flag over > this change. > 3. Code readability does not necessarily benefit from autoref on arguments: > > let a = ~Foo; > foo(a); // reading this code looks like it moves `a` > fn foo(_: &Foo) {} // ah, nevermind, it doesn't move `a`! > > let mut a = ~[ ... ]; > sort(a); // not only does this not move `a`, but it mutates it! > > The basic idea is that reasoning about code is no longer a local > function decision, but rather you must understand all the pointer-ness > of the called signatures to understand when a move happens or not. > With no autoref, the code would look like > > let a = ~Foo; > foo(&*a); // clearly not passing by value > > let mut a = ~[ ... ]; > sort(a.as_mut_slice()); // clearly loaning a mutable reference > > > That being said, this proposal is not yet set in stone. I don't think > that there are many people that are fans of `&*foo` or `&mut *foo`; > both cases look fairly ugly. So far the general agreement is that > local small ugliness is the price we pay for local readability, and > the discussions have been unable to unearth a better system. > > I'm curious if others have a better idea of how to go about doing > this, or if others just think it's a terrible idea in the first place. > _______________________________________________ > Rust-dev mailing list > [email protected] > https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
