Why can't i use an enum that implements the same trait?

src/mods.rs

pub mod app;
pub mod limit_tracker;
pub mod messenger;

src/mods/messenger.rs

pub trait Messenger {
    fn send(&self, msg: &str);
}

src/mods/app.rs

use super::messenger::Messenger;

#[derive(Debug)]
pub enum App {
    Telegram,
    Session,
    Tutanota,
}

impl Messenger for App {
    fn send(&self, msg: &str) {
        let common_string = "Sending a message via";

        match self {
            Self::Telegram => println!("{} telegram: `{}`", common_string, msg),
            Self::Session => println!("{} session: `{}`", common_string, msg),
            Self::Tutanota => println!("{} tutanota: `{}`", common_string, msg),
        }
    }
}

src/mods/limit_tracker.rs

use std::fmt::Debug;

use super::{app::App, messenger::Messenger};

pub struct LimitTracker<'a, T>
where
    T: Messenger,
{
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger + Debug,
{
    pub fn new(messenger: &'a T, max: usize) -> Self {
        Self {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn print(&self) {
        let common_string = "Limit tracking in";

        match self.messenger {
            App::Telegram => println!("{} Telegram", common_string),
            App::Session => println!("{} Session", common_string),
            App::Tutanota => println!("{} Tutanota", common_string),
        }

        // println!("Messenger: {:?}", self.messenger);
        // println!("Value: {}", self.value);
        // println!("Max: {}", self.max);
        // println!();
    }
}

src/main.rs

pub mod mods;

use mods::{app::App, limit_tracker::LimitTracker, messenger::Messenger};

fn main() {
    let telegram = App::Telegram;
    let session = App::Session;
    let tutanota = App::Tutanota;

    telegram.send("hello from Telegram");
    session.send("hello from  Session");
    tutanota.send("hello from Tutanota");

    println!();

    let telegram_limit_tracker = LimitTracker::new(&telegram, 10);
    let session_limit_tracker = LimitTracker::new(&session, 4);
    let tutanota_limit_tracker = LimitTracker::new(&tutanota, 1);

    telegram_limit_tracker.print();
    session_limit_tracker.print();
    tutanota_limit_tracker.print();
}

I am getting this error:

error[E0308]: mismatched types
  --> src/mods/limit_tracker.rs:30:13
   |
14 | impl<'a, T> LimitTracker<'a, T>
   |          - this type parameter
...
29 |         match self.messenger {
   |               -------------- this expression has type `&T`
30 |             App::Telegram => println!("{} Telegram", common_string),
   |             ^^^^^^^^^^^^^ expected type parameter `T`, found `App`
   |
  ::: src/mods/app.rs:5:5
   |
5  |     Telegram,
   |     -------- unit variant defined here
   |
   = note: expected type parameter `T`
                        found enum `App`

error[E0308]: mismatched types
  --> src/mods/limit_tracker.rs:31:13
   |
14 | impl<'a, T> LimitTracker<'a, T>
   |          - this type parameter
...
29 |         match self.messenger {
   |               -------------- this expression has type `&T`
30 |             App::Telegram => println!("{} Telegram", common_string),
31 |             App::Session => println!("{} Session", common_string),
   |             ^^^^^^^^^^^^ expected type parameter `T`, found `App`
   |
  ::: src/mods/app.rs:6:5
   |
6  |     Session,
   |     ------- unit variant defined here
   |
   = note: expected type parameter `T`
                        found enum `App`

error[E0308]: mismatched types
  --> src/mods/limit_tracker.rs:32:13
   |
14 | impl<'a, T> LimitTracker<'a, T>
   |          - this type parameter
...
29 |         match self.messenger {
   |               -------------- this expression has type `&T`
...
32 |             App::Tutanota => println!("{} Tutanota", common_string),
   |             ^^^^^^^^^^^^^ expected type parameter `T`, found `App`
   |
  ::: src/mods/app.rs:7:5
   |
7  |     Tutanota,
   |     -------- unit variant defined here
   |
   = note: expected type parameter `T`
                        found enum `App`

For more information about this error, try `rustc --explain E0308`.

Why can't i use the enum App when both LimitTracker and App implement the Messenger trait ?

Because you can only refer to things defined in the Messenger trait (namely your send method), when you are using a generic type T with the T: Messenger trait bound. T: Messenger allows you to pass any type that implements Messenger to LimitTracker, not just App.

If you want to refer to App's variants, change your LimitTracker to store &'a App in its messenger field, not some generic type T: Messenger, or add a method print(&self) to Messenger and implement the match statement from your LimitTracker::print function in the Messenger impementation of App. Example.

5 Likes

If the bound allows me to pass any type that implements the trait, why can't i pass App then ?! Don't you see a contradiction here.

As a caller (i.e. in main), you can - when you call LimitTracker::new, you do exactly that: you pass App as an argument of type T.

As an implementer (i.e. in impl LimitTracker), however, you don't know whether the caller have actually passed an App or some other type implementing Messenger.

2 Likes

I am talking about this part in src/mods/limit_tracker.rs

 pub fn print(&self) {
        let common_string = "Limit tracking in";

        // match self.messenger {
        //     App::Telegram => println!("{} Telegram", common_string),
        //     App::Session => println!("{} Session", common_string),
        //     App::Tutanota => println!("{} Tutanota", common_string),
        // }

        println!("Messenger: {:?}", self.messenger);
        println!("Value: {}", self.value);
        println!("Max: {}", self.max);
        println!();
    }

Isn't self.messenger supposed to be any type that implements Messenger ? Then why can't i match against App ?

If T: Animal, can you assume it's a Cat? No, because it might be a Dog.

If T: Messenger, can you assume it's an App? No, because it might be something else that implements Messenger.

Your generic implementations have to work for any and all types which meet the stated bounds. Not just the ones you call it with, nor just with one single type you assume in the implementation.

5 Likes

Aha i think got it, so the genericness and trait bounding works only when passing data as arguments to functions and methods, but the blueprints themselves (functions and methods) should be configured to deal with and accept all types with specific bounds.

Yes, you can think of it as an API contract (on a function, say) -- the implementation gets to assume all the bounds are met (but nothing more), while the caller must satisfy all the bounds (but nothing more).

Then any type which meets the bounds can call the function and it will still work.

3 Likes

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.