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

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.

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.

Differences generic trait-bounded method vs 'direct' trait method

I have this code:
fn main() {
let p = Person;
let r = &p as &dyn Eatable;
Consumer::consume(r);
// Compile error
Consumer::consume_generic(r);
}
trait Eatable {}
struct Person;
impl Eatable for Person {}
struct Consumer;
impl Consumer {
fn consume(eatable: &dyn Eatable) {}
fn consume_generic<T: Eatable>(eatable: &T) {}
}
Error:
the size for values of type dyn Eatable cannot be known at
compilation time
I think it is strange. I have a method that literally takes a dyn Eatable and compiles fine, so that method knows somehow the size of Eatable. The generic method (consume_generic) will properly compile down for every used type for performance and the consume method will not.
So a few questions arise: why the compiler error? Are there things inside the body of the methods in which I can do something which I can not do in the other method? When should I prefer the one over the other?
Sidenote: I asked this question for the language Swift as well: Differences generic protocol type parameter vs direct protocol type. In Swift I get the same compile error but the underlying error is different: protocols/traits do not conform to themselves (because Swift protocols can holds initializers, static things etc. which makes it harder to generically reference them). I also tried it in Java, I believe the generic type is erased and it makes absolutely no difference.
The problem is not with the functions themselves, but with the trait bounds on types.
Every generic types in Rust has an implicit Sized bound: since this is correct in the majority of cases, it was decided not to force the developer to write this out every time. But, if you are using this type only behind some kind of reference, as you do here, you may want to lift this restriction by specifying T: ?Sized. If you add this, your code will compile fine:
impl Consumer {
fn consume(eatable: &dyn Eatable) {}
fn consume_generic<T: Eatable + ?Sized>(eatable: &T) {}
}
Playground as a proof
As for the other questions, the main difference is in static vs dynamic dispatch.
When you use the generic function (or the semantically equivalent impl Trait syntax), the function calls are dispatched statically. That is, for every type of argument you pass to the function, compiler generates the definition independently of others. This will likely result in more optimized code in most cases, but the drawbacks are possibly larger binary size and some limitations in API (e.g. you can't easily create a heterogeneous collection this way).
When you use dyn Trait syntax, you opt in for dynamic dispatch. The necessary data will be stored into the table attached to trait object, and the correct implementation for every trait method will be chosen at runtime. The consumer, however, needs to be compiled only once. This is usually slower, both due to the indirection and to the fact that individual optimizations are impossible, but more flexible.
As for the recommendations (note that this is an opinion, not the fact) - I'd say it's better to stick to generics whenever possible and only change it to trait objects if the goal is impossible to achieve otherwise.

How to idiomatically require a trait to retrieve a slice

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.

Implement foreign trait for foreign type [duplicate]

This question already has answers here:
How do I implement a trait I don't own for a type I don't own?
(3 answers)
Closed 7 years ago.
So I see there are reasons to forbid orphan trait implementations because of forward compatibility considerations (to prevent the addition of a further trait implementation in a library from breaking somewhere where the type's trait is used) and it possibly making compilation a lot more difficult. But I wonder which workaround would be considered by the Rust community to be the most ideal one:
(Just in case this wasn't enough background: I am trying to use rusqlite with chrono's DateTime. So I want to implement rusqlite's FromSql and ToSql traits for DateTime<UTC> but that's apparently not as easy as I thought it'd be - I'm just starting out with Rust currently.)
Fork rusqlite and implement the trait. (I kind of feel like this is not the nicest way to do it because maybe it's just me who needs this trait implementation there and so I might end up having to keep my own fork up to date. Also I wasn't able to implement the traits because there are some complicated generics things that I don't understand perfectly yet.)
Implement my own DateTime struct (probably the best workaround but I feel like this is just some replication of work that shouldn't be nescessary).
Somehow "copy" the DateTime<UTC> trait and give it an alias and implement the FromSql and ToSql traits for my aliased type (however I think this is not trivial either and when I tried it I wasn't able to get it to work since it was still treated as an external type).
I hope somebody can explain to me how to best solve this issue, from my pure OOP experience I would just like to be able to inherit DateTime and implement the interface but (for valid reasons) this is not how it gets done in Rust...
The simplest way would be to use newtype pattern:
extern crate a;
extern crate b;
use a::SomeTrait;
use b::SomeStruct;
pub struct MySomeStruct(SomeStruct);
impl SomeTrait for MySomeStruct {
...
}
Here you create a wrapper around the foreign struct, and since this wrapper is a completely different type which belongs to your crate, you are free to implement a::SomeTrait for it. This is similar to your 2nd point, except that you absolutely don't need to reimplement the type from scratch.
Naturally, you won't be able to call all methods of SomeStruct on MySomeStruct. You must either forward all methods you want, or unwrap the inner value when you don't need its trait implementation anymore, or you can impl Deref for MySomeStruct { type Target = SomeStruct; ... }, but the latter is considered an antipattern.
I'm not sure what would be the most idiomatic, but it looks like the best approach would be to use the newtype pattern, which is a tuple-struct with one field. This creates a new type distinct from the old one, and you can implement the traits for that new type. To use the trait methods, you will need to wrap it in the newtype, but to use the normal methods, you'll use the normal struct without your newtype wrapper.
struct MyType(TheirType);
impl TheTrait for MyType {
....
}
fn main() {
let a = TheirType::new(....);
a.method_on_their_type();
let b = MyType(TheirType::new(....));
b.method_on_the_trait();
}

How do I call a function through a member variable?

Toying with Rust, I'm extracting some code into a class. To keep it self-contained but separate functionality, I want to hang onto a callback function and call it later. To keep it simple, including skipping the obvious fn new(), we have something like:
pub struct Toy {
go: fn(count: i16) -> String,
}
impl Toy {
fn lets_go(&mut self, n: i16) -> String {
self.go(n)
}
}
Building gives me...
...path.../src/toy.rs:7:14: 7:19 error: type `&mut toy::Toy` does not implement any method in scope named `go`
...path.../src/toy.rs:7 self.go(n)
Presumably, there's a special syntax (or entirely different construct) that makes sense of the self.go() call, but I don't see examples or descriptions of comparable situations in any of the documentation, so I'd appreciate any direction.
Obviously, .go could be of a functor-like class, but that doesn't seem very idiomatic for Rust.
foo.bar(...) is always parsed as a method call, it never looks for fields. This avoids ambiguity, especially with traits. One can force it to be a field access by separating the call and the field access into two distinct expressions, for example,
let f = self.go;
f(n)
Or, better, just (self.go)(n).
Issue #2392 covers improving these diagnostics.

Resources