Search code examples
genericsstructrustsyslog

What would be the signature of a function returning a generic struct?


I was going through the syslog crate, and from my function, I wanted to return a logger object which I believe is of a generic struct syslog::Logger. Most of the below piece is taken from the crate documentation except for the return part and unwrap().

extern crate syslog;
use syslog::{Facility, Error, Formatter3164};

pub fn get_logger() -> syslog::Logger{
    let formatter = Formatter3164 {
        facility: Facility::LOG_USER,
        hostname: None,
        process: "myprogram".into(),
        pid: 0,
    };
    syslog::unix(formatter).unwrap()
}

However I am seeing

pub fn get_logger() -> syslog::Logger{
                       ^^^^^^^^^^^^^^ expected 3 type arguments

The signature of syslog::Logger struct is

pub struct Logger<Backend: Write, T, Formatter: LogFormat<T>>

and the signature of syslog::unix is

pub fn unix<U: Display, F: Clone+LogFormat<U>>(formatter: F) -> Result<Logger<LoggerBackend, U, F>> {

where Display is std::fmt::Display and LogFormat is a trait defined in syslog crate with the following definition.

pub trait LogFormat<T> {
  fn format<W: Write>(&self, w: &mut W, severity: Severity, message: T)   -> Result<()>;

  fn emerg<W: Write>(&mut self, w: &mut W, message: T)   -> Result<()> {
    self.format(w, Severity::LOG_EMERG, message)
  }

  fn alert<W: Write>(&mut self, w: &mut W, message: T)   -> Result<()> {
    self.format(w, Severity::LOG_ALERT, message)
  }

  fn crit<W: Write>(&mut self, w: &mut W, message: T)    -> Result<()> {
    self.format(w, Severity::LOG_CRIT, message)
  }

  fn err<W: Write>(&mut self, w: &mut W, message: T)     -> Result<()> {
    self.format(w, Severity::LOG_ERR, message)
  }

  fn warning<W: Write>(&mut self, w: &mut W, message: T) -> Result<()> {
    self.format(w, Severity::LOG_WARNING, message)
  }

  fn notice<W: Write>(&mut self, w: &mut W, message: T)  -> Result<()> {
    self.format(w, Severity::LOG_NOTICE, message)
  }

  fn info<W: Write>(&mut self, w: &mut W, message: T)    -> Result<()> {
    self.format(w, Severity::LOG_INFO, message)
  }

  fn debug<W: Write>(&mut self, w: &mut W, message: T)   -> Result<()> {
    self.format(w, Severity::LOG_DEBUG, message)
  }
}

This is a generic struct, so what would be the type's signature that I should return if I want to return a logger object from my function?


Solution

  • The only optional parameter here is T. This is the type of the message you want when you are logging. E.g. in the below example, the log message type is a &str:

    let mut logger = get_logger();
    
    logger.info("This is a log message");
    

    You can either choose the type inside get_logger, and put it in its signature, or make get_logger generic, and have the call site decide. The first approach looks like this:

    extern crate syslog;
    use syslog::{Facility, Error, Formatter3164, LoggerBackend};
    
    fn get_logger<'a>() -> syslog::Logger<LoggerBackend, &'a str, Formatter3164>{
        let formatter = Formatter3164 {
            facility: Facility::LOG_USER,
            hostname: None,
            process: "myprogram".into(),
            pid: 0,
        };
        syslog::unix(formatter).unwrap()
    }
    
    pub fn main(){
      let mut logger = get_logger();
    
      logger.info("This is a log message");
    }
    

    And the generic approach looks like this.

    extern crate syslog;
    use syslog::{Facility, Error, Formatter3164, LoggerBackend};
    
    pub fn get_logger<T: std::fmt::Display>() -> syslog::Logger<LoggerBackend, T, Formatter3164>{
        let formatter = Formatter3164 {
            facility: Facility::LOG_USER,
            hostname: None,
            process: "myprogram".into(),
            pid: 0,
        };
        syslog::unix(formatter).unwrap()
    }
    
    pub fn main(){
      let mut logger = get_logger();
    
      logger.info("This is a log message");
    }