New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Objects of type T should be implicitely convertible to &T #137
Conversation
A comment: Note that for your local variable example, while you cannot write this today: let x: int = 5;
let y: &int = x; you can write this today: let x: int = 5;
let ref y: int = x; which has the same effect of making But of course I suspect your driving motivation here is not the local variable case, but is instead the other case: argument passing. The choice to do auto-borrow and auto-deref magic on the receiver in I personally prefer the status quo over this change, but that may be because I consumed the "dot is magic" kool-aid early on. |
There are two other areas of trouble that I am not sure this RFC as written addresses:
In some cases I might want to make a Here is a concrete example of what I am talking about: extern crate debug;
fn main() {
let mut v1 = Vec::new();
let mut v2 = Vec::new();
let x = 3; let y = 4;
v1.push(1); v1.push(2);
v2.push(&x); v2.push(&y);
println!("v1: {:?}, v2: {:?}", v1.as_slice(), v2.as_slice())
} resulting in the following run transcript:
If I understand the RFC correctly, after this change is put in, I might choose to rewrite the above push invocations as follows:
and perhaps even this would be accepted:
and one might think this should work as well:
and I think any of these should work and produce the same results, assuming I have at least added appropriate type annotations to the bindings of But without those type annotations on |
@pnkfelix: If I'm correct the way type inference works today is that the type is inferred depending on by the order of the function calls. So in the second example, when you write In the third example, the compiler will determine that
|
I think this hurts readability. With the explicit |
@Tomaka17 wait, I do not understand. Are you saying that you don't think this should work under your RFC ? extern crate debug;
fn main() {
let mut v1 : Vec<int> = Vec::new();
let mut v2 : Vec<&int> = Vec::new();
let x = 3; let y = 4;
v1.push(1); v1.push(2);
v2.push(x); v2.push(&y);
println!("v1: {:?}, v2: {:?}", v1.as_slice(), v2.as_slice())
} The above is what I meant when I said: "assuming I have at least added appropriate type annotations to the bindings of v1 and v2." |
@gsingh93 Right now when you call |
@pcwalton Oh, I didn't get this correctly. |
In the past we have resisted this. The majority of C++ programmers that I've spoken to seem to think that being forced to explicitly reference is a feature (those same programmers have never complained about the autoref behavior of method calls). It may very well be that this asymmetry is valuable, and that seeking fundamental consistency between method calls and function calls is a boondoggle. However, I'm very sympathetic to the issue of exposing consistent APIs among functions that are commonly used together, such as Ultimately I'd like to see more consideration of how this would interact with our other automatic borrowing rules. We already have autoderef, and we have a near-universal desire for auto-cross-borrowing sometime in the future. For those not aware, auto-cross-borrowing makes stuff like this possible: fn foo(_: &int) {}
let x = box 2;
foo(x); // with auto-cross-borrowing
foo(&*x); // without auto-cross-borrowing In some ways, auto-cross-borrowing is morally equivalent to autoref, just with a layer of indirection. I dunno. I really want to run this idea by some heavy C++ users. |
Requiring blah(&mut foo) might be nice to make it clear what is written, really what you need to know at a glance is whats modifiable, its would be nice to just assume an immutable input is going to be either a value or reference depending on whats sensible, so auto borrow would be nice overall. is it something that could be done experimentally with a feature gate are there any weird cases like moving a pointer vs passing a reference to a Box that might stop being clear ? it is the case that rust' pointer safety does away with the need for C++ T* vs T& and hence -> vs . ... feels a bit strange initially but the error messages help you get used to it. |
This proposal also has some slightly strange interaction with let mut x: int = 1;
let a = foo(x);
x = 2; I would expect that |
I do not understand this reasoning. |
@almale : That's not an issue with the language, just a personal preference. I think it should be clear when an object will be modified. |
@Tomaka17:
Of course. |
This feature will encourage people to add an `&` before their parameter type whenever they only need to read the value instead of consuming it. | ||
For example the `Vec::get` function would have the `fn get(&self, &uint)` definition instead. | ||
|
||
Note that this only concerns `&T`. The `&mut` syntax should still be mandatory when passing an object by mutable reference, because it explicitly says that the variable will be modified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that (some) variables passed by &
can still be modified. http://is.gd/x2XQMn
use std::cell::Cell;
fn foo(x: &Cell<uint>) { x.set(2) }
fn main() {
let x = Cell::new(1);
println!("{}", x); // 1
foo(&x);
println!("{}", x); // 2
}
Closing. There are a number of unsolved technical problems here and no agreement that this is something we want. Thank you. |
I think this proposal could been modified to only implicitly convert In the case where a function takes For example, when we take fn foo<T: std::fmt::Show>(a: &T) {
println!("{}", a);
}
fn main() {
let x = "hello!";
// '&' is required
foo(&x);
foo(&x);
} When we take fn foo<T: std::fmt::Show + Copy>(a: T) {
println!("{}", a);
}
fn main() {
let x = "hello!";
// '&' is optional
foo(x);
foo(&x);
} |
The problem @zwarich points out can be solved by only making let mut x: int = 1;
let a = foo(&x); // '&' is required when the argument has a lifetime
let a = bar(x); // '&' is optional when the argument has a not a lifetime
x = 2; |
This rule also solves the problem @pnkfelix pointed out with inferring the type: extern crate debug;
fn main() {
let mut v1 = Vec::new();
let mut v2 = Vec::new();
let x = 3; let y = 4;
v1.push(1); v1.push(2);
// This would not be allowed since it the argument is not anonymous lifetime
v2.push(&x); v2.push(y);
println!("v1: {:?}, v2: {:?}", v1.as_slice(), v2.as_slice())
} |
Registering unpark is the responsibility of the callee
Rendered view