I don't follow this. The only operations that require allocation or access to "capacity" are ones that modify the Vec, but you can't do those with a &Vec, anyway. You can still read and iterate through the elements, and even clone the Vec if you need a mutable copy.
Unless you mean Vec couldn't be made Copy, but it wouldn't have to be. The only restriction would be that it could not contain internal mutability (no Unsafe elements, as tbu mentioned), which it doesn't.
@P1start You are right that the idea can be separated into two proposals, but I donāt think they are unrelated. The second, for dropping the special types &str and &[T]` requires the first, and I think it makes the first idea very compelling.
As @tbu and @rkjnsn noted, thereās no need for Copy, the only requirement is that there wonāt be any Unsafe elements.
As @rkjnsn said, I donāt think thereās a problem with using &Vec and &String in code without allocation. String and Vec could be defined in core, along with the operations on &Vec and &String which donāt require allocation. Methods which create a new Vec or String, which require allocation, would be defined in std, where allocation is available.
@rkjnsn Operations that return a view into an object could be implemented just like they are implemented now, using mem::transmute. For example, the current implementation of Vec<T>::as_slice is:
I still donāt see why the first proposal is necessary for the second. Right now &str is represented as (ptr, len). With your second proposal, &String is represented as str where str points to (ptr, len). Sure, this is less efficient, but I donāt think itās strictly required. And if the & optimisation is required, how do you handle &mut [T] with your system (because there is no such optimisation for &mut)? The #[not_in_view] optimisation also seems unnecessary (but obviously useful).
With those two additions (#[not_in_view] and by-value views/references), &String is now significantly different from String. It doesnāt have the capacity field, and itās not actually a reference to a string as the name suggests. What youāve effectively done is renamed &str to &String, which is both a good and a bad thing. Good: itās less confusingāthere is only one ābaseā string type. Bad: itās inconsistentā&String is basically just a String but without a capacity field, and when allocation is not available, String is not usable, while &String is. At the same time, youāve added a new pointer type (or really, renamed one and added another), and Ref<'a, T> could easily turn out to be more common than the new &'a T and is counter-intuitive to what people expect (& is a reference (effectively a pointer AFAIK) in C++, and is used to construct a pointer in C/C++).
I can see the advantages, but Iām not sure they outweigh the disadvantages. Itās certainly a very interesting and original idea, though.
You are right that it's not strictly required, but as Rust is a systems programming language, no one would agree to add an unneeded level of indirection just for the sake of consistency. So keeping the same performance is very important.
I haven't thought about &mut [T]. I wonder how frequently it's being used. Perhaps it can be replaced with a struct that contains a &mut T reference to the first element and a length? If it can't, then my proposal doesn't handle &mut [T]. Too bad!
I have dropped my idea about Ref<'a, T>, to avoid extra complexity in the language. So now &Foo would always be a view of Foo, and would always behave the same way, but may be implemented either as a value copy or as a pointer, according to the type attributes. I agree that it would make the implementation more opaque than it is today. It can be seen as an optimization - think about &Foo as a pointer to Foo, and if the author of the library thought that it would be more efficient to pass it by copying the bytes, it would work that way.
I don't see &String and String as inconsistent. According to my proposal, &String would have just the same functionality as &String has now - you won't be able to distinguish between the two without size_of. It's just an optimization - if you are not allowed to mutate the object, you have no interest in the capacity, so there's no need to copy it.
Edit: Currently Vec has a method pub fn capacity(&self) -> uint, which would of course not work with my proposal. From what I can see, it could be replaced by pub fn capacity(&mut self) -> uint without causing any trouble.
This seems somewhat contradictory to me. If the library implementation has to rely on &Vec<'a, T> having a specific layout, then passing & references by value becomes more than just an implementation detail. It is, in effect, another type. I think this has the potential to be confusing, and the possibility of having two &Vec<'a, T>s that point to the same vector and yet are not equal (one being a slice) is semantically weird. I also don't like the idea that the semantic meaning of &Type depends on a attribute on Type
All that said, I do like the idea of the compiler being able to automatically pass small-enough & references by value as an optimization for types without Unsafe. Is there currently anything in the language definition that would prevent a conforming compiler from doing this, today?