This is probably nonsense, but might invoke an interesting conversation. Or perhaps it’s not nonsense?
Niko Matsakis, in his excellent presentation, said that Rust is about control without compromising safety. So you can embed the contents of a field in the contents of a struct, instead of holding a pointer to the heap. According to the same philosophy, when you transfer x
to a function it is done by copying its bytes and not by passing a pointer to it, since this extra level of indirection will usually cost more than the duplication of the bytes.
However, if x
has a destructor, you can’t pass it to a function by value, unless you’re willing not to use x
any more. For example, many file-related functions expect a &Path
, which is basically a pointer to a pointer and a length. I’m pretty sure most programs will run faster if a function would just receive a pointer to the data and a length. It is possible with two special types: if you have a String
you can pass s.to_slice()
, and the same goes for a vector. But I don’t know of any way to do this with any other type.
It just seems to me that performance-wise, most programs would benefit if instead of passing &T
as a pointer, it would be passed by copying the bytes of T
. This would work as long as T
doesn’t include any Unsafe
fields (directly or through other fields). For those types, and for cases where performance-wise you’d prefer a pointer over a copy, you’d use, say, Ref<'a, T>
, which would be a new name for today’s &'a T
.
Because this would make calling &T
“a reference” a bit strange, I’ll call it “a view”.
You can add another feature, which would make the special types &str
and &[T]
unneeded. If, in a struct definition, a field is prefixed by the keyword onlyinmut
, it would not be copied to &T
and would not be accessible from it. This would mean that the definition of Vec
would become:
pub struct Vec<T> {
ptr: *mut T
len: uint,
onlyinmut capacity: uint,
}
Function which currently receive a &[T]
would receive a &Vec<T>
, which would be exactly equivalent to today’s &[T]
. And when calling them, you’d just use &v
instead of today’s v.as_slice()
. This would make Rust’s type system simpler (as there would be no &str
and &[T]
), and would make string handling simpler - no need to have &str
, String
, Str
, and &String
(which is legal but shouldn’t be used) - there would just be String
and &String
, like with any other type.
(It might make sense to force onlyinmut
fields to be private, so the difference in representation between T
and &T
would remain an implementation detail, an optimization, and won’t bother users of the type)
If the meaning of &T
changes that way, it would make perfect sense to not require explicit referencing of function arguments - that is, you’d be able to write open(path)
instead of open(&path)
. It would make sense since the performance characteristics of passing a view would be just those of passing a value. The only difference is that you’d still be allowed to use path
afterwards.
This would make iteration simpler. For example, it would make the following code work:
for path in paths.iter() {
tmp_path = path.plus_suffix(".tmp");
rename(path, tmp_path).unwrap();
}
(Currently you have to prefix tmp_path
with a &
for this to work, which I find a bit ugly.) You’d also be able to just use .iter()
in most cases, whereas today you’d usually want to use .move_iter()
if it’s possible.
To summarise, this would make views more similar to simple values, which would make Rust more performant, more consistent, and easier to use.
What do you think? Where is my crucial mistake?
Cheers, Noam