Which is better between direct const value parameter or const inside trait to generic parameter?

⚓ Rust    📅 2026-02-20    👤 surdeus    👁️ 1      

surdeus

I have two versions of the code

In the first version, I pass the numeric value directly to a const value in the struct, then use the Into trait

use std::marker::PhantomData;

trait Validator<T> {
    fn validate(val: &T) -> Result<(), &'static str>;
}

struct Validated<T, V: Validator<T>>(T, PhantomData<V>);

impl<T, V: Validator<T>> Validated<T, V> {
    fn new(val: T) -> Result<Self, &'static str> {
        V::validate(&val)?;
        Ok(Self(val, PhantomData))
    }
    
    fn get(&self) -> &T {
        &self.0
    }
    
    fn take(self) -> T {
        self.0
    }
}

struct Min<const N: i128>;
impl<T, const N: i128> Validator<T> for Min<N> 
where T: PartialOrd + Copy + Into<i128> {
    fn validate(val: &T) -> Result<(), &'static str> {
        if (*val).into() >= N {
            return Ok(());
        }
        Err("Value too small")
    }
}

struct Tes {
    val: Validated<i32, Min<10>>
}

fn main() -> Result<(), &'static str> {

    let tes = Tes {
        val: Validated::new(20)?
    };
    
    println!("{}", tes.val.take());
    
    Ok(())
}

In the second version, I define a trait containing a const variable and pass that trait into the struct's generic parameter

use std::marker::PhantomData;

trait Validator<T> {
    fn validate(val: &T) -> Result<(), &'static str>;
}

struct Validated<T, V: Validator<T>>(T, PhantomData<V>);

impl<T, V: Validator<T>> Validated<T, V> {
    fn new(val: T) -> Result<Self, &'static str> {
        V::validate(&val)?;
        Ok(Self(val, PhantomData))
    }
    
    fn get(&self) -> &T {
        &self.0
    }
    
    fn take(self) -> T {
        self.0
    }
}

trait Num<T> {
    const val: T;
}

struct Min<N>(PhantomData<N>);
impl<T, N> Validator<T> for Min<N> 
where 
    T: PartialOrd + Copy,
    N: Num<T>
{
    fn validate(val: &T) -> Result<(), &'static str> {
        if *val >= N::val {
            return Ok(());
        }
        Err("Value too small")
    }
}

struct Tes {
    val: Validated<i32, Min<Numm>>
}

struct Numm;
impl Num<i32> for Numm {
    const val: i32 = 10;
}

fn main() -> Result<(), &'static str> {

    let tes = Tes {
        val: Validated::new(20)?
    };
    
    println!("{}", tes.val.take());
   
    Ok(())
}

My goal is to centralize the minimum value, so callers don't have to specify it repeatedly to reducing the risk of inconsistency. I also want the value to be protected, only overridable via ::new() to ensure that everyone creating new value uses a consistent minimum value

Which approach is better?

2 posts - 2 participants

Read full topic

🏷️ Rust_feed