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.
Related
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.
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.
I wrote a program where I manipulated a lot of BigInt and BigUint values and perform some arithmetic operations.
I produced code where I frequently used BigInt::from(Xu8) because it is not possible to directly add numbers from different types (if I understand correctly).
I want to reduce the number of BigInt::from in my code. I thought about a function to "wrap" this, but I would need a function for each type I want to convert into BigInt/BigUint:
fn short_name(n: X) -> BigInt {
return BigInt::from(n)
}
Where X will be each type I want to convert.
I couldn't find any solution that is not in contradiction with the static typing philosophy of Rust.
I feel that I am missing something about traits, but I am not very comfortable with them, and I did not find a solution using them.
Am I trying to do something impossible in Rust? Am I missing an obvious solution?
To answer this part:
I produced code where I frequently used BigInt::from(Xu8) because it is not possible to directly add numbers from different types (if I understand correctly).
On the contrary, if you look at BigInt's documentation you'll see many impl Add:
impl<'a> Add<BigInt> for &'a u64
impl Add<u8> for BigInt
and so on. The first allows calling a_ref_to_u64 + a_bigint, the second a_bigint + an_u8 (and both set OutputType to be BigInt). You don't need to convert these types to BigInt before adding them! And if you want your method to handle any such type you just need an Add bound similar to the From bound in Frxstrem's answer. Of course if you want many such operations, From may end up more readable.
The From<T> trait (and the complementary Into<T> trait) is what is typically used to convert between types in Rust. In fact, the BigInt::from method comes from the From trait.
You can modify your short_name function into a generic function with a where clause to accept all types that BigInt can be converted from:
fn short_name<T>(n: T) -> BigInt // function with generic type T
where
BigInt: From<T>, // where BigInt implements the From<T> trait
{
BigInt::from(n)
}
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).
Rust newbie here. What would be a good way to go about dynamically inferring the most probably type given a string? I am trying to code a function that given a string returns the most possible type but I have no idea where to start. In Python I would probably use a try-except block. This is what I would expect to have:
"4" -> u32 (or u64)
"askdjf" -> String
"3.2" -> f64
and so on? I know that some strings can be assigned to several possible types so the problem is not well defined but I am only interested in the general philosophy on how to solve the problem efficiently in rust.
There is a parse method on string slices (&str) that attempts to parse a string as a particular type. You'll have to know the specific types you're ready to handle, though. The parse method can return values of any type that implements FromStr.
fn main() {
if let Ok(i) = "1".parse::<u32>() {
println!("{}", i);
}
if let Ok(f) = "1.1".parse::<f64>() {
println!("{}", f);
}
}
Note that the ::<T> part is only necessary if the compiler is unable to infer what type you're trying to parse into (you'll get a compiler error in that case).
I am trying to code a function that given a string returns the most possible type but I have no idea where to start.
First of all: Rust is statically typed which means that a function returns one and only one type, so you can't just return different types, like in dynamically typed languages. However, there are ways to simulate dynamic typing -- namely two (that I can think of):
enum: If you have a fixed number of possible types, you could define an enum with one variant per type, like this:
enum DynType {
Integer(i64),
Float(f32),
String(String),
}
fn dyn_parse(s: &str) -> DynType {
...
}
You can read more on enums in this and the following Rust book chapter.
There is a trait in the standard library designed to simulate dynamic typing: Any. There is more information here. Your code could look like this:
fn dyn_parse(s: &str) -> Box<Any> {
...
}
You can't return trait objects directly, so you have to put it in a Box.
Keep in mind that both possibilities require the user of your function to do additional dispatch. Since Rust is statically typed, you can't do the things you are used to in a dynamically typed language.
Maybe you should try to solve your problems in a different way that makes more sense in the statically typed world.
About the implementation part: Like Francis Gagné said, there is parse which tries to parse a string as a type the programmer specifies. You could of course just chain those parse calls with different types and take the first one that succeeds. But this might not be what you want and maybe not the fastest implementation.
Of course you should first think of exact rules what string should parse as what type. After that you could, for example, build a finite state machine that detects the type of the string. Doing that properly could be a bit tricky though.