What is the Rust equivalent of Scala's value classes? - rust

Scala's value classes provide a way to use the type system without allocating runtime objects (structs in case of Rust). I'm looking for an equivalent in Rust.
The use case I'd like to cover is passing around a string that represents a URI, by annotating relevant signatures with something like Uri instead of String, and, ideally, use this Uri when a String is expected. All this with minimal overhead.
An obvious solution would be to use structs with one field:
struct Uri { val: String }
This has the drawback of slightly awkward usage, not being acceptable where a String is expected, and I'm unsure of its overhead.
Is there something similar to Scala's value classes in Rust? Is there some other mechanism that facilitates this use case?

I believe the thing you are looking for is the "strong type system". In Rust it is represented with a tuple struct with one field (a newtype):
struct Uri(pub String);
This creates a strong type with the least overhead. I'd say this just creates a mark for the compiler that this type is Uri and nothing else. Since this is a strong type, you can't simply pass String to it and get a String from it, you must do a conversion manually.
Simple example gives you a strong type on top of the String:
struct Uri(pub String);
fn takes_uri(uri: Uri) {
println!("URI: {}", uri.0);
}
fn takes_string(uri: String) {
println!("String: {}", uri);
}
fn main() {
let uri = Uri("https://stackoverflow.com".to_owned());
takes_uri(uri);
// takes_string(uri); // This does not compile
}
The key thing with strong types is that you can not implicitly cast them so this requires the code writer to write explicit code.

Related

Ergonomically passing a slice of trait objects

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.

Rust type conversion

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.

How to store an invariant type variable in Rust

I would like to parse the type of each value in a row of data from tokio-postgresql
Here is an example of getting a single value for a row of data from postgresql:
...
let rows = client
.query("select * from ExampleTable;")
.await?;
// This is how you read a string if you know the first column is a string type.
let thisValue: &str = rows[0].get(0);
In this example, it is known at design-time that the type in the first column is a string, and therefore the type for thisValue is &str. I would like to accept an invariant type.
I intend to use std::any::type_name::<T>() to derive the type name in thisValue and then use conditional logic (if/switch) to process this data differently depending on the type.
Is there an invariant way to store a variable in Rust? Will std::any::type_name::<T>() work on that variable? Is there another way to "box" the variable instead?
I understand that std::any::type_name::<T>() is using a kind of generics interface. To me, this means it's probably a compile-time strategy, not run-time. So I have my doubts that the way I am researching will work, but I hope I am on the right track and only need the final piece: an invariant type.
Should I be using &dyn Any and TypeId::of::<TypeHere>() == thisValue.type_id()?
In this situation, the get function of this API tokio-postgresql uses generics and doesn't return a boxed value. Therefore in this situation I may need to use columns() to determine the Rust type and the use separate functions to call get with different variables types.
The overall question still needs to be answered "How to store an invariant type variable in Rust", regardless of the specifics I have used to ask the title question.
The preferred invariant type is &dyn any
With &dyn any: any is the trait, dyn means the type of the trait.
Declaring:
let thisValue: &dyn Any = rows[0].get(0); //This works if tokio-postgresql returned a "dyn any" type, which it doesn't
Example of testing what type is referenced:
TypeId::of::<String>() == thisValue.type_id() //type checking using TypeId of boxed value.
Example of testing the type with downcast:
if let Some(string) = thisValue.downcast_ref::<String>() {
println!("String ({}): {}", string.len(), string);
}
Boxed
Box to force heap allocation (if necessary). This strategy is included too, so you can see how &dyn Any works with Boxed
A "boxed" value of dyn Any is invariant:
let thisValue: Boxed<dyn Any> = rows[0].get(0); //This works if tokio-postgresql returned a "dyn any" type, which it doesn't
In this scenario the API being used requires generic inference, so for tokio-postgresql this won't work, but it is the answer for the title question.
An example of testing the type with the downcast function of Boxed:
if let Ok(string) = thisValue.downcast::<String>() {
println!("String ({}): {}", string.len(), string);
}
About the postgresql sub-problem
As per the original post,
In this situation, the get function of this API tokio-postgresql uses
generics and doesn't return a boxed value. Therefore in this situation
I may need to use columns() to determine the Rust type and the use
separate functions to call get with different variables types.
So this answer solves the question, and although it won't work with the tokio-postgresql API, it equips you with the knowledge of the kind of API you would like to find/build/wait-for.

Can Python's isinstance be emulated or used in Rust?

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.

Does Rust have a type or trait that all entities "inherit" or implement?

In Java, all objects inherit from java.lang.Object. In Go, all types/structs implement the empty interface interface {}. Is there any similar construct in the Rust language?
If the answer is no, what makes it unnecessary? Is it because all entities in Rust (except modules) can be parameterized by type? Does that remove the need for a common "supertype" or common Trait shared by all Rust entities/structs/enums?
Yes, there is a trait. It is std::any::Any.
From the docs:
The Any trait is implemented by all 'static types, and can be used for dynamic typing
The way I understand it, if not completely unnecessary, it is less necessary in Rust to have a base type.
Note that the introduction of parametric polymorphism (generics) has removed most of the use cases for Object also in java.
With Object you can implement a "generic" method that can work with any kind of java type. On the other hand, when you have an Object you can't do much with it... You have to cast it back to the actual subtype to use it.
For instance, the old non-generic version of java collections worked with Objects, This means you had to work like this (sample is straight from this post on Oracle's site):
LinkedList list = new LinkedList();
list.add(new Integer(1));
Integer num = (Integer) list.get(0);
add takes an Object (so you can use the collection with any type). But get returns an Object too, so you have to cast it back to Integer based on the knowledge that you (the programmer) have on what you originally inserted in the LinkedList. There's very little type safety here.
The new generic version of the same container, since java 1.5, is:
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(new Integer(1));
Integer num = list.get(0);
Now you have a list of Integer, so add takes Integer and get returns Integer. Object is nowhere to be seen any more (although due to type erasure it's just there, barely hiding under the hood...)
Note that Go's main use of interface{} stems from the fact that Go does not have generics. The main pattern of use for interface{} is roughly the same. You use it when you have to work with multiple types and you sort-of cast it (although in a safer way and with a more elegant pattern) back to a more useful type before using it.
With that in mind, you could theoretically use Any in Rust in the same way (checking actual type and casting to it before using it). It's just that you will likely not find many cases where this might be useful.
The code below works on rustc 0.13-nightly (2015-01-01), although I'm sure there are better ways to write it...
use std::any::{Any, AnyRefExt};
// just a couple of random types
struct Foo {
a: f64
}
enum Bar {
Baz(int),
Qux
}
fn main() {
// create an array of things that could have any type
let anything = &[&1i as &Any, &Foo{a: 2.} as &Any, &Bar::Qux as &Any];
// iterate and print only the integer
for &any in anything.iter() {
if any.is::<int>() { // check if type is int
match any.downcast_ref::<int>() { // cast to int
Some(a) => { println!("{}", *a); }
None => panic!()
}
// or just (shorter version of the match)
println!("{}", *any.downcast_ref::<int>().unwrap());
}
}
}

Resources