Noob question about shared behaviour

Hi, I'm new at using Rust and as one of my projects I'm trying to create a basic physics engine based off of some old YouTube videos. These videos, however, aren't in Rust, and take an Object-Oriented approach.

I'm at the point that I want to create my second type of "collider", I have a sphere and now want to make a cuboid. Intersections between two colliders create an IntersectionData object. I want to be able to print data about the intersection from this object, and that worked fine when I had only one collider type:

impl IntersectData {
    pub fn log_intersection(shape1: &BoundingSphere, shape2: &BoundingSphere) {...}
}

Now, I'm not sure how I can make it so that the function just takes a generic "collider" for shape1 and shape2. I've tried using Traits and Generics a bit but I must be doing it wrong somehow. Could someone walk me through it a bit? Thanks.

Is this BoundingSphere type something that implements Collider?

If so, then you'd want the function to be generic over the trait. However, given how it looks, you might be better to make the log log the intersect between self and other. Additionally, you could make this generic over any collider.

Rust Playground

This is a playground link with comments explaining how to get there. If you need clarification, feel free to ask.

In Rust, shared behavior doesn't work as in other languages. I don't know how much do you know Rust, and because you said the videos used an other language with probably a different OOP approach, I'll maybe say a lot of thing you know.

In Rust, there is not class inheritance. It means you cannot create a class Collider, which implements intersect(...), and then create two other classes Sphere and Cuboid. You'll need to create what is called a trait. A trait is a set of functions with known name and signature, that can be implemented to various structs (classes) to implement a shared behavior. For example, you can have the following:

trait Collider {
    // or whatever you do to know if two objects or in collision
    fn get_shape(&self) -> Shape;
}

struct Sphere { ... }

impl Collider for Sphere {
    fn get_shape(&self) -> Shape {
        // Here, put your logic
    }
}

struct Cuboid { ... }

impl Collider for Cuboid {
    fn get_collide_info(&self) -> Shape {
        // same here
    }
}

Here, the trait Collider has an associated function get_shape, and both the Cuboid and the Sphere structs implement it. It means you can call the get_shape method on these two types. Note that the &self is needed, I'll explain it later.

Now, if you want your log_intersection function to take two objects implementing the Collider trait - that should give you the necessary tools to know if there is a collision or not - you have several ways.

The first way is using generics:

impl IntersectcData {
    pub fn log_intersection<S1: Collider, S2: Collider>(shape1: S1, shape2: S2) { ... }
} 

Here, you say that your function takes two objects of a known type (alias S1 and S2) that implement the trait Collider. This means that:

  • The type is known at compile time.
  • You can take ownership of shape1 and shape2.
  • shape1 and shape2 are not the same type.
  • You can only call on them the methods defined by Collider.

Technically, Rust will derive your function for every valid S1 and S2. (And keep only the used ones.) If you want to avoid this, an other method will be the following:

impl IntersectData {
    pub fn log_intersection(shape1: &dyn Collider, shape2: &dyn Collider) { ... }
}

Here, the function will not be derivated for every types. It means:

  • The function cannot take ownership of shape1 and shape2, the borrow is needed (& or &mut).
  • The function will not know the spefific type at compile time.
  • Both shape1 and shape2 will have the same type.
  • You can only use method defined by Collider

If you want to know more about traits and generics, you should read the related sections on the Book. If you give more details about you current project architecture and the way you manage the collision, we may be able to be more precise. Fell free to ask

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.