So I want to write a custom wrapper type around any sorts of boxed slice types, like a fancier Rc<[T]>. Naively, I'd do something like
However, there's a problem with that: it wouldn't work for str, because FancySlice<str> wouldn't be equivalent to Rc<str>. But I do want str to be a first-class citizen. Same thing for other slice-like newtypes like OsStr.
So I'd like to have something like struct FancySlice<T>(Rc<T>), but now which trait bound do I use to make sure T is actually a slice type?
If you need trait bounds for a set of types that aren’t sufficiently abstracted over already using standard library traits alone, you can always define your own trait. struct FancySlice<S: MySliceTrait + ?Sized>(Rc<S>);
So I tried the option of using bounds only where needed, and while being a bit verbose it got me pretty far already. However, I'm increasingly convinced that a Slice trait would be something good to have in std. It would increase API consistency: For example, as_ptr_range only exists on slices and not str, and OsStr does not even provide as_ptr. from_raw_parts is equally only provided for actual slice types. Of course you can always combine raw parts of a byte array and then convert to str later (as even shown in the documentation: str - Rust), but this only works if you know the concrete type you are working with. There is no way to abstract over that functionality.
There is another benefit a Slice trait would have: Currently, if the user provides a wrong type, they'll get a long type error that <P as Deref>::Target does not satisfy Index<R, Output = <P as Deref>::Target> or something, and only when calling certain methods. With a trait, the error message could be as simple as "this type does not implement Slice, and already in the constructor.
Yes I have thought about using Pointee<Metadata = usize> for that use case, but I am not comfortable with the assumption that any such pointer types look and behave like slice types. Any function relying on this would have to be marked as unsafe in order to not be unsound. Therefore, such a trait needs to be explicitly implemented by the relevant types.
Future language/std design is better suited to IRLO, but is there anything else you're looking for help accomplishing using Rust today?
(from_raw_parts is already unsafe, by the way, so no loss there. The OsStr considerations are probably because it wasn't even officially decided to own up/commit to the underlying representation always being bytes until quite recently.)
That's no benefit. If you think you want a slice, and we ask why you want one, and you reply "because I want to index into it", then you don't really want a slice. You want precisely something that implements Index instead.
You should be using generics for enforcing and using capabilities, not for restricting to concrete types.
Well, you can build a safe abstraction around it in general. But with Pointee<Metadata = usize> you cannot, because its soundness relies on the assumption that the type actually behaves like a slice, and the caller must uphold it, which is outside of my control as library author.
Well I do want a slice, because I want to do all the things one can do with a slice. Yes I also want to index into it, and I successfully used Index for that.
The thing I need to do now is to check whether a slice is a true sub-slice of another, as in "pointing within a specific memory region" and not "contains the slice based on Eq". For that I'd need a trait that gives me either a pointer+len or a pointer range. I don't know what you'd call it from a capabilities perspective, but I'd call such a trait Slice. (Or maybe such functionality actually belongs to Index/SliceIndex in a way. It's interesting that the trait allows you to get the entire slice, but won't tell you its actual length. This also means that you can't index into the last element, for example. Also I'm not sure but one could probably implement Index for non-slicelike types like linked lists)
(Pointee<Metadata = usize> does not give me the length, it gives me a usize which happens to be the length for some types, but there's no semantic guarantee associated with it which I'd need for using it safely.)