Help understanding documentation

Noobie here.
I started reading the book but started getting bored around chap 10 so I decided to rewrite an existing C++ app in Rust to get me going again. The first thing I needed to do was get system logging going. This "works" but I have some questions:

use syslog::Facility;
use log::LevelFilter;

fn main() {
    match syslog::init(Facility::LOG_USER,LevelFilter::Trace,None) {
        Err(e) => println!("Could not initialize syslog: {:?}",e),
        _      => ()
    }
    log::error!("This is an error message");
}

The documentation for syslog::init() says it returns Result(<>) which is apparently shorthand for Result((),Error). It seems odd to me to have to deal with the empty type but OK.

Can someone explain why the order of the match items is significant? If I put the 'Err(e)' line after the '_ => ()' line, I get an unreachable code warning.

The code produces this:
Aug 31 15:16:35 stevesdesktop myprog[5027]: This is an error message
in /var/log/syslog

I want to change that to format the date differently and include the level, source file and line number in the message. I see the docs on the Formatters but I don't see how to get the log macros to use them.

Steve

It is because the underscore pattern matches everything and it will match them in the order you write them. In your case, it is more idiomatic to match on Ok(()) for the ok case.

match syslog::init(Facility::LOG_USER,LevelFilter::Trace,None) {
    Err(e) => println!("Could not initialize syslog: {:?}",e),
    Ok(()) => ()
}

Then the order does not matter as they patterns do not overlap.

1 Like

What's odd about it? In what way do you have to "deal" with it?

Match arms are evaluated in the order they are written in the code. Otherwise, patterns that do not bind all subpatterns couldn't be evaluated unambiguously. For example, the following could match either pattern:

match (false, false) {
    (false, _) => println!("first"),
    (_, false) => println!("second"),
}

If the rule weren't that the first matching pattern is executed, then how would you decide which arm to execute in this situation?

They don't appear to be customizable.

There's an error handling pattern some crates use of creating their own error types named Error and then aliasing Result<T> to be Result<T, Error> (using their error type). That's what's happening here. If you go to the docs and click around enough, you'll find it's really returning a Result<(), syslog::Error>. You'll also see this pattern in std::io for example.

If you need to use (type out) their types and don't want to occlude the standard ones, you can do something like

use syslog::Error as SyslogError;
// And just ignore their `Result` alias, or if you prefer to use it
use syslog::Result as SyslogResult;

Or just use paths like syslog::Error directly.

P.s. you can surround your inline code with single backticks (`) in your forum posts.

Make sense, thanks.

Think of () like void in other languages. Returning Result<(), Error> is like saying you'll either complete successfully with no interesting return value, or fail with an error.

That's how I was thinking about it which is why I thought it odd that you have to specifically say do nothing with the return value that is nothing.

match (false, false) {
    (false, _) => println!("first"),
    (_, false) => println!("second"),
}

If the rule weren't that the first matching pattern is executed, then how would you decide which arm to execute in this situation?

You could execute both arms since both match but that's not how match works. I was mis-understanding the '_' character. I though it meant nothing when it actually means anything.

They don't appear to be customizable.

I guess I would have to write my own macros then? Time to read that section of the book.

Usually people do not handle errors by matching on the Result, instead using things like the question mark operator.

You can also write

if let Err(e) = syslog::init(Facility::LOG_USER,LevelFilter::Trace,None) {
    println!("Could not initialize syslog: {:?}", e);
}

to only check for an error and do nothing if there isn't one. I would usually write it like this if I'm only interested in one variant of an enum.

1 Like

Indeed, but that would be problematic once again, when you consider that match is also an expression, and so it needs to have a single definite value and type.

So I've almost finished The Book now and I wrote this program to add the file and line number to the syslog entries:

extern crate syslog;

use std::process;
use syslog::{Facility, Formatter3164};

macro_rules! my_log {
    ($logger: ident, $log_msg: expr) => {
        match $logger.warning(format!("{}, {}: {}", file!(), line!(), $log_msg)) {
            Err(error) => panic!("Problem logging: {:?}", error),
            _ => (), 
        };
    }   
}

fn main() {
    let formatter = Formatter3164 {
            facility: Facility::LOG_USER,
            hostname: None,
            process: "this".to_string(),
            pid: process::id() as i32,
    };  
    let mut logger = syslog::unix(formatter).expect("could not connect to syslog");

    my_log!(logger, "log msg");
}

which works and produces this in /var/log/syslog

Oct 8 18:10:52 stevesdesktop this[24283]: src/main.rs, 24: log msg

but the log level is missing and I get the same result if I use $logger.error instead of $logger.warning.
I assume I am using this incorrectly. Could you point out where.

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.