Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a function for structs like Java's instanceof?

I'm making an OOP chat client in Rust. The module messages.rs creates and handles messages to other modules as structs: SimpleMessage and ComplexMessage structs:

//! # Messages

use time::SteadyTime;

/// Represents a simple text message
pub struct SimpleMessage<'a> {
    pub user: ...
    pub time: &'a SteadyTime<'a>,
    pub content: &'a str,
}

/// Represents attachments, like text or multimedia files.
pub struct ComplexMessage<'a> {
    pub user: ...
    pub time: &'a SteadyTime<'a>,
    //pub content: PENDING
}

impl<'a> SimpleMessage<'a> { }
impl<'a> ComplexMessage<'a> { }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_simple() {
        assert_eq!(&self.instance_of(), SimpleMessage);
    }

    #[test]
    fn is_complex() {
        assert_eq!(&self.instance_of(), ComplexMessage);
    }
}

I'm troubled finding a Java-like function such as InstanceOf() for structs, that would potentially work like:

&self.instance_of() -> str

This would be used to process a ComplexMessage different from a SimpleMessage in a GUI, adding a preview and download button for the ComplexMessage.

Any ideas?

like image 219
Dialvive Avatar asked Aug 24 '18 13:08

Dialvive


1 Answers

First of all, if you try to port Java OOP idioms to Rust you are going to have a hard time. Rust programmers use completely different idioms and patterns, which are more suited to the design of the language.

That said, you can compare types using std::any::TypeId. A similar function to instanceOf can be implemented like this:

use std::any::{Any, TypeId};

trait InstanceOf
where
    Self: Any,
{
    fn instance_of<U: ?Sized + Any>(&self) -> bool {
        TypeId::of::<Self>() == TypeId::of::<U>()
    }
}

// implement this trait for every type that implements `Any` (which is most types)
impl<T: ?Sized + Any> InstanceOf for T {}

And use it like this:

let msg = ComplexMessage::new();

println!("msg is ComplexMessage: {}", msg.instance_of::<ComplexMessage>());
println!("msg is SimpleMessage: {}", msg.instance_of::<SimpleMessage>());

Outputs:

msg is ComplexMessage: true
msg is SimpleMessage: false

Note that Rust does not have a concept of type inheritance like Java does, so this will only tell you if it is exactly the same type.


A more Rusty approach to your problem, as DK commented below this answer, would be to use an enum to model the fact that you have two kinds of message. Rust enums are nothing like Java enums - they are just as powerful as structs except they model the idea of alternatives, as opposed to aggregates. Here is one way you could implement this using the types you have and wrapping them up:

enum Message<'a> {
    Complex(ComplexMessage<'a>),
    Simple(SimpleMessage<'a>),
}

Whenever a function can only accept a ComplexMessage you can write the signature to reflect that:

fn send_multimedia(msg: ComplexMessage) { ... }

And whenever you can accept either type, use the enum:

fn get_msg_size(msg: Message) -> usize {
    match(msg) {
        Message::Complex(complex) => complex.content.len() + complex.file.size(),
        Message::Simple(simple) => simple.content.len(),
    }
}
like image 116
Peter Hall Avatar answered Nov 20 '22 01:11

Peter Hall