How to idiomatically require a trait to retrieve a slice - rust

I'm working on a library that operates on [T] slices. I would like the library user to specify a type that can retrieve a slice in a mutable or immutable way.
My current lib defines 2 traits
pub trait SliceWrapper<T> {
fn slice(&self) -> &[T];
}
pub trait SliceWrapperMut<T> {
fn slice_mut (&mut self) -> &mut [T];
}
But the names slice and slice_mut seem arbitrary and not somewhere in the core Rust libs.
Is there a trait I should be requiring instead, like Into::<&mut [T]> ?
I'm afraid that Into consumes the type rather than just referencing it, so I can't simply demand the caller type implements core::convert::Into, can I?

The simplest way to answer this is to look at the documentation for existing types that more or less do what you want. This sounds like something Vec would do, so why not look at the documentation for Vec?
If you search through it looking for -> &, you'll find numerous methods that return borrowed slices:
Vec::as_slice
Borrow::borrow
Index::<Range<usize>>::index (plus for RangeTo<usize>, RangeFrom<usize>, RangeFull, RangeInclusive<usize>, and RangeToInclusive<usize>)
Deref::deref
AsRef::<[T]>::as_ref
Which of these should you implement? I don't know; which ones make sense? Read the documentation on each method (and the associated trait) and see if what it describes is what you want to permit. You say "that can retrieve a slice", but you don't really explain what that means. A big part of traits is not just abstracting out common interfaces, but giving those interfaces meaning beyond what the code strictly allows.
If the methods listed aren't right; if none of them quite convey the correct semantics, then make a new trait. Don't feel compelled to implement a trait just because you technically can.
As for Into, again, read the documentation:
A conversion that consumes self, which may or may not be expensive.
Emphasis mine. Implementing Into in this context makes no sense: if you consume the value, you can't borrow from it. Oh, also:
Library authors should not directly implement this trait, but should prefer implementing the From trait, which offers greater flexibility and provides an equivalent Into implementation for free, thanks to a blanket implementation in the standard library.
So yeah, I wouldn't use Into for this. Or From.

Related

Object safe generic without type erasure

In the trait Extendable below, I'd like to make app generic where it currently uses the concrete type i32.
At first blush, you'd think to use a generic type but doing so while keeping Extendable object safe isn't easy.
trait Isoextender {
type Input;
type Output;
fn forward(&self, v: Self::Input) -> Self::Output;
fn backward(&self, v: Self::Output) -> Self::Input;
}
trait Extendable {
type Item;
fn app(
&self,
v: &dyn Isoextender<Input = Self::Item, Output = i32>,
) -> Box<dyn Extendable<Item = i32>>;
}
There's a (really neat) type erasure trick (link) I can use with std::Any but then I'm losing type information.
This is one of those things that feels like it should be possible from my understanding of how Rust works but which simply might not be. I do see there's an issue with size. Clearly rust needs to know the size of the associated type Item, but I don't see how to solve with with references/pointers in a way that's both object safe and keeps the type information.
Is it the case that:
I am missing something and it's actually possible?
This is not possible today, but it may become possible in the future?
This is not likely to be possible ever?
Update:
So I suppose a part of the issue here is that I feel like the following is a generic function that has only 1 possible implementation. You should only have to compile this once.
fn pointer_identity<T>(v: &T) -> &T {
v
}
It feels like I should be able to put this function into a vtable, but still somehow express that it is generic. There are a whole class of easily identifiable functions that effectively act this way. Interactions between trait objects often act this way. It's all pointers to functions where the types don't matter except to be carried forward.
I found some document ion that may answer my question.
Currently, the compiler has two concepts surrounding Generic code, only one of which seems to be surfaced in the type system (link).
It sounds like this may be possible some day, but not currently expressible with Rust.
Monomorphization
The compiler stamps out a different copy of the code of a generic function for each concrete type needed.
Polymorphization
In addition to MIR optimizations, rustc attempts to determine when fewer copies of functions are necessary and avoid making those copies - known as "polymorphization".
As a result of polymorphization, items collected during monomorphization cannot be assumed to be monomorphic.
It is intended that polymorphization be extended to more advanced cases, such as where only the size/alignment of a generic parameter are required.

Why does the Rust standard library implement traits for both Thing and &Thing?

I was reading the question The trait `std::fmt::Write` is not implemented for `Stdout` when it should be where the asker noted that the rust documentation shows that the std::io::Write trait is implemented for both &Stdout and Stdout.
I don't understand why this is necessary or how you would use it. Isn't everything you define for Thing always implemented for &Thing? Why would you implement something for &Thing without implementing it for it's definition?
Isn't everything you define for Thing always implemented for &Thing?
No, an implementation for a type T will not automatically implement anything for &T. Now, sometimes blanket implementations can kick in, and if you pass a &&T to a function expecting a &T, then Rust will insert dereferences for you, but that does not mean the trait was implemented for &T, just that Rust helped you out a bit.
Why would you implement something for &Thing without implementing it for it's definition?
There's a very good example of that which we use all the time: String::from.
impl From<&str> for String {
fn from(value: &str) -> String {
...
}
}
From::<T>::from takes an argument, by value. No references or anything, just straight-up a value of type T. So we can never write a From<str> implementation for anything, since str is unsized and hence cannot be a function argument on its own. But it makes perfect sense to convert a &str to a String: that's just making an owned copy of the string.
Building on #Silvio-Mayolo's answer: in this particular case, there was originally just an implementation for Stdout; and the more-flexible implementation for &Stdout was added later, which provides a strict superset of functionality, notably allowing you to share the reference between threads. The original implementation can't be removed without breaking backwards compatibility with existing code using the by-value implementation (auto-deref isn't perfect, you would still need to go back and add an & in some situations).

Why can't RefCell be used as a self parameter

You can do this:
impl Foo {
fn foo(self: &Rc<Self>) {}
}
But not this:
impl Foo {
fn foo(self: &Rc<RefCell<Self>>) {}
}
The former is quite useful - e.g. I can have methods return objects containing weak references to self. But because I can't use RefCell I can't return anything that would mutate self.
There are ways around this (e.g. wrapping the whole struct in RefCell internally) but none as convenient for my current task as just allowing self: &Rc<RefCell<>>.
The grammar allowed is described here. It allows Box, Rc, Arc and Pin but not RefCell. Why?
As of the time of this writing, in August 2022, method receiver type support is still fairly limited to a handful of types, and compositions of those types. You've noticed that RefCell is not among them.
arbitrary_self_types, a feature for expanding the possible types of self, has a tracking issue for discussing its implementation. From the discussion, it seems that this feature is currently targeting types that implement Deref[Mut], which RefCell does not implement. A few comments point out that this is limiting though, so there's still a possibility.
Ultimately, I think the answer is that RefCell does not work because the full-fledged feature hasn't been designed thoroughly enough yet. The Rust team likely implemented the basic feature so that we could have Pin as a self type and thus make futures work, and added a few other "easy" types to get more bang for their buck, but a proper and generic implementation has been deferred.

How does Arc access the underlying object without an extra field? [duplicate]

I often use the newtype pattern, but I am tired of writing my_type.0.call_to_whatever(...). I am tempted to implement the Deref trait because it permits writing simpler code since I can use my newtype as if it were the underlying type in some situations, e.g.:
use std::ops::Deref;
type Underlying = [i32; 256];
struct MyArray(Underlying);
impl Deref for MyArray {
type Target = Underlying;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let my_array = MyArray([0; 256]);
println!("{}", my_array[0]); // I can use my_array just like a regular array
}
Is this a good or bad practice? Why? What can be the downsides?
the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.
— std::ops::Deref
I think it's a bad practice.
since I can use my newtype as if it were the underlying type in some situations
That's the problem — it can be implicitly used as the underlying type whenever a reference is. If you implement DerefMut, then it also applies when a mutable reference is needed.
You don't have any control over what is and what is not available from the underlying type; everything is. In your example, do you want to allow people to call as_ptr? What about sort? I sure hope you do, because they can!
About all you can do is attempt to overwrite methods, but they still have to exist:
impl MyArray {
fn as_ptr(&self) -> *const i32 {
panic!("No, you don't!")
}
}
Even then, they can still be called explicitly (<[i32]>::as_ptr(&*my_array);).
I consider it bad practice for the same reason I believe that using inheritance for code reuse is bad practice. In your example, you are essentially inheriting from an array. I'd never write something like the following Ruby:
class MyArray < Array
# ...
end
This comes back to the is-a and has-a concepts from object-oriented modeling. Is MyArray an array? Should it be able to be used anywhere an array can? Does it have preconditions that the object should uphold that a consumer shouldn't be able to break?
but I am tired of writing my_type.0.call_to_whatever(...)
Like in other languages, I believe the correct solution is composition over inheritance. If you need to forward a call, create a method on the newtype:
impl MyArray {
fn call_to_whatever(&self) { self.0.call_to_whatever() }
}
The main thing that makes this painful in Rust is the lack of delegation. A hypothetical delegation syntax could be something like
impl MyArray {
delegate call_to_whatever -> self.0;
}
While waiting for first-class delegation, we can use crates like delegate or ambassador to help fill in some of the gaps.
So when should you use Deref / DerefMut? I'd advocate that the only time it makes sense is when you are implementing a smart pointer.
Speaking practically, I do use Deref / DerefMut for newtypes that are not exposed publicly on projects where I am the sole or majority contributor. This is because I trust myself and have good knowledge of what I mean. If delegation syntax existed, I wouldn't.
Contrary to the accepted answer, I found out that some popular crates implement Deref for types which are newtypes and aren't smart pointers:
actix_web::web::Json<T> is a tuple struct of (T,) and it implements Deref<Target=T>.
bstr::BString has one field typed Vec<u8> and it implements Deref<Target=Vec<u8>>.
So, maybe it's fine as long as it's not abused, e.g. to simulate multi-level inheritance hierarchies. I also noticed that the two examples above have either zero public methods or only one into_inner method which returns the inner value. It seems then a good idea to keep the number of methods of a wrapper type minimal.

Why does T not implement AsRef<T>?

This code does not compile:
fn ref_on_int<T>(_: T) where T: AsRef<i32> {}
fn main() {
ref_on_int(&0_i32)
}
because
the trait bound `i32: std::convert::AsRef<i32>` is not satisfied
Why is it so?
This could be useful for example with a newtype like
struct MyInt(i32);
impl AsRef<i32> for MyInt {
/* etc. */
}
then you could indifferently pass a reference on an i32 or a reference on a MyInt, because in the memory we have in both cases an i32.
AsRef and Borrow are pretty similar at first glance, but they are used for different things. The Book describes the difference between them pretty well:
Choose Borrow when you want to abstract over different kinds of
borrowing, or when you’re building a data structure that treats owned
and borrowed values in equivalent ways, such as hashing and
comparison.
Choose AsRef when you want to convert something to a reference
directly, and you’re writing generic code.
In your case Borrow is a more reasonable choice because there is no conversion involved.
As for the question of why AsRef is not implemented between different integral types, I guess this would go against the intent of Rust to be expressive about casts; I think it's similar to the question Why can't I compare two integers of different types?.
Here's an authoritative answer by Aaron Turon:
Borrow provides a blanket implementation T: Borrow<T>, which is essential for making the above collections work well. AsRef provides a different blanket implementation, basically &T: AsRef<U> whenever T: AsRef<U>, which is important for APIs like fs::open that can use a simpler and more flexible signature as a result. You can't have both blanket implementations due to coherence, so each trait is making the choice that's appropriate for its use case.
I think that is one of the differences of AsRef and Borrow.
That is, Borrow<T> is implemented directly for &T, while AsRef<T> is not implemented for &T.
Funny thing is that AsRef<U> is implemented for &T if T implements AsRef<U>. That is, if you can use AsRef with a type, you can use it with a reference to the same time.
And another funny thing is that Borrow<T> is implemented for &T but also for T!

Resources