Home > OS >  Prevent compiling in Rust if a struct's associated constant fails a check
Prevent compiling in Rust if a struct's associated constant fails a check

Time:12-05

I have a generic function serialize that serializes a struct implementing serde::Serialize together with a custom trait MaxSize to a byte buffer. The MaxSize trait is implemented with a derive macro, and has an associated constant MAX_SIZE that gets filled in with the maximum possible size the struct serializes to.

I want to get an error at compile time if serialize is called on a struct whose MAX_SIZE is larger than some constant, MAX_ALLOWED_SIZE. (The idea is to prevent creating structs that could exceed the max UDP datagram size when serialized.) How do I do this? I've tried the following (omitting the derive macro here and instead just filling in MAX_SIZE for the example struct manually for simplicity) (playground link):

const MAX_ALLOWED_SIZE: usize = 100;

trait MaxSize {
    const MAX_SIZE: usize;
}

#[derive(serde::Serialize)]
struct ExampleStruct {
    a: f64,
}

// (This would actually be implemented using a derive macro.)
impl MaxSize for ExampleStruct {
    const MAX_SIZE: usize = 7;
}

fn serialize<T: serde::Serialize   MaxSize>(data: &T, buf: &mut [u8]) {
    const _: () = assert!(T::MAX_SIZE < MAX_ALLOWED_SIZE);
    // <code to actually serialize>
}

fn main() {
    serialize(&ExampleStruct {a: 6.}, &mut [0u8; MAX_ALLOWED_SIZE]);
}

I get the following error:

error[E0401]: can't use generic parameters from outer function
  --> src/main.rs:18:27
   |
17 | fn serialize<T: serde::Serialize   MaxSize>(data: &T, buf: &mut [u8]) {
   |              - type parameter from outer function
18 |     const _: () = assert!(T::MAX_SIZE < MAX_ALLOWED_SIZE);
   |                           ^^^^^^^^^^^ use of generic parameter from outer function

CodePudding user response:

On stable:

struct Const<T>(std::marker::PhantomData<T>);
impl<T: MaxSize> Const<T> {
    const C: () = assert!(T::MAX_SIZE < MAX_ALLOWED_SIZE);
}
fn serialize<T: serde::Serialize   MaxSize>(data: &T, buf: &mut [u8]) {
    [Const::<T>::C; 1];
    // <code to actually serialize>
}

On nightly:

#![feature(inline_const)]

fn serialize<T: serde::Serialize   MaxSize>(data: &T, buf: &mut [u8]) {
    const { assert!(T::MAX_SIZE < MAX_ALLOWED_SIZE) };
    // <code to actually serialize>
}

Note that both generate a post-monomorphization error, meaning they'll succeed cargo check but fail cargo build, and so are discouraged to use (but can be used nonetheless).

  • Related