Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

boolean-validation

Level: warn · Article: Errors

Your type signatures are lying to you.

What it checks

A function named validate_*, verify_* or is_valid* that returns bool. Plain predicates (is_empty, check_flag) are not validation and are left alone. Validation has a reason to say no, and false cannot carry it.

Don’t

#![allow(unused)]
fn main() {
fn validate_email(s: &str) -> bool {
    s.contains('@') && !s.starts_with('@')
}

fn register(input: &str) -> Result<(), ApiError> {
    if !validate_email(input) {
        return Err(ApiError::Invalid("email")); // which rule? the user has to guess
    }
    Ok(())
}
}

Do

#![allow(unused)]
fn main() {
enum EmailError {
    MissingAt,
    MissingLocalPart,
}

struct Email(String);

impl Email {
    fn parse(s: &str) -> Result<Self, EmailError> {
        if !s.contains('@') {
            return Err(EmailError::MissingAt);
        }
        if s.starts_with('@') {
            return Err(EmailError::MissingLocalPart);
        }
        Ok(Email(s.to_lowercase()))
    }
}
}

The reason travels with the failure, and the success is a type: nothing past this line checks the email again.

Silence it

#![allow(unused)]
fn main() {
// rabot: allow(boolean-validation) a pure predicate used in a filter; there is no caller to inform
fn is_valid_utf8(bytes: &[u8]) -> bool { .. }
}