So I have a function. And I want to test it. It takes a struct as param. And the struct has some trait implemented. This trait has a long running io method. I don't want this io method to actually go fetch data, I want to mock this io method and just return the result. I am a little lost about how this can be done. Here is my try (not working)
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
fn important_func(a: TestStruct) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method()
.returning(|| 1);
important_func(mock);
}
}
I obviously get this error:
error[E0308]: mismatched types
--> src/test.rs:35:24
|
35 | important_func(mock);
| -------------- ^^^^ expected struct `TestStruct`, found struct `MockTestStruct`
| |
| arguments to this function are incorrect
How can I achieve mocking trait methods? 1) One way is to change function param and instead of accepting a concrete struct accept trait. And implement this trait on MockTestStruct. But then we have dynamic dispatching and it hurts the performance. I don't want a performance degrade just for the test. 2) I also tried reimplementing the Trait right where the test is, but conflicting implementations are not allowed in Rust. 3) Make function accept TestStruct or MockTestStruct? Probably not great way either.
Could you please tell me what is the idiomatic way to do it?
You can make your function, important_func a generic function. You can then use generic bounds to restrict the type to implementors of your trait.
Here is an example with your code:
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
// important_func can now use any type T which implements TestTrait,
// including your mock implementation!
fn important_func<T: TestTrait>(a: T) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method().returning(|| 1);
important_func(mock);
}
}
Related
I'd like to define a trait with many methods:
pub trait DataSetT{
fn numeric_op_1(&self){...};
fn numeric_op_2(&self)->f64{...};
...
fn io_op_1(&self) {..};
fn io_op_2(&self) -> DataFrame {...};
...
}
Now, if I define all these methods in the same file it would get humongous.
For the sake of clean and visibile code, I'd like to split these definitions across different files/modules.
For example numeric operatins would live in:
src/numerics.rs
And io operations would live in
src/io.rs
Same thing with implementing this trait for a Struct (overriding the default trait behaviour).
As soon as I try doing that, I either get not all trait items implemented or confilicting definitions.
What is the best practice solution in this kind of situtation?
Without macro you should not be able to split a trait definition over different modules. Where you write trait MyTrait { .. } you need to define it.
But you can define multiple traits and have a super trait, like this:
// src/ops/a.rs
pub trait OpA {
fn op_a1(&self);
fn op_a2(&self) -> f64;
}
// src/ops/b.rs
pub trait OpB {
fn op_b1(&self);
fn op_b2(&self);
}
// src/ops/mod.rs
pub trait Op: OpA + OpB {}
// src/ops_impl/mod.rs
struct MyOp {}
impl Op for MyOp {}
// src/ops_impl/a.rs
impl OpA for MyOp {
fn op_a1(&self) {}
fn op_a2(&self) -> f64 {
42.0
}
}
// src/ops_impl/b.rs
impl OpB for MyOp {
fn op_b1(&self) {}
fn op_b2(&self) {}
}
I want to implement an OOP approach in Rust. My base class BaseClass would look like this (but with more parameters):
struct BaseClass {
name: String,
}
impl BaseClass {
fn new(name: &str) -> Self {
Self{name: name.to_string()}
}
}
This base class has an associated trait that does the trick for inheritance:
trait BaseClassInterface {
fn as_base(&self) -> &BaseClass;
fn get_name(&self) -> &str {
&self.as_base().name
}
}
Now, I want to inherit from this base class and add extra stuff with generics:
struct MiddleClass<T> {
base: BaseClass,
value: T,
}
impl<T> MiddleClass<T> {
fn new(name: &str, value: T) -> Self {
Self{base: BaseClass::new(name), value}
}
}
Again, this is an "abstract" class. Users of my library will "inherit" from this middle class to define their structs. So let's do a trait for it:
trait MiddleClassInterface {
type Type;
fn as_middle(&self) -> &MiddleClass<Self::Type>;
fn get_value(&self) -> &Self::Type {
&self.as_middle().value
}
}
Now we implement the BaseClassInterface trait for the MiddleClassInterface trait and we achieved "class inheritance"!
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
fn as_base(&self) -> &BaseClass {
&self.as_middle().base
}
}
So now, users can implement their own versions of the MiddleClassInterface and use methods from the BaseClassInterface trait:
struct MyIntClass {
middle: MiddleClass<i32>,
}
impl MyIntClass {
fn new(name: &str, value: i32) -> Self {
Self{middle: MiddleClass::new(name, value)}
}
}
impl MiddleClassInterface for MyIntClass {
type Type = i32;
fn as_middle(&self) -> &MiddleClass<Self::Type> {
&self.middle
}
}
As MyIntClass implements the MiddleClassInterface, MyIntClass will implement the BaseClassInterface... or not?
Let's look at my main function:
fn main() {
let my_class = MyIntClass::new("my_class", 1);
println!("{}", my_class.get_name());
}
When compiling, I get the following error:
error[E0599]: no method named `get_name` found for struct `MyIntClass` in the current scope
--> src/main.rs:71:29
|
52 | struct MyIntClass {
| ----------------- method `get_name` not found for this
...
71 | println!("{}", my_class.get_name());
| ^^^^^^^^ method not found in `MyIntClass`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `BaseClassInterface` defines an item `get_name`, perhaps you need to implement it
--> src/main.rs:13:1
|
13 | trait BaseClassInterface {
| ^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0599`.
MyIntClass implements the MiddleClassInterface<i32> class, which in turn implements the BaseClassInterface trait. So... why does MyIntClass not implement the BaseClassInterface indirectly?
Thanks in advance!
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3aa8007b440b965aedf124a69dacc6f7
Your code will work if you change:
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
to:
impl<T> BaseClassInterface for T where T: MiddleClassInterface {
What you did above is implement BaseClassInterface for a trait object but such implementations are not considered for method lookup on concrete types. You would first have to coerce my_class into a trait for it to work:
(&my_class as &dyn MiddleClassInterface<Type=i32>).get_name())
Whereas the fix is to not implement BaseClassInterface for a trait object, but rather implement it for all types that implement the MiddleClassInterface. The difference being that MyIntClass will itself implement the base class and won't have to go through dynamic dispatch to call those methods.
I started working with ws, and I would like to split the Handler trait implementation into multiple files.
So I wrote this in one file, on_open.rs:
impl Handler for Client {
fn on_open(&mut self, _: Handshake) -> Result<()> {
println!("Socket opened");
Ok(())
}
}
And this in another file, on_message.rs:
impl Handler for Client {
fn on_message(&mut self, msg: Message) -> Result<()> {
println!("Server got message '{}'. ", msg);
Ok(())
}
}
While compiling it I got the following error:
error[E0119]: conflicting implementations of trait `ws::handler::Handler` for type `models::client::Client`:
--> src\sockets\on_message.rs:9:1
|
9 | impl Handler for Client {
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `models::client::Client`
|
::: src\sockets\on_open.rs:8:1
|
8 | impl Handler for Client {
| ----------------------- first implementation here
I'd like to have the files to be separated so that each developer can work on a separate one. Is there a way to achieve this or am I forced to have the full trait implementation in a single file?
Although you can have multiple impl blocks for the same object, you can't have two which are exactly the same, hence the error of conflicting implementations indicated by E0119:
Since a trait cannot be implemented multiple times, this is an error.
(If the trait could be specialised because it takes any number of generic type arguments the situation would be very much different because every specialisation would be a different impl block. However even then you wouldn't be able to have the same specialisation implemented more than once.)
If you would like to split the functionality into separate files, you could do that, but in a slightly different way than you originally thought. You could split the Client's impl block instead of the Handler implementation as the following minimal and compilable example demonstrates. (Try it in the playground!)
As you can see, the Handler trait is implemented for Client in one place, but all the implementations of Client are split into multiple files/modules and the Handler implementation is just referencing those:
mod handler
{
pub type Result<T> = ::std::result::Result<T, HandlerError>;
pub struct HandlerError;
pub trait Handler
{
fn on_open(&mut self, h: usize) -> Result<()>;
fn on_message(&mut self, m: bool) -> Result<()>;
}
}
mod client
{
use super::handler::{ self, Handler };
struct Client
{
h: usize,
m: bool,
}
impl Handler for Client
{
fn on_open(&mut self, h: usize) -> handler::Result<()>
{
self.handle_on_open(h)
}
fn on_message(&mut self, m: bool) -> handler::Result<()>
{
self.handle_on_message(m)
}
}
mod open
{
use super::super::handler;
use super::Client;
impl Client
{
pub fn handle_on_open(&mut self, h: usize) -> handler::Result<()>
{
self.h = h;
Ok(())
}
}
}
mod message
{
use super::super::handler;
use super::Client;
impl Client
{
pub fn handle_on_message(&mut self, m: bool) -> handler::Result<()>
{
self.m = m;
Ok(())
}
}
}
}
Thanks for #Peter's answer, I re-wrote my code as below, and it is working fine:
socket.rs
use ws::Handler;
use crate::models::client::Client;
use ws::{Message, Request, Response, Result, CloseCode, Handshake};
impl Handler for Client {
fn on_open(&mut self, hs: Handshake) -> Result<()> {
self.handle_on_open(hs)
}
fn on_message(&mut self, msg: Message) -> Result<()> {
self.handle_on_message(msg)
}
fn on_close(&mut self, code: CloseCode, reason: &str) {
self.handle_on_close(code, reason)
}
fn on_request(&mut self, req: &Request) -> Result<(Response)> {
self.handle_on_request(req)
}
}
sockets/on_open.rs
use crate::models::client::Client;
use crate::CLIENTS;
use crate::models::{truck::Truck};
use ws::{Result, Handshake};
impl Client {
pub fn handle_on_open(&mut self, _: Handshake) -> Result<()> {
println!("socket is opened");
Ok(())
}
}
I want to construct a indexing-type associated to a value which is a wrapper around a usize such that any vector created from that value I don't need to bounds-check the index. It seems like phantom lifetimes can be used to do some small amount of dependently typed programming like this. Will this work or are there things I'm not considering?
In other words, using the module below is it impossible to write ("safe") code which will step out of memory?
Also, is there a way to do this without the unit references?
pub mod things {
use std::iter;
#[derive(Clone, Copy)]
pub struct ThingIndex<'scope>(usize, &'scope ());
pub struct Things {
nthings: usize,
}
pub struct ThingMapping<'scope, V>(Vec<V>, &'scope ());
impl Things {
pub fn in_context<F: FnOnce(&Things) -> V, V>(nthings: usize, continuation: F) -> V {
continuation(&Things { nthings })
}
pub fn make_index<'scope>(&'scope self, i: usize) -> ThingIndex<'scope> {
if i >= self.nthings {
panic!("Out-of-bounds index!")
}
ThingIndex(i, &())
}
pub fn make_mapping<'scope, V: Clone>(&'scope self, def: V) -> ThingMapping<'scope, V> {
ThingMapping(iter::repeat(def).take(self.nthings).collect(), &())
}
}
impl<'scope, V> ThingMapping<'scope, V> {
pub fn get<'a>(&'a self, ind: ThingIndex<'scope>) -> &'a V {
unsafe { &self.0.get_unchecked(ind.0) }
}
// ...
}
}
Update:
This doesn't seem to work. I added a test that I expected would fail to compile and it compiled without complaint. Is there a way to repair it and make it work? What if I write a macro?
#[cfg(test)]
mod tests {
use crate::things::*;
#[test]
fn it_fails() {
Things::in_context(1, |r1| {
Things::in_context(5, |r2| {
let m1 = r1.make_mapping(());
let i2 = r2.make_index(3);
assert_eq!(*m1.get(i2), ());
});
})
}
}
Note: in_context is loosely based on Haskell's runST function. In Haskell the type-signature of runST requires RankNTypes. I wonder if perhaps this is impossible because the Rust compiler does nothing conjugate to the behavior of RankNTypes.
Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.
It seems like we cannot test for equality in the following case. Why is this? Is there a workaround? (I am using Rust 0.11).
trait A: PartialEq {}
#[deriving(PartialEq)]
enum T {Ta, Tb}
impl A for T {}
fn main() {
assert!(Ta == Ta);
assert!(Ta != Tb);
assert!(some_fn(&Ta, &Ta));
assert!(!some_fn(&Ta, &Tb));
}
fn some_fn(an_a: &A, another_a: &A) -> bool {
an_a == another_a
// ERROR ^~~~~~~~~~~~ binary operation `==` cannot be applied to type `&A`
}
fn another_fn(an_a: &A + PartialEq, another_a: &A + PartialEq) -> bool {
// ERROR: ^~~~~~~~~ only the builtin traits can be used as closure or object bounds
an_a == another_a
}
With help from Vladimir Matveev, I figured out how to use Any to downcast my trait to a concrete type and test the resulting value for equality:
// `Any` allows us to do dynamic typecasting.
use std::any::Any;
trait A {
// An &Any can be cast to a reference to a concrete type.
fn as_any(&self) -> &dyn Any;
// Perform the test.
fn equals_a(&self, _: &dyn A) -> bool;
}
#[derive(Debug, PartialEq)]
enum T {
Ta,
Tb,
}
// Implement A for all 'static types implementing PartialEq.
impl<S: 'static + PartialEq> A for S {
fn as_any(&self) -> &dyn Any {
self
}
fn equals_a(&self, other: &dyn A) -> bool {
// Do a type-safe casting. If the types are different,
// return false, otherwise test the values for equality.
other
.as_any()
.downcast_ref::<S>()
.map_or(false, |a| self == a)
}
}
fn main() {
assert_eq!(T::Ta, T::Ta);
assert_ne!(T::Ta, T::Tb);
assert!(some_fn(&T::Ta, &T::Ta));
assert!(!some_fn(&T::Ta, &T::Tb));
}
fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
// It works!
an_a.equals_a(another_a)
}
Here is the definition of the PartialEq trait:
pub trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
Note the Self parameter type. This means that eq() and ne() methods accept a parameter of the same type as implementor. For example:
impl PartialEq for i32 {
fn eq(&self, other: &i32) -> bool { ... }
}
impl PartialEq for String {
fn eq(&self, other: &String) -> bool { ... }
}
Note how type of other changes to reflect the type PartialEq is implemented for.
This is the problem. In trait objects, the actual type is erased and unavailable at runtime. This means that it is impossible to obtain a reference to a concrete type from a trait object; in particular, you can't go from &A to &T in your example.
This means that it is impossible to call methods accepting or returning the Self type on trait objects. Indeed, these methods always require a concrete type, but if you have only a trait object, there is no concrete type, and there is no way such method could work in any sensible way.
In certain cases of trait objects, you wish to compare them based on some properties exposed via the trait. You can achieve this by implementing methods on the trait type itself:
trait A {
fn id(&self) -> i32;
}
impl PartialEq for dyn A + '_ {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for dyn A + '_ {}
fn some_fn(an_a: &dyn A, another_a: &dyn A) -> bool {
an_a == another_a
}
This doesn't directly address the original case which wants to delegate back to the implementation of PartialEq of the underlying type, but you can combine the existing solution:
impl PartialEq for dyn A + '_ {
fn eq(&self, other: &Self) -> bool {
self.equals_a(other)
}
}
See also:
Why would I implement methods on a trait instead of as part of the trait?