How can I test that two structs have the same value without cloning? - testing

I write tests in other languages where I explicitly define expected and actual values. In Rust, I was running into lifetime errors because my test code creates a struct that uses a value from another struct.
Two ways I have found to avoid this is to:
use references with the struct type defined with a pointer
use cloning.
I have seen many posts asking how to avoid excessive cloning so I wonder if there is a more idiomatic way of doing this.
Can I get rid of the clone in the following test:
#[derive(Clone, Debug, PartialEq)]
struct SomethingElse {
a: u16,
}
struct Something {
inner: SomethingElse,
}
impl Something {
pub fn do_something(&self) -> SomethingElse {
return self.inner.clone();
}
}
mod tests {
#[cfg(test)]
use super::*;
#[test]
fn something_does_something() {
let expected = SomethingElse { a: 1 };
let something = Something {
inner: expected.clone(),
};
assert_eq!(something.do_something(), expected);
}
}

In any programming language, "best practices" for production code are very different from best practices for tests. In particular, you probably don't much care about performance in tests. The most important thing is that someone reading the test can see what it does and that it doesn't contain too much noise or indirection that distracts from what the test is actually testing.
If you need to assert that a field of one struct contains the same data as some field of another struct, cloning seems like exactly the right thing to do. In your case, since it's simple, you might even consider duplicating the code that describes the inner struct:
#[test]
fn something_does_something() {
let something = Something {
inner: SomethingElse { a: 1 },
};
assert_eq!(something.do_something(), SomethingElse { a: 1 });
}

I'm not saying that this is idiomatic, or wise in this particular case, but you can avoid cloning by comparing through a reference. Instead of
let expected = SomethingElse { a: 1 };
let something = Something {
inner: expected.clone(),
};
assert_eq!(something.do_something(), expected);
write
let something = Something {
inner: SomethingElse { a: 1 },
};
let expected = &something.inner;
assert_eq!(&something.do_something(), expected);
I would only do this if it is actually important to the properties being tested that the value not be cloned (perhaps because its impl Clone does something special).

Related

is `as_ref` rust's ways to implement idiomatic generic?

As I understand it, rust is a "has a" and not a "is a" language (composition over inheritance).
this makes Liskov substitutions slightly more complicated but not impossible using Traits. While I can use LSP, it appears to not be the idiomatic way of implementing type coercion in rust. I'm left confused of how to operate.
minimal example
Let's assume I have two structs
struct Real(i32);
struct Complex(Real, Real);
And a function project which takes a Complex and return a projection of the input.
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
// we pass by reference because we need to be blazingly fast
fn project(c : &Complex) -> Complex {
Complex(c.0, Real(0))
}
fn main() {
let a = Complex(Real(1), Real(2));
let x = project(&a);
println!("{} + {}i", x.0.0, x.1.0)
}
To keep things simple, please assume we are the situation in which we benefit from passing Real by reference and project should not be duplicated as multiple implementation from a trait for Real and Complex.
Assume we expect to also use project on Reals from time to time.
Making project somewhat generic
My OOP instincts pushes me to make some supertype for Real and Complex, let's say the trait AsReal
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
trait AsReal {
fn as_real(&self) -> Real;
}
impl AsReal for Real { fn as_real(&self) -> Real { *self } }
impl AsReal for Complex { fn as_real(&self) -> Real { self.0 } }
fn project (r : &impl AsReal) -> Complex {
Complex( r.as_real(), Real(0) )
}
fn main() {
let a = Real(1);
let b = Complex(Real(2), Real(3));
let x = project(&a);
let y = project(&b);
println!("{} + {}i", x.0.0, x.1.0);
println!("{} + {}i", y.0.0, y.1.0);
}
But apparently, the rusty way would be to use AsRef<Real> instead
#[derive(Clone, Copy)]
struct Real(i32);
struct Complex(Real, Real);
fn project<U: AsRef <Real>>(r : U) -> Complex {
Complex ( *r.as_ref(), Real(0) )
}
impl AsRef<Real> for Complex {
fn as_ref(&self) -> &Real { &self.0 }
}
impl AsRef<Real> for Real {
fn as_ref(&self) -> &Real { self }
}
fn main() {
let a = Real(1);
let b = Complex(Real(2), Real(3));
let x = project(&a);
let y = project(&b);
println!("{} + {}i", x.0.0, x.1.0);
println!("{} + {}i", y.0.0, y.1.0);
}
Which leaves me unsatisfied : the prototype for project became very wordy and hard to read. So much so it feels like the convenience of use for project is simply not worth it.
Furthermore, it means the function must opt-in for Complex into Real coercion and I dislike that notion as it feel like it pushes me to develop defensively and use AsRef<...> all the time.
I don't feel like I have the full picture, what would be the idiomatic way to interact with rust for situation like this ?
Based on your description, it seems like you could go with this:
project takes a Real
Complex provides an into_real() method that returns a Real
Small sidenote: if your types are small and Copy, a pointer isn't always faster. Compiler explorer can be a great tool for showing you what the assembly for a snippet is/
That being said, I'd write it like this.
fn project(real: Real) -> Real {
// very complex logic
}
// deriving Copy makes these types behave more like numbers
#[derive(Copy, Clone)]
struct Real(i32);
#[derive(Copy, Clone)]
struct Complex(Real, Real);
impl Complex {
fn into_real(self) -> Real {
self.0
}
}
fn main() {
let real = Real(0);
let complex = Complex(Real(0), Real(0));
project(real);
project(complex.into_real());
}
If you really hate having to write into_real(), you could make the call-site simpler and make the declaration-site more complex by:
implementing From<Complex> for Real (though arguably this needs its own trait since there's more than one way to get a Real from a Complex)
making project accept an impl Into<Real>
impl From<Complex> for Real {
fn from(c: Complex) {
c.0
}
}
fn project(real: impl Into<Real>) {
let real = real.into();
// real is a `Real` here
}
Though honestly, I'd just go for the first option. Your function doesn't really need to be generic, and that increases monomorphization cost. It's not very OOP, but Rust is not an OOP language.

How do I use rust traits to abstract HTTP call for tests?

Coming from Go there are a lot of interfaces you can use to do something like the below:
async fn get_servers(client: &dyn std::marker::Send) -> Result<String, impl std::error::Error> {
let servers_str = client.send().await?.text()
let v: Value = serde_json::from_str(servers_str)?;
println!("{:?}", v);
Ok(servers_str.to_string())
}
// ...
get_servers(client.get(url))
I could pass in something that just implemented the send and return the text. That way makes the code testable. I thought maybe the send auto trait would do that but apparently not. Says send not found. Maybe some kind of impl requestbuilder?
In general, this is absolutely possible and (correct me if I'm wrong) even advised. It's a programming paradigm called dependency injection.
Simplified, this means in your case, pass in the dependent object via an interface (or in Rust: trait) so you can replace it at test time with an object of a different type.
Your mistake here is that the std::marker::Send trait does not what you think it does; it marks objects for being transferrable between threads. It's closely linked to std::marker::Sync, meaning, it can be accessed by multiple threads without causing race conditions.
While many libraries already have traits you can use for that purpose, in a lot of cases you will have to set up your own trait. Here, for example, we have a hello world function, that gets tested by replacing its printer with a different one, specialized for testing. We achieve that by passing the printer into the hello world function through the abstraction of a trait, as already mentioned.
trait HelloWorldPrinter {
fn print_text(&mut self, msg: &str);
}
struct ConsolePrinter;
impl HelloWorldPrinter for ConsolePrinter {
fn print_text(&mut self, msg: &str) {
println!("{}", msg);
}
}
// This is the function we want to test.
// Note that we are using a trait here so we can replace the actual
// printer with a test mock when testing.
fn print_hello_world(printer: &mut impl HelloWorldPrinter) {
printer.print_text("Hello world!");
}
fn main() {
let mut printer = ConsolePrinter;
print_hello_world(&mut printer);
}
#[cfg(test)]
mod tests {
use super::*;
struct TestPrinter {
messages: Vec<String>,
}
impl TestPrinter {
fn new() -> Self {
Self { messages: vec![] }
}
}
impl HelloWorldPrinter for TestPrinter {
fn print_text(&mut self, msg: &str) {
self.messages.push(msg.to_string());
}
}
#[test]
fn prints_hello_world() {
let mut printer = TestPrinter::new();
print_hello_world(&mut printer);
assert_eq!(printer.messages, ["Hello world!"]);
}
}
When doing cargo run:
Hello world!
When doing cargo test:
Running unittests src/main.rs
running 1 test
test tests::prints_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
As a little explanation, if that code doesn't explain itself:
we create a trait HelloWorldPrinter whic his the only thing our print_hello_world() function knows about.
we define a ConsolePrinter struct that we use at runtime to print the message. The ConsolePrinter of course has to implement HelloWorldPrinter to be usable with the print_hello_world() function.
for testing, we write the TestPrinter struct that we use instead of the ConsolePrinter. Instead of printing, it stores what it received so we can test whether it got passed the correct message. Of course, the ConsolePrinter also has to implement the HelloWorldPrinter trait to be usable with print_hello_world().
I hope that goes into the direction of your question. If you have any questions, feel free to discuss further.
I can't directly tell you what you should write to solve your problem, as your question is quite vague, but this should be the toolset you need to solve your problem. I hope.

How to implement From trait for custom error types?

I am currently trying to write a custom error type for my CLI application. Now I want to write an implementation of the From trait so my custom error type can wrap all third party library errors that can occur.
The error enum:
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
Now I want to implement the From Trait for the git2::Error from the git2 library to use the ? operator in my functions.
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
But when I try to use my custom error to map an error like this:
let repo = Repository::open(path).map_err(|err| CustomError::Git)?;
I am getting the following error message:
the trait `std::convert::From<fn(git2::error::Error) -> error::CustomError {error::CustomError::Git}>` is not implemented for `error::CustomError `
Can anyone help me to understand why I am getting this error and how to solve this problem ?
Any help is appreciated
You've mixed up a whole bunch of concepts; let's see if we can walk through this together and hopefully clarify all of it.
The git2 crate has its own error type, that you no doubt have discovered. Your definition of custom errors is fine as well.
The issue is twofold:
Your implementation of From<_>
From<E> allows you to transform a type from one type to another by providing the translation function (from()).
Your implementation of this was the following:
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
Brackets in rust aren't added where they should not be, and this is precisely one of the cases where this is the case. By doing this, you've actually defined From<(T)>, not From<T>. That's mistake #1.
The correct implementation simply drops the brackets:
impl From<git2::Error> for CustomError {
fn from(cause) -> Self {
CustomError::Git(cause)
}
}
Your actual conversion
Not an error per se, but a completely unnecessary operation as the ? operator handles it for you. There is no need for the map_err(), and if there was you'd be using into() rather than hard-calling the type (which should already be defined as a type in your function).
Remember, the whole point of conversion traits is to define them so you don't have to explicitly call them.
A final "demo" version of the code in working order could look like this:
extern crate git2;
use git2::Repository;
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
fn test() -> Result<(), CustomError> {
let path = "foo";
let output = Repository::open(path)?;
Ok(())
}
fn main() {
println!("Hello, world!");
}

How does the ? operator interact with the From trait?

Say I have the following:
use std::fs::File;
impl From<i32> for Blah {
fn from(b:i32) -> Blah {
Blah {}
}
}
fn main() {}
enum MyError {
ParseError,
}
impl From<std::io::Error> for MyError {
fn from(_:std::io::Error) -> Self {
MyError::ParseError
}
}
fn get_result() -> Result<Blah, MyError> {
let mut file = File::create("foo.txt")?;
}
This compiles fine. I don't understand how.
File::create throws an std::io::error, which we're trying to wrap in a MyError. But we never explicitly call from anywhere!? How does it compile?
As the comments from this answer Rust understanding From trait indicate, you do have to explicitly call from.
So, how is the above snippet compiling?
The difference is stated in The Rust Programming Language, chapter 9, section 2, when talking about the ? operator:
Error values that have the ? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another. When the ? operator calls the from function, the error type received is converted into the error type defined in the return type of the current function. This is useful when a function returns one error type to represent all the ways a function might fail, even if parts might fail for many different reasons. As long as each error type implements the from function to define how to convert itself to the returned error type, the ? operator takes care of the conversion automatically.
You have provided this implementation of From<std::io::Error> for that error type, therefore the code will work and convert values of this type automatically.
The magic is in the ? operator.
let mut file = File::create("foo.txt")?;
expands to something like (source)
let mut file = match File::create("foo.txt") {
Ok(t) => t,
Err(e) => return Err(e.into()),
};
This uses the Into trait, which is the counterpart to the From trait: e.into() is equivalent to T::from(e). Here you have the explicit conversion.
(There is an automatic impl<T, U> Into<U> for T for every impl<T, U> From<T> for U, which is why implementing From is enough.)

Is it possible to cast a trait object to another trait object? [duplicate]

This question already has answers here:
Why doesn't Rust support trait object upcasting?
(5 answers)
Closed 2 years ago.
I tried the following code:
trait TraitA {
fn say_hello(&self) {
self.say_hello_from_a();
}
fn say_hello_from_a(&self);
}
trait TraitB {
fn say_hello(&self) {
self.say_hello_from_b();
}
fn say_hello_from_b(&self);
}
struct MyType {}
impl TraitA for MyType {
fn say_hello_from_a(&self) {
println!("Hello from A");
}
}
impl TraitB for MyType {
fn say_hello_from_b(&self) {
println!("Hello from B");
}
}
fn main() {
let a: Box<dyn TraitA> = Box::new(MyType {});
let b: Box<dyn TraitB>;
a.say_hello();
b = a;
b.say_hello();
}
I get the following compilation error:
error[E0308]: mismatched types
--> src/main.rs:34:9
|
34 | b = a;
| ^ expected trait `TraitB`, found trait `TraitA`
|
= note: expected struct `std::boxed::Box<dyn TraitB>`
found struct `std::boxed::Box<dyn TraitA>`
I declared two traits and a type called MyType and implemented both traits for MyType. I created a new trait object TraitA of type MyType which I called a. Since a also implements TraitB, I thought it should be able to be casted as TraitB.
I haven't figured out if it's even possible. If it is, how can I cast trait object a into TraitB?
In C++, I would use something similar to std::dynamic_pointer_cast<TraitB>(a); for the same purpose.
Here's an example of a case where I could use lateral casting: I have a struct with some data inside that represents some real life entity:
struct MyType {
a: i32,
b: i32,
}
Instances of this type can be used in at least two different parts of the code base. On both parts I need a behavior called get_final_value.
The interesting part is that get_final_value should respond differently depending on who called it.
Why don't I split the type into two different ones?: Technically, by design, a and b belong together, not to say that get_final_value() uses both values to compute the result.
Why not use generics/static dispatch? Because MyType is just one example. In the real case I have different structs, all of them implementing both traits in different ways.
Why not use Any trait? To be honest, I didn't know of it's existence until recently. I don't recall The Rust Programming Language mentioning it. Anyway, it seems you need to know the concrete type to do a cast from Any to that concrete type and then to the trait object.
Another option is to create a trait that uses both TraitA and TraitB as supertraits and provides a cast to each type:
trait TraitC: TraitA + TraitB {
fn as_trait_a(&self) -> &dyn TraitA;
fn as_trait_b(&self) -> &dyn TraitB;
}
Then have MyType implement it:
impl TraitC for MyType {
fn as_trait_a(&self) -> &dyn TraitA {
self
}
fn as_trait_b(&self) -> &dyn TraitB {
self
}
}
Once you do that, you can use TraitC for your Box and your program logic that uses both TraitA and TraitB together.
Sample main to show various ways to use:
fn test_a(a: &TraitA) {
a.say_hello();
}
fn test_b(b: &TraitB) {
b.say_hello();
}
fn main() {
let c: Box<dyn TraitC> = Box::new(MyType {});
TraitA::say_hello(&*c);
TraitB::say_hello(&*c);
c.as_trait_a().say_hello();
c.as_trait_b().say_hello();
test_a(c.as_trait_a());
test_b(c.as_trait_b());
let a: &dyn TraitA = c.as_trait_a();
a.say_hello();
let b: &dyn TraitB = c.as_trait_b();
b.say_hello();
}
Rust Playground
If A and B do truly belong together, this better represents that and still gives you the freedom to use them separately if you desire.
Using Box<MyType> instead of Box<dyn Trait> solves this problem.
fn main() {
let a = Box::new(MyType {});
TraitA::say_hello(&*a);
TraitB::say_hello(&*a);
}
In this case there's no need to use trait objects. Rust has a different paradigm from C++. In most cases you can usually use generic types to solve problems.
If your problem is really suitable to solve with trait objects, you can refer to the OOP chapter in the book.