Implicit vs explicit "into" - rust

So I've noticed there are multiple ways to convert from &str to String, but (for the sake of my question) I'll only talk about two ways, which would be "foo".to_string() and "foo".into().
Now I know that those are fundamentally different. The first of those is a method, while the second one is a trait-(implementation).
I currently need a piece of code that converts a ObjectNode into a Node.
First I implemented this using the Into trait:
struct ObjectNode {}
impl Into<Node> for ObjectNode {
fn into(self) -> Node {
Node::Object(self)
}
}
enum Node {
Object(ObjectNode)
}
But then I noticed that I had used "foo".to_string() instead of "foo".into() in my code. Simple because that's the thing I read online in pieces of code by other people. So I thought about implementing:
impl ObjectNode {
fn into_node(self) -> Node {
Node::Object(self)
}
}
I'm now wondering if this "explicit" into_node method is somehow preferred over the "implicit" into, as into could be implemented for quiet a large number of types (which could lead to some... unexpected behavior, maybe?). Or maybe it's better to do it the other way around?

Well I guess you should implement From<ObjectNode> for Node rather than implementing Into<Node> for ObjectNode because the standard library for Into highlights it:-
One should avoid implementing Into and implement
From instead. Implementing From automatically
provides one with an implementation of Into thanks to
the blanket implementation in the standard library.
You should probably go for an implementation of From rather than creating a separate method for converting it. Most crates in the Rust ecosystem heavily depend on these functions for their type conversions.

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.

How to hide trait implementation in Rust

I have a project with its own error type, which is exposed outside of crate. Lets call it MyErrorType. The project itself internally depends on another crate with its own error type. Lets call it ForeignErrorType.
In order to simplify code and make it more readable I've created following implementation of From trait:
impl From<ForeignErrorType> for MyErrorType {
...
}
This allows to use question mark operator, ?, when dealing with foreign error types without a necessity to convert them in place.
The issue is that the mentioned trait implementation is exposed outside of my crate. I don't want the users of my crate to accidentally rely on the conversion possibility from ForeignErrorType to MyErrorType.
What I've tried so far: Have put the mentioned trait implementation into module with pub(crate) visibility. This surprisingly hides all structs defined in such module, but leaves trait implementation exposed.
Is there way to keep my From implementation private and not to expose it outside of crate?
Probably I'm trying to achieve error handling benefits in a non‑idiomatic way. If this is true, what's the proper way to be able to use ? operator on foreign error types without exposing them?
Is there way to keep my From implementation private and not to expose it outside of crate?
No. Trait implementations have no scope.
What you can do instead in this case is write a function with the same contents as your From implementation would have had, and apply it with Result::map_err before ?.
pub(crate) fn foreign_err(e: ForeignErrorType) -> MyErrorType {
todo!()
}
...
let foo = their_function().map_err(foreign_err)?;
This has to be done at every ? usage, but there is no way to have an implicit conversion that is scoped to a crate or module, so that's the best you can have.

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.

Is it recommended to use traits to implement utility functions for structs in external crate?

I want to implement a simple utility/helper function in Rust. The function just concatenates the path in a struct (from an external crate) and the argument passed. Is it more idiomatic to implement the helper-function as a normal function or as function of a custom trait?
The implementation of the trait-based approach:
use std::path::{Path, PathBuf};
pub trait RepositoryExt {
fn get_full_path(&self, path_in_repository: &Path) -> PathBuf;
}
impl RepositoryExt for othercrate::Repository {
// othercrate::Repository's workdir() returns its path
fn get_full_path(&self, path_in_repository: &Path) -> PathBuf {
self.workdir().join(path_in_repository)
}
}
With just a function:
pub fn get_repository_full_path(repo: othercrate::Repository,
path_in_repository: &Path) -> PathBuf {
repo.workdir().join(path_in_repository)
}
The trait-based approach shortens the code when using the helper-function, but I'm worried that it may introduce difficulty to understand where it's defined.
Though both implementations should work, I want to know which is the recommended way in Rust.
(Disclaimer: I am not entirely sure about this. If this answer receives enough™ upvotes, I will delete this disclaimer)
Good question! I have already seen both solution in the wild and would say that both are valid to use. Or in other words: neither of the two solutions are considered bad.
However, I'd say that using the Ext-trait approach is often a slightly better choice due to these advantages:
Many operations feel way more natural to call "on an object" (with dot-notation) than to call a function with both objects.
Chaining multiple calls looks nice in code because it fits our left-to-right way of reading, whereas with the function-approach the code is harder to read: f(f(a, f(d, e)), c).
If the user prefers the plain-function style, he can also use it that way with Trait::func(self_object, arg).
But of course there are some disadvantages (you already mentioned one):
It's harder for the user to understand where the helper-function is defined.
The user needs to have the trait in scope (read: use 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