Problem: I really wish I didn’t need any unsafe code, but didn’t see any way around this bit:
(There are other ways I could write the code, but they’re all unsafe.)
Question: Any good ideas for language or library changes we might do to make unsafe unnecessary there?
I don’t think I need custom DSTs here, @ubsan. I’m completely happy with the normal, your-last-field-DST-makes-you-a-DST support that we already have. (I don’t need different metadata from what the normal slice DST already has.) There’s just no constructor I can use.
I think you can let it return RevSliceRef<'a,T> and RevSliceMut<'a,T> instead? then you can just let it hold a reference to the original array. No need to use unsafe code.
I could, but then it doesn’t have the &mut RevSlice<T> -> &RevSlice<T> coercion, I can’t return it from Index/IndexMut, it doesn’t have auto-reborrow, I have to duplicate the methods because I can’t Deref, …
Separately, I believe the transmutes in your current implementation are currently both undefined behavior. https://github.com/scottmcm/rev_slice/issues/1
The RefCast derive catches missing repr(C) / repr(transparent).
Assuming we settle on guaranteeing the same memory layout for newtype structs as their one field (rust-rfcs/unsafe-code-guidelines#31), would it be possible to allow a safe `as` cast from &T to &U where U is a struct containing a single field of type T, so long as the field’s visibility makes it visible to the code containing the cast?
struct U {
t: T,
}
fn demo(t: &T) -> &U {
t as &U
}
Alternatively, the following would make it clearer that visibility is required.
Ooh, this is really interesting. It's like adding literals to our existing place expressions, and could easily extend to similar cases that currently need unsafe. For example, imagine something like this:
fn demo2<T>(t: &T) -> &[T; 1] {
&[*t]
}
In some ways it also feels like a generalization of rvalue static promotion: &[4] is 'static because it's like { static x: i32 = 4; demo2(&x) }.
Hopefully it could also support e.g. &U { t: *t, marker: PhantomData }.
Unfortunately there may be an ambiguity in the mutable case:
struct S(i32);
fn f(x: &mut i32) {
let s = &mut S(*x);
s.0 = 1;
}
fn main() {
let mut x = 0;
f(&mut x);
println!("{}", x);
}
This compiles today and prints 0. If &mut S(*x) where made equivalent to mem::transmute::<&mut i32, &mut S>(x) then this would begin printing 1 instead.
I’ve heard whispers that people are souring on `as`. Does that mean using `as` here is out of the question? It has the benefit of not conflicting with existing compilable code.
Here is a somewhat more confusing but unambiguous spin on the earlier place expressions suggestion. This is analogous to reference patterns like let &t = t.
As one of those people, I'm only soured on as when it does things that are different but look the same, in a way that accidentally doing the wrong thing is easy. Like where as *mut T might be an innocuous coercion from &mut T, but also might be a contributing factor to the bug that necessitated Announcing Rust 1.15.1 | Rust Blog
I'm unsure whether this would be one of the cases I dislike. I guess that since it's ref-to-ref it would be safe and pretty limited, and there are no other to-ref casts in as today, so it's probably fine.