I'm trying to implement something in Rust that works like a C++ virtual function in a class, I would have a base struct with data, then I would keep some functions undefined, like the following example:
class A {
int stuff;
public:
virtual void foo(int a, int b) = 0;
void function_that_calls_foo() { /*...*/ foo(1, 2); /*...*/ }
}
class B: public A { void foo(int a, int b) { /* ... */ } }
I was trying to implement it using function pointers, but without much success. I could use a trait with A's functions, and implement A on the other class, but I would lose the struct's data. What's the best (fastest?) way to implement this kind of thing in Rust?
struct A {
...
}
impl A {
fn function_that_calls_foo(&self) {
...
self.foo(a, b);
...
}
}
struct B {
a: A;
}
impl B {
fn xxx(&self) {
a.function_that_calls_foo(1, 2);
}
fn foo(&self, a: i32, b: i32) {...}
}
keep some functions undefined
I'm adding the implicit "and have some functions that call that to-be-defined function".
As E_net4 says, use a trait:
trait Foo {
fn foo(&self, a: i32, b: i32) -> i32;
fn function_that_calls_foo(&self) {
println!("{}", self.foo(1, 2));
}
}
You can then implement the trait for Base:
struct Base {
stuff: i32,
}
impl Foo for Base {
fn foo(&self, a: i32, b: i32) -> i32 {
self.stuff + a + b
}
}
And as Matthieu M. says, Rust doesn't have inheritance, so use composition:
struct Base {
stuff: i32,
}
impl Base {
fn reusable(&self) -> i32 {
self.stuff + 1
}
}
struct Alpha {
base: Base,
modifier: i32,
}
impl Foo for Alpha {
fn foo(&self, a: i32, b: i32) -> i32 {
(self.base.reusable() + a + b) * self.modifier
}
}
You can combine the two concepts as well, by taking a generic that is constrained by a type parameter.
I'll strongly second Dietrich Epp's point. Using a new language should involve checking out new paradigms. Inheritance for the purposes of code reuse is not usually a great idea, even in languages that support it. Instead, create smaller building blocks and combine them together.
Related
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);
}
}
I'm trying to grok the appropriate way to split up my binary project into multiple files. In the Rust book, 7.1 Packages and Crates starts off by including an example of creating a bin project with cargo, but the rest of the examples are lib. I don't really care about the hierarchy of my code since I'm not exposing it in a library. Other, older questions (for example) mention that you generally need <module_name>/mod.rs; while this isn't strictly required, it does seem appropriate for larger library projects.
I have three files:
src/main.rs:
mod A;
mod B;
fn main() {
let a = A::A { a: 123 };
let b = B::B { b: 3.14 };
println!("{:?}", a);
println!("{:?}", b);
}
src/A.rs:
use crate::B;
#[derive(Debug)]
pub struct A {
pub a : u32
}
impl From<B::B> for A {
fn from(b : B::B) -> A {
A { a: b.b as u32 }
}
}
src/B.rs:
use crate::A;
#[derive(Debug)]
pub struct B {
pub b : f32
}
impl From<A::A> for B {
fn from(a : A::A) -> B {
B { b: a.a as f32 }
}
}
This works as expected (snake case warnings removed for brevity):
$ cargo run
A { a: 123 }
B { b: 3.14 }
However, this seems like a bit more song-and-dance than should be necessary: qualifying references with their module name for every reference seems excessive. I would have assumed that I could do something like mod A::A or use A::* to 'import' types, functions, etc.
Is this the expected, idiomatic way to split code up?
Edit:
Per discussion, I updated my source to use use a::*. New code is:
main.rs:
mod a;
mod b;
use a::*; // !
use b::*; // !
fn main() {
let a = A { a: 123 };
let b = B { b: 3.14 };
println!("{:?}", a);
println!("{:?}", b);
}
a.rs:
use crate::b::*;
#[derive(Debug)]
pub struct A {
pub a : u32
}
impl From<B> for A {
fn from(b : B) -> A {
A { a: b.b as u32 }
}
}
b.rs:
use crate::a::*;
#[derive(Debug)]
pub struct B {
pub b : f32
}
impl From<A> for B {
fn from(a : A) -> B {
B { b: a.a as f32 }
}
}
Including both mod a; and use a::*; (changed to follow snake_case convention, but importantly to prevent name conflicts with the previously uppercase A module from conflicting with the still-uppercase A struct) now lets components be referenced without qualification.
I have a module file (src/map/map.rs):
type Layer = Vec<Vec<Tile>>;
pub struct Map {
height: i32,
width: i32,
data: Layer,
rooms: Vec<Rect>,
}
impl Map {
pub fn new(width: i32, height: i32) -> Self {
Map {
height: height,
width: width,
data: vec![vec![Tile::wall(); height as usize]; width as usize],
rooms: vec![Rect],
}
}
pub fn generate_with(&self, creator: module) {
creator::generate(&self)
}
}
a nested module map::gen::dungeon::basic (src/map/gen/dungeon/basic.rs)
with one function in the file:
pub fn generate(map: &mut Map) -> (Map, (i32, i32)) {}
and the map module file (src/map/mod.rs):
mod rect;
mod tile;
mod map;
pub mod room;
pub mod gen;
pub use self::map::Map;
pub use self::rect::Rect;
pub use self::tile::Tile;
imported into main.rs like this:
mod map;
use map::*;
use map::gen;
I want to be able to use it like this:
let (map, (player_x, player_y)) = Map::new(MAP_WIDTH, MAP_HEIGHT).generate_with(gen::dungeon::basic);
the error I get though is:
[cargo] expected value, found module 'gen::dungeon::basic': not a value [E]
A complete repo is available.
As stated in the comments, a module is not a concrete concept like that; what you are attempting to do is not possible.
Instead, you can pass something that can be a value:
mod basic {
pub fn generate() -> u8 {
0
}
}
mod advanced {
pub fn generate() -> u8 {
42
}
}
fn play_the_game(generator: fn() -> u8) {
let dungeon = generator();
println!("{}", dungeon);
}
fn main() {
play_the_game(basic::generate);
play_the_game(advanced::generate);
}
You could also introduce a trait and pass the implementing type as a generic:
trait DungeonGenerator {
fn generate() -> u8;
}
mod basic {
use DungeonGenerator;
pub struct Basic;
impl DungeonGenerator for Basic {
fn generate() -> u8 {
0
}
}
}
mod advanced {
use DungeonGenerator;
pub struct Advanced;
impl DungeonGenerator for Advanced {
fn generate() -> u8 {
42
}
}
}
fn play_the_game<G>()
where
G: DungeonGenerator,
{
let dungeon = G::generate();
println!("{}", dungeon);
}
fn main() {
play_the_game::<basic::Basic>();
play_the_game::<advanced::Advanced>();
}
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?
Rust doesn't seem to distinguish between different implementations of a trait only if they differ by an associated type.
How can I implement a method on all kinds of collections/iterators, but have specific implementations for each concrete type they contain?
error: conflicting implementations for trait Foo [E0119]
The code:
trait Foo { fn foo(self); }
impl<T> Foo for T
where T: IntoIterator<Item=u32>
{
fn foo(self) {
self.into_iter();
}
}
impl<T> Foo for T
where T: IntoIterator<Item=u16>
{
fn foo(self) {
self.into_iter();
}
}
fn main() {
vec![0u32].foo();
vec![0u16].foo();
}
You can not do the generic form directly, which is issue #20400. You'll have to introduce either a trait that can be used as a bound on T::Item to merge the two implementations, or wrapper types. E.g. the first one might look like:
trait FooIterItem {
// behaviours needed for Foo impl
}
impl FooIterItem for u32 { ... }
impl FooIterItem for u16 { ... }
impl<T> Foo for T
where T: IntoIterator, T::Item: FooIterItem
{
...
}