Is it possible to convert type T into generic type without use of into() ?
struct MyType<T>(T);
impl<T> From<T> for MyType<T> {
fn from(v: T) -> Self {
Self(v)
}
}
#[allow(unused_variables)]
fn main() {
let test1 = MyType(12);
//let test2: MyType<i32> = 12; // why is this not working
let test3: MyType<i32> = 12.into();
let test4 = MyType::from(12);
}
fn test_fnc_1() -> MyType<i32> {
12 // not working
12.into()
}
I want to do "test2", but this is not possible. I have to do "test3".
Another sample, I thought it is somehow possible, is in test_func_1.
Playground
The reason it is not possible is because the cost of converting a value into your time can be arbitrarily large. Implicit conversion would hide this cost, which is not desirable for system language.
As far as I know there are no plans to add implicit conversion into Rust.
Rust is fairly reserved about implicit conversions. In Type Coercions in the Rust Reference you can see most of them are dictated by the compiler, with Deref being the only user-defined conversion that may be done implicitly. An exception of course is the ? operator which does invoke a From conversion.
There is no implicit From conversion that is called when passing arguments, returning a value, or assigning to a variable. You will have to use .into() or T::from().
What you're looking for is implicit type conversion, which isn't possible in Rust. As you've seen, you have to explicitly construct types at some point (i.e. MyType(...) in your example), and that can be done either directly or with a function call.
There are some special cases where implicit coercion occurs, but these are generally only when dealing with references or traits, not concrete value types.
Related
I am converting a variety of types to String when they are passed to a function. I'm not concerned about performance as much as ergonomics, so I want the conversion to be implicit. The original, less generic implementation of the function simply used &[impl Into<String>], but I think that it should be possible to pass a variety of types at once without manually converting each to a string.
The key is that ideally, all of the following cases should be valid calls to my function:
// String literals
perform_tasks(&["Hello", "world"]);
// Owned strings
perform_tasks(&[String::from("foo"), String::from("bar")]);
// Non-string types
perform_tasks(&[1,2,3]);
// A mix of any of them
perform_tasks(&["All", 3, String::from("types!")]);
Some various signatures I've attempted to use:
fn perform_tasks(items: &[impl Into<String>])
The original version fails twice; it can't handle numeric types without manual conversion, and it requires all of the arguments to be the same type.
fn perform_tasks(items: &[impl ToString])
This is slightly closer, but it still requires all of the arguments to be of one type.
fn perform_tasks(items: &[&dyn ToString])
Doing it this way is almost enough, but it won't compile unless I manually add a borrow on each argument.
And that's where we are. I suspect that either Borrow or AsRef will be involved in a solution, but I haven't found a way to get them to handle this situation. For convenience, here is a playground link to the final signature in use (without the needed references for it to compile), alongside the various tests.
The following way works for the first three cases if I understand your intention correctly.
pub fn perform_tasks<I, A>(values: I) -> Vec<String>
where
A: ToString,
I: IntoIterator<Item = A>,
{
values.into_iter().map(|s| s.to_string()).collect()
}
As the other comments pointed out, Rust does not support an array of mixed types. However, you can do one extra step to convert them into a &[&dyn fmt::Display] and then call the same function perform_tasks to get their strings.
let slice: &[&dyn std::fmt::Display] = &[&"All", &3, &String::from("types!")];
perform_tasks(slice);
Here is the playground.
If I understand your intention right, what you want is like this
fn main() {
let a = 1;
myfn(a);
}
fn myfn(i: &dyn SomeTrait) {
//do something
}
So it's like implicitly borrow an object as function argument. However, Rust won't let you to implicitly borrow some objects since borrowing is quite an important safety measure in rust and & can help other programmers quickly identified which is a reference and which is not. Thus Rust is designed to enforce the & to avoid confusion.
In this question, an issue arose that could be solved by changing an attempt at using a generic type parameter into an associated type. That prompted the question "Why is an associated type more appropriate here?", which made me want to know more.
The RFC that introduced associated types says:
This RFC clarifies trait matching by:
Treating all trait type parameters as input types, and
Providing associated types, which are output types.
The RFC uses a graph structure as a motivating example, and this is also used in the documentation, but I'll admit to not fully appreciating the benefits of the associated type version over the type-parameterized version. The primary thing is that the distance method doesn't need to care about the Edge type. This is nice but seems a bit shallow of a reason for having associated types at all.
I've found associated types to be pretty intuitive to use in practice, but I find myself struggling when deciding where and when I should use them in my own API.
When writing code, when should I choose an associated type over a generic type parameter, and when should I do the opposite?
This is now touched on in the second edition of The Rust Programming Language. However, let's dive in a bit in addition.
Let us start with a simpler example.
When is it appropriate to use a trait method?
There are multiple ways to provide late binding:
trait MyTrait {
fn hello_word(&self) -> String;
}
Or:
struct MyTrait<T> {
t: T,
hello_world: fn(&T) -> String,
}
impl<T> MyTrait<T> {
fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;
fn hello_world(&self) -> String {
(self.hello_world)(self.t)
}
}
Disregarding any implementation/performance strategy, both excerpts above allow the user to specify in a dynamic manner how hello_world should behave.
The one difference (semantically) is that the trait implementation guarantees that for a given type T implementing the trait, hello_world will always have the same behavior whereas the struct implementation allows having a different behavior on a per instance basis.
Whether using a method is appropriate or not depends on the usecase!
When is it appropriate to use an associated type?
Similarly to the trait methods above, an associated type is a form of late binding (though it occurs at compilation), allowing the user of the trait to specify for a given instance which type to substitute. It is not the only way (thus the question):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Or:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
Are equivalent to the late binding of methods above:
the first one enforces that for a given Self there is a single Return associated
the second one, instead, allows implementing MyTrait for Self for multiple Return
Which form is more appropriate depends on whether it makes sense to enforce unicity or not. For example:
Deref uses an associated type because without unicity the compiler would go mad during inference
Add uses an associated type because its author thought that given the two arguments there would be a logical return type
As you can see, while Deref is an obvious usecase (technical constraint), the case of Add is less clear cut: maybe it would make sense for i32 + i32 to yield either i32 or Complex<i32> depending on the context? Nonetheless, the author exercised their judgment and decided that overloading the return type for additions was unnecessary.
My personal stance is that there is no right answer. Still, beyond the unicity argument, I would mention that associated types make using the trait easier as they decrease the number of parameters that have to be specified, so in case the benefits of the flexibility of using a regular trait parameter are not obvious, I suggest starting with an associated type.
Associated types are a grouping mechanism, so they should be used when it makes sense to group types together.
The Graph trait introduced in the documentation is an example of this. You want a Graph to be generic, but once you have a specific kind of Graph, you don't want the Node or Edge types to vary anymore. A particular Graph isn't going to want to vary those types within a single implementation, and in fact, wants them to always be the same. They're grouped together, or one might even say associated.
Associated types can be used to tell the compiler "these two types between these two implementations are the same". Here's a double dispatch example that compiles, and is almost similar to how the standard library relates iterator to sum types:
trait MySum {
type Item;
fn sum<I>(iter: I)
where
I: MyIter<Item = Self::Item>;
}
trait MyIter {
type Item;
fn next(&self) {}
fn sum<S>(self)
where
S: MySum<Item = Self::Item>;
}
struct MyU32;
impl MySum for MyU32 {
type Item = MyU32;
fn sum<I>(iter: I)
where
I: MyIter<Item = Self::Item>,
{
iter.next()
}
}
struct MyVec;
impl MyIter for MyVec {
type Item = MyU32;
fn sum<S>(self)
where
S: MySum<Item = Self::Item>,
{
S::sum::<Self>(self)
}
}
fn main() {}
Also, https://blog.thomasheartman.com/posts/on-generics-and-associated-types has some good information on this as well:
In short, use generics when you want to type A to be able to implement a trait any number of times for different type parameters, such as in the case of the From trait.
Use associated types if it makes sense for a type to only implement the trait once, such as with Iterator and Deref.
How to define an array or vector that can contain any primitive data type in rust?
let v: [std::any::Any] = <something>;
Solution 1 (faster + safer)
enum Data {
Usize(usize),
U8(u8),
U16(u16),
U32(u16),
U64(u16),
Isize(isize),
I8(i8),
I16(i16),
I32(i16),
I64(i16),
F32(f32),
F64(f64),
Bool(bool),
Char(char),
}
fn main() {
let v: [Data; 3] = [Data::Usize(1), Data::Bool(true), Data::Char('a')];
}
Solution 2 (flexible, supports almost any type)
use std::any::Any;
fn main() {
let v: [Box<dyn Any>; 3] = [Box::new(1), Box::new('a'), Box::new(true)];
}
You can't. The very point of an array or a Vec is that they are homogenous, which also means each member needs to be the same size. But when given a u8 and a u128 - which are both std::any::Any - their sizes are not the same. One, therefore, needs a layer of indirection e.g. via [Box<dyn std::any::Any>; _] or Vec<Box<std::any::Any>>.
Contrary to Javascript, Rust is a strongly typed language. This means that any variable has a single type that is known at compile-time. This includes vector or array elements. You can work around this limitation by wrapping your data in an enum that will keep track of the actual type of the contained value at runtime.
Note that I strongly advise against using Any until you have lots of experience with Rust and know that your are using it for the right reason (and in particular not just to reproduce a pattern found in scripting languages.
I'm trying to make a simple utility function that reads multiple elements from stdin and puts them in collection and returns it. However I'm stuck at this point. The compiler says missing associated type Err value. How do I make it work, while keeping it generic as possible?
While this function seems useless, it's for learning the language and its type system.
use std::io::{ stdin };
use std::str::FromStr;
use std::io::Read;
use std::iter::FromIterator;
pub fn read_all<C>() -> C
where C: FromIterator<FromStr<Err>>
{
let mut buff = String::new();
stdin().read_to_string(&mut buff).expect("read_to_string error");
buff.split_whitespace()
.filter_map(|w| w.parse().ok())
.collect()
}
Usage example:
let v: Vec<i32> = read_all();
Working code
The only thing you need to change to your code in order to make it compile is the type signature of the function:
pub fn read_all<C, F>() -> C
where F: FromStr,
C: FromIterator<F>
Explanation
Your code is almost correct, but there is a problem:
FromIterator<T> is a trait, but T is a type.
You use FromStr in the place of T, but FromStr is a trait, not a type.
To solve this, you need to get a type that implements FromStr. You can do this by adding a type parameter F to the function and constraining it with where F: FromStr. Then you can write FromIterator<F>.
A note on associated types
Besides the issue of using a trait instead of a type, typing FromStr<Err> is wrong syntax. While in this case it is not necessary to specify the type of Err in the FromStr trait, you could do it as shown below:
pub fn read_all<C, F, E>() -> C
where F: FromStr<Err=E>,
C: FromIterator<F>
As you can see, instead of writing FromStr<E> we need to write FromStr<Err=E>. That is, you need to explicitly type the name of the associated type you are referring to.
A note on types vs traits
Usually traits cannot be treated as types. However, there are exceptions to this rule, as illustrated by the example below:
use std::fmt::Display;
pub fn print_box(thing: Box<Display>) {
println!("{}", thing)
}
fn main() { print_box(Box::new(42)); }
Here, you would expect T in Box<T> to be a type, but the Display trait is supplied instead. However, the compiler does not reject the program. The type checker sees Display as an unsized type. That is, the type of an object with a size unknown at compile time (because it could be any type implementing Display). When T in Box<T> is a trait, the resulting type is usually referred to as a trait object. It is impossible to cover this topic in depth here, but the links I refer to are a good starting point in case you want to know more.
In Python you can do something like this:
if isinstance("hello", basestring):
print "hello is a string"
else:
print "Not a string"
My question is can this kind of code be recreated or emulated using Rust ? If it is possible, is this kind of checking necessary or useful in Rust ?
Python is dynamically typed. When you write for example a function def foo(x):, the caller can choose to give a value of any type as the parameter x. That’s why Python has isinstance(), so that you can check when it’s important.
Rust is statically typed. Any variable in the code has a type that is known at compile-time. For functions parameters you have to write it explicitly: fn foo(x: String) {. For local variables you can write it: let x: String = something(); or leave it to the compiler’s type inference to figure out: let x = something(); based on other information (here based on the return type of something()). Sometimes there is not enough context for type inference and you have to write an explicit type annotation.
If everything has a known type, an isinstance function that returns true or false doesn’t make sense. So Rust doesn’t quite have one.
Note that some form of dynamic typing is possible with trait objects and the Any trait:
http://doc.rust-lang.org/book/trait-objects.html
http://doc.rust-lang.org/std/any/trait.Any.html
So you can write:
fn foo(object: &Any) {
if object.is::<String>() {
// ...
}
}
object’s type is still static: it’s &Any. But it also represents a value of some other, arbitrary type. You can access that value with other Any methods such as downcast_ref.
Rust has a limited for a downcasting that can be provided by Any: you can Any to query whether the concrete type is X or Y.
The usefulness of the construct is rather limited though; Rust is a statically typed language so in most situation you:
either know the exact concrete type
or use a trait that has sufficient methods for your needs
still, Chris Morgan developed an AnyMap for example to store one value of each type, without knowing said types a-priori, which he then used to provide a typed interface to HTTP headers without restricting the set of headers to a known set.
I think the most similar paradigm in Rust is using the match keyword on enum types. For instance, in the std::net::IpAddr case, you can use matching to decide whether you are dealing with an Ipv4Addr or an Ipv6Addr.
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn main() {
do_something(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
do_something(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
}
fn do_something(v: IpAddr) {
match v {
IpAddr::V4(_x) => {
println!("I'm an IPv4 Address!");
}
IpAddr::V6(_x) => {
println!("I'm an IPv6 Address!");
}
}
}
Link to Rust Playground with working example
This has the advantage of having well-defined types everywhere, which is of course required as Rust is strongly typed. However, it does have a potential impact on how you arrange your data structures; thus this sort of behavior has to be planned in advance.