Henry Gomersall
Henry Gomersall

Reputation: 8692

How can I have a struct which is only used for its internal constant?

I need to encapsulate constants in the Rust type system. Ideally, RFC2000 would be ready, but in its absence, and since I only need a restricted set of constants, I can implement something close to what I need:

trait U32Const {
    const VAL: u32;
}

struct U32Const10;
impl U32Const for U32Const10 {
    const VAL: u32 = 10;
}

struct MyType<X: U32Const> {
    val: u32,
    template: X,
}

impl<X: U32Const> MyType<X> {
    fn do_something(&self) -> u32 {
        self.val * X::VAL
    }
}

fn main() {
    let a = MyType::<U32Const10> {
        val: 20,
        template: U32Const10 {},
    };
    println!("{}", a.do_something());
}

This prints out 200 as desired - that is, the constant value comes from the type that is passed in at instantiation inside main.

Now, this is a bit warty as it requires an instance of X to be created in the struct, which I call template, which is then unused so I get a compiler warning.

If one removes the template field, which is the ideal API, then the compiler complains about an unused parameter X on struct MyType < X: U32Const >. If I get rid of the X parameter on struct MyType, I then get an unexpected type argument on MyType in the the impl block.

Is there some way I can do what I'm trying to do that keeps the compiler happy? Effectively I want a struct which is only used for its internal const.

Upvotes: 2

Views: 521

Answers (2)

Jmb
Jmb

Reputation: 23329

You can get rid of the compiler warning by declaring template to have type PhantomData::<X>:

use std::marker::PhantomData;

trait U32Const {
    const VAL: u32;
}

struct U32Const10;
impl U32Const for U32Const10 {
    const VAL: u32 = 10;
}

struct MyType<X: U32Const> {
    val: u32,
    template: PhantomData::<X>,
}

impl<X: U32Const> MyType<X> {
    fn do_something(&self) -> u32 {
        self.val * X::VAL
    }
}

fn main() {
    let a = MyType::<U32Const10> {
        val: 20,
        template: PhantomData,
    };
    println!("{}", a.do_something());
}

playground

You still need to initialize it, but it doesn't use any memory and the compiler won't complain when you don't use it.

Upvotes: 1

&#214;mer Erden
&#214;mer Erden

Reputation: 8793

Instead of giving a phantom variable into a struct, using associated types might do the trick,

trait U32Const {
    type U32;
    const VAL: Self::U32;
}

struct U32Const10;

impl U32Const for U32Const10 {
    type U32 = u32;
    const VAL: Self::U32 = 10;
}

struct MyType<X: U32Const> {
    val: X::U32,
}

impl<X: U32Const<U32 = u32>> MyType<X> {
    fn do_something(&self) -> X::U32 {
        self.val * X::VAL
    }
}

fn main() {
    let a = MyType::<U32Const10> { val: 20 };
    println!("{}", a.do_something());
}

Playground (with multiple const implementations)

Upvotes: 4

Related Questions