Pre-RFC: Validation Trait

This is likely a very bad idea, very useless, but its worth a shot

Anyways, I'm suggesting to add a very simple trait: Validate, which allows types like NonZero to be validated, this trait is useless in safe code, but can be useful for very performance-reliant code which uses 'unchecked' functions

Questions i asked

  • where will this be located, in a new module or an existing one (and if in an existing one, where?)
  • Will this do anything? is it just useless?
  • How will this be implemented? is it just going to be a one-function trait
trait Validate {
    fn valid(&self) -> bool;
}

or will it have more?

trait Validate {
    fn valid(&self) -> bool;
    /* More */
}
  • Can this optimize performance (by the compiler), or will it do nothing and be only useful for users

Motivation

This trait allows users to validate their types, thus (probably) making it more performant due to less checks (for example if they already checked if a value was valid, they can use an 'unchecked' function with that value, knowing it is valid)

Implementation

As said above, i do not really know yet how many members it should have. But there are a few things i know for sure it should have a 'valid' function (could also be 'is_valid')

trait Validate {
    fn valid(&self) -> bool;
}

Alternatives

  • Using a struct with a Boolean field, though that is less flexible
    • However, maybe another can use this trait to validate data, and thus always be valid, which would (probably) improve performance

Feel free to flame me in the replies (or whatever they are called) for suggesting a stupid idea, this is my first time contributing to rust

1 Like

This is probably a question better suited to the users forum, as new traits can be added to many libraries (and often should be prototyped there before being promoted to std).

Validate on its own is pretty ambiguous. What if I have data that might be valid in several ways? Typically I would want multiple traits to communicate this information. A String might be valid utf-8, but also a valid phone number, but an invalid email. Having just a String, should it be valid or not?

As a general design principle (in rust and outside of it), try to move towards a pattern of parsing, not validating. Instead of having your user check is_valid at every moment, try to make types which can't even be constructed in an invalid state. I might have a PhoneNumber type which just wraps a String, but not allow you to construct a PhoneNumber without validating it first. Now operations that only work on phone numbers can safely be done on any PhoneNumber.

5 Likes

Alright

The problem with applying this idea to types like NonZero is that it’s already undefined behavior for such a value to exist. You might well find valid() returning true spuriously because the compiler is allowed to assume the value is nonzero, which leads to rewriting the body of valid into just an unconditional fn valid(&self) { true }.

So, having a validation function is only useful for types that promise never to make a validity invariant[1] for the incorrect value, but that makes some optimization impossible, so in the contexts where the type would be used in unsafe code at all, it’s not usually a desirable choice.


Also, Rust already has automatic checks for NonZero. If you compile with debug assertions (as the default Cargo profile) then e.g. NonZeroU8::new_unchecked() actually does check:

use std::num::NonZero;
fn main() {
    let _ = unsafe { NonZero::<u8>::new_unchecked(0) };
}
thread 'main' panicked at core/src/panicking.rs:221:5:
unsafe precondition(s) violated: NonZero::new_unchecked requires the argument to be non-zero

  1. This is the technical term for “it is UB to produce a value which doesn’t have this property”, as opposed to weaker invariants where breaking them might lead to UB later ↩︎

2 Likes

Such trait does have utility for zero copy conversions, and will probably eventually be part of safe transmute. However we're not there yet.