I come from JVM world and I'm struggling with dealing with the traits. My goal is to provide an interface (that what it would be called in Java) that would represent the time provider, so I could use 'real' implementation in production environment and 'fake' in the tests, so I could stub the time.
In Java I'd have
interface Clock {
Instant now();
}
class UtcClock implements Clock {
Instant now() {
return Instant.now();
}
}
and then I could use Clock type as any other type.
In Rust I have
pub trait Clock {
fn now(&self) -> DateTime<Utc>;
}
pub struct UtcClock;
impl Clock for UtcClock {
fn now(&self) -> DateTime<Utc> {
return Utc::now();
}
}
However to be able to use dynamic type Clock in Rust and to move it between threads I have to used boxed type e.g. Arc<dyn Clock + Send + Sync> which wouldn't be required if I'd use concrete type UtcClock.
Is provided solution of using traits idiomatic in Rust? Or there are other techniques to decouple the 'interface' and the implementation?
If it is OK then is there any way to make it look better than Arc<dyn Clock + Send + Sync>?
You don't have to use a trait object (i.e. a dyn Clock of some kind), you may also be able to use regular generics:
fn use_clock<T: Clock>(clock: T) {
let time = clock.now();
// ...
}
Though I imagine you'll have a struct containing various "services" and having lots of generics everywhere might get slightly messy.
As for trait objects, if you just want to "not have to think about it" and get Java-like behaviour, an Arc<dyn Clock> will largely do what you want, other than thread-safety related traits. To resolve those issues, you can write Arc<dyn Clock + Send + Sync + 'static>, but I prefer to make these supertraits:
trait Clock: Send + Sync + 'static /* + whatever other traits you want */ {
// ...
}
Note, this syntax doesn't do inheritance like the equivalent would in Java. Instead it imposes the restriction that: types which implement Clock must also implement Send, etc. Especially when working with unit structs (which are Send + Sync + 'static implicitly), moving the constraints to the trait definition can help cut down on boilerplate.
There's also type aliases, which essentially allow you to change the "spelling" of a type:
type ArcClock = Arc<dyn Clock + Send + Sync + 'static>;
and then you can use ArcClock like a regular type. Note, to the compiler, ArcClock is literally the same thing as Arc<dyn Clock + ...>.
As an aside, trait objects are often looked down upon in Rust for "bad performance". While it is true that calling a method on a trait object is generally harder to optimize than a regular monomorphized generic and requires an extra pointer indirection, this overhead is likely fairly small unless you're calling the method in a tight loop.
For example, in the case of a web server that stores the time a user was created, the overhead of the trait object will be negligible. FWIW, all java method calls behave in this way, so at worst you'll get Java-tier performance.
Arc<dyn Clock + Send + Sync> seems likely to be best, unless you're able to use &dyn Clock + Send + Sync. You can then use an alias type to make it look better, e.g. type DynClock = Arc<dyn Clock + Send + Sync>;. See the Rust book and the reference.
Related
I'm implementing a game using Rust, just to learn the language and some game patterns, and basically I have a concept where I have a Creature type, which can be a Character (a player) or Enemy (the monsters of my game). The way that I'm doing this right now is just declaring Creature as an enum and having two options, Character and Enemy, like this:
pub enum Creature {
Character(Character),
Enemy(Enemy),
}
The problem is that in my systems, I always need to add a match to know if it's a character or an enemy, and in most cases I don't care about that, since both have fields in common. So improving this with OOP would be easy, just declare Creature as a class and make Character and Enemy inherit from Creature. But of course this doesn't work in Rust.
So, what I'm trying is to declare Creature as a Trait, and that works for some extent, until I have to type fields as Creature. Basically, I'm sending data over a TCP socket from the server to the client, and serializing the data, and for that, the server message struct must have 'static and implement Send .
So right now I'm getting this error when I try to type a field inside my server message struct as Creature:
the size for values of type `(dyn Creature + 'static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn Creature + 'static)`
no field of an enum variant may have a dynamically sized type
change the field's type to have a statically known size
And my server message looks like:
pub enum ServerMessage {
...
SpawnCreature {
creature: dyn Creature,
position: Position,
},
...
}
And in my struct that has the code to send data over the socket, I'm getting this:
`(dyn Creature + 'static)` cannot be sent between threads safely
within `ServerMessage`, the trait `Send` is not implemented for `(dyn Creature + 'static)`
required for `ServerMessage` to implement `PostMsg`
Also, I'm using threads to send and listen for messages in the socket.
So, searching on google I didn't find any good explanation on how I can do this, or if this is even possible, I see some very simple examples using Box, but I don't know if that's going to work for my case.
I know that in Rust I should use composition instead of inheritance, but for this specific case, something like inheritance would improve my code so much.
If there is not really a good way of doing this, it's all good, I'm just curious to know if this is possible in some way.
I think the simplest way is a Creature struct with an enum inside.
struct Creature {
common_stuff: i32,
kind: CreatureKind,
}
enum CreatureKind {
Monster { monster_stuff: i32 },
Player { player_stuff: i32 },
}
enum ServerMessage {
SpawnCreature {
creature: Creature,
},
}
The alternative where you use a trait object should be possible, but it should know how to serialize itself, if you want to include it as part of a serializable ServerMessage. This seems to involve some complicated type trickery as you can see in this question: How to implement `serde::Serialize` for a boxed trait object?.
I find myself playing around with warp.
I would like to pass a database Trait to a warp::Filter using static dispatch, such that the concrete database may vary.
I read that Send + Sync are unsafe to implement, but I don't fully understand when it is appropriate to implement them.
Is it sound to implement Send + Sync in this context:
use std::sync::{Arc, RwLock};
use warp::Filter;
use warp::reply::{json};
use serde_json::{Value};
pub trait IDatabase {
fn db_read(&self) -> Value;
fn db_write(&self, data: Value);
}
pub fn get_api_v0_test<T: IDatabase + Send + Sync>(
db: Arc<RwLock<T>>
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("api" / "v0" / "test")
.and(warp::get())
.map(move || {
json(&db.read().unwrap().db_read())
})
}
Edit:
Yes, this example is completely sound!
As #trentcl and #Masklinn pointed out, I am in fact not implementing Send + Sync, I am simply requiring that any type T passed into the filter, implements the Send + Sync Traits, which is completely safe Rust.
I read that Send + Sync are unsafe to implement, but I don't fully understand when it is appropriate to implement them.
Because Send and Sync are automatically derived, it is only appropriate to derive them explicitely when wrapping raw pointers, usually because you're wrapping a native library, and after having made sure that the trait is appropriate for the underlying type.
Is it sound to implement Send + Sync in this context:
I don't understand what you'd want to "implement Send + Sync" on in this context.
pub trait IDatabase {
Rust is not C#, IWhatever is not a thing.
I would to write a method that returns self.hash_map.keys() while hiding from the caller the concrete type Keys<'_, K, V>.
A downside of the Keys return type is that it exposes to the caller that the K elements are coming from a HashMap. Intuitively, it seems as though it shouldn't be necessary to expose this information.
What is the cheapest way (with respect to CPU/allocations) to return an iterator of key references?
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Or does it require an invocation in the function body? Is some transformation necessary?
Both of the options you speculated about are possible.
The simplest option is to use the impl type syntax, which is an “existential” type: “I am going to return a value which implements Iterator but I'm not telling you what the concrete type is”. In this case, the compiler knows what the type is (so the compiled code is exactly the same as if it wasn't hidden), but the user of your method cannot rely on anything but the specified trait, so you aren't leaking implementation details.
impl MyType {
fn keys(&self) -> impl Iterator<Item = &MyKeyType> {
self.hash_map.keys()
}
}
(Note that this resembles but is not the same as dyn Iterator; when you use dyn, you're using runtime dispatch and the same function can return different concrete types from different calls to it. With impl, the type is static, just hidden, and there is no overhead.)
The disadvantage of this option is that the type is entirely unnameable; for example, nobody can write a structure that holds your keys() iterator except by making it generic over all Iterators. (This is rarely a problem for iterators in particular, since iterator wrappers are usually generic anyway.)
Also, if your iterator implements any additional traits you want to allow the caller to use, like Debug or ExactSizeIterator, then you need to add them to the impl type or they won't be visible.
Another option is to wrap the iterator in your own struct. This allows you to hide the implementation type while still allowing callers to refer to it by name, so it's the most flexible. The disadvantage of this option is that you have to explicitly implement Iterator (and any other traits) for the wrapper:
impl MyType {
fn keys(&self) -> MyKeyIterator<'_> {
MyKeyIterator(self.hash_map.keys())
}
}
#[derive(Clone, Debug)]
struct MyKeyIterator<'a>(Keys<'a, MyKeyType, MyValueType>);
impl<'a> Iterator for MyKeyIterator<'a> {
type Item = &'a MyKeyType;
fn next(&mut self) -> Option<&'a MyKeyType> {
self.0.next()
}
}
Rust Playground link with supporting code
This wrapper should not add any performance cost (when compiled with optimization), except that by default the wrapper method will not be inlined if called from another crate. If you're writing a library and this method is performance-sensitive, you can either enable link-time optimization (LTO) in the build, or add #[inline] to the next method (which enables cross-crate inlining). Of course, don't do any such tweaking without checking whether it makes a difference to actual performance; otherwise you're just increasing compile time (and instruction cache thrashing).
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Yes! You can return an impl Trait to indicate that you're returning a type that implements Trait but doesn't expose the concrete type:
fn keys(&self) -> impl Iterator<Item = &K> {
self.hash_map.keys()
}
See it working on the playground.
I have a closure, which I construct:
let r = receiver.clone();
let is_channel_empty = move || -> bool { r.is_empty() };
and then inject via:
Adapter::new(is_channel_empty)
The Adapter looks like this:
type ChannelEmptyFn = dyn Fn() -> bool + Send + Sync;
// for Box: type ChannelEmptyFn = Box<dyn Fn() -> bool + Send + Sync + 'static>;
pub struct Adapter {
is_channel_empty: ChannelEmptyFn,
}
impl Adapter {
pub fn new(is_channel_empty: ChannelEmptyFn) -> Self
{
Self { is_channel_empty }
}
}
I'm getting the obvious, size not known at runtime error.
If I pass a reference, it needs a lifetime and 'static isn't going to work.
If I pass a Box, it gets heap allocated, and while I don't like heap allocations, is an easy solution.
Is there another alternative? I can't pass an fn (function pointer) as the closure has a captured environment. Is there a way to specify a field type as a closure? other than Fn, FnMut, or FnOnce? I'm just wondering if I'm missing something in my limited knowledge of Rust.
One last thing, the Adapter eventually is wrapped in an Arc, ya there's some irony here, which is the other reason I'd like to have it all allocated in a single block.
Each closure is its own anonymous type that implements a particular closure trait. This means each closure is implemented differently from all others, and you cannot expect a single size or method. To abstract over this, we have two choices: generics, or trait objects.
Generics are compile-time, while trait objects are dynamic (run-time), which incurs a necessary run-time cost, made explicit by Rust.
The thing is, generics are basically just making a bunch of different types based on the generic one, which means a struct with a generic closure is not the same type as the same struct containing a different closure, even if the two closures have the same closure trait, because the closures have different types.
If you don’t want to exchange one Adapter for another, then generics are perfect for you.
pub struct Adapter<F: Fn() -> bool + Send + Sync> {
is_channel_empty: F,
}
impl<F: Fn() -> bool + Send + Sync> Adapter<F> {
pub fn new(is_channel_empty: F) -> Self
{
Self { is_channel_empty }
}
}
If you want to be able to exchange the closure or the Adapter for another one at run-time, you need to go dynamic, like you have now.
You have to keep the closure behind a layer of indirection, so that its size can be safely exchanged around. You can go the reference route, but as you said, you need a lifetime, and that will not work with Arc, because Arc needs to own its content (static lifetime). The only way to own something of unknown size is a box, because the heap is dynamic. If you go dynamic, you often must commit all the way. There is no way around heap allocation if that is what is needed. Heap allocation is not evil, it is another tool in your toolbox, that does not incur much cost in most closure cases as it only needs to allocate for the dynamic dispatch table and the captured variables, if any.
From the Rust documentation on Arc:
The type Arc provides shared ownership of a value of type T, allocated in the heap.
Recent Rust changes have made "trait objects" more prominent to me, but I only have a nebulous grasp of what actually makes something into a trait object. One change in particular is the upcoming change to allow trait objects to forward trait implementations to the inner type.
Given a trait Foo, I'm pretty sure that Box<Foo> / Box<dyn Foo> is a trait object. Is &Foo / &dyn Foo also a trait object? What about other smart-pointer things like Rc or Arc? How could I make my own type that would count as a trait object?
The reference only mentions trait objects once, but nothing like a definition.
You have trait objects when you have a pointer to a trait.
Box, Arc, Rc and the reference & are all, at their core, pointers. In terms of defining a "trait object" they work in the same way.
"Trait objects" are Rust's take on dynamic dispatch.
Here's an example that I hope helps show what trait objects are:
// define an example struct, make it printable
#[derive(Debug)]
struct Foo;
// an example trait
trait Bar {
fn baz(&self);
}
// implement the trait for Foo
impl Bar for Foo {
fn baz(&self) {
println!("{:?}", self)
}
}
// This is a generic function that takes any T that implements trait Bar.
// It must resolve to a specific concrete T at compile time.
// The compiler creates a different version of this function
// for each concrete type used to call it so &T here is NOT
// a trait object (as T will represent a known, sized type
// after compilation)
fn static_dispatch<T>(t: &T)
where
T: Bar,
{
t.baz(); // we can do this because t implements Bar
}
// This function takes a pointer to a something that implements trait Bar
// (it'll know what it is only at runtime). &dyn Bar is a trait object.
// There's only one version of this function at runtime, so this
// reduces the size of the compiled program if the function
// is called with several different types vs using static_dispatch.
// However performance is slightly lower, as the &dyn Bar that
// dynamic_dispatch receives is a pointer to the object +
// a vtable with all the Bar methods that the object implements.
// Calling baz() on t means having to look it up in this vtable.
fn dynamic_dispatch(t: &dyn Bar) {
// ----------------^
// this is the trait object! It would also work with Box<dyn Bar> or
// Rc<dyn Bar> or Arc<dyn Bar>
//
t.baz(); // we can do this because t implements Bar
}
fn main() {
let foo = Foo;
static_dispatch(&foo);
dynamic_dispatch(&foo);
}
For further reference, there is a good Trait Objects chapter of the Rust book
Short Answer: You can only make object-safe traits into trait objects.
Object-Safe Traits: Traits that do not resolve to concrete type of implementation. In practice two rules govern if a trait is object-safe.
The return type isn’t Self.
There are no generic type parameters.
Any trait satisfying these two rules can be used as trait objects.
Example of trait that is object-safe can be used as trait object:
trait Draw {
fn draw(&self);
}
Example of trait that cannot be used as trait object:
trait Draw {
fn draw(&self) -> Self;
}
For detailed explanation: https://doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html
Trait objects are the Rust implementation of dynamic dispatch. Dynamic dispatch allows one particular implementation of a polymorphic operation (trait methods) to be chosen at run time. Dynamic dispatch allows a very flexible architecture because we can swap function implementations out at runtime. However, there is a small runtime cost associated with dynamic dispatch.
The variables/parameters which hold the trait objects are fat pointers which consists of the following components:
pointer to the object in memory
pointer to that object’s vtable, a vtable is a table with pointers which point to the actual method(s) implementation(s).
Example
struct Point {
x: i64,
y: i64,
z: i64,
}
trait Print {
fn print(&self);
}
// dyn Print is actually a type and we can implement methods on it
impl dyn Print + 'static {
fn print_traitobject(&self) {
println!("from trait object");
}
}
impl Print for Point {
fn print(&self) {
println!("x: {}, y: {}, z: {}", self.x, self.y, self.z);
}
}
// static dispatch (compile time): compiler must know specific versions
// at compile time generates a version for each type
// compiler will use monomorphization to create different versions of the function
// for each type. However, because they can be inlined, it generally has a faster runtime
// compared to dynamic dispatch
fn static_dispatch<T: Print>(point: &T) {
point.print();
}
// dynamic dispatch (run time): compiler doesn't need to know specific versions
// at compile time because it will use a pointer to the data and the vtable.
// The vtable contains pointers to all the different different function implementations.
// Because it has to do lookups at runtime it is generally slower compared to static dispatch
// point_trait_obj is a trait object
fn dynamic_dispatch(point_trait_obj: &(dyn Print + 'static)) {
point_trait_obj.print();
point_trait_obj.print_traitobject();
}
fn main() {
let point = Point { x: 1, y: 2, z: 3 };
// On the next line the compiler knows that the generic type T is Point
static_dispatch(&point);
// This function takes any obj which implements Print trait
// We could, at runtime, change the specfic type as long as it implements the Print trait
dynamic_dispatch(&point);
}
This question already has good answers about what a trait object is. Let me give here an example of when we might want to use trait objects and why. I'll base my example on the one given in the Rust Book.
Let's say we need a GUI library to create a GUI form. That GUI form will consist of visual components, such as buttons, labels, check-boxes, etc. Let's ask ourselves, who should know how to draw a given component? The library or the component itself? If the library came with a fixed set of all the components you might ever need, then it could internally use an enum where each enum variant represents a single component type and the library itself could take care of all the drawing (as it knows all about its components and how exactly they should be drawn). However, it would be much better if the library allowed you to also use third-party components or ones that you wrote by yourself.
In OOP languages like Java, C#, C++ and others, this is typically done by having a component hierarchy where all components inherit a base class (let's call it Component). That Component class would have a draw() method (which could even be defined as abstract, so as to force all sub-classes to implement that method).
However, Rust doesn't have inheritance. Rust enums are very powerful, as each enum variant can have different types and amounts of associated data, and they are often used in situations where you'd use inheritance in a typical OOP language. An important advantage of using enums and generics in Rust is that everything is known at compile time, which means you don't need to sacrifice performance (no need for things like vtables). But in some cases, as in our example, enums don't provide enough flexibility. The library needs to keep track of components of different type and it needs a way to call methods on components that it doesn't even know about. That's generally known as dynamic dispatch and as explained by others, trait objects are the Rust way of doing dynamic dispatch.