Reputation: 8692
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
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());
}
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
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