I want to write some code like below:
use crate::{Ty1, Ty2};
struct Test<A, B> {
..
}
/// Match when A = Ty1 and B = Ty2
impl Test<Ty1, Ty2> {
fn test() {
..
}
}
/// Match all other cases
impl<?, ?> Test<?, ?> {
fn test() {
..
}
}
Of course it can be possible to implement all 4 cases manually, but I don't want to do. As far as I know, Rust doesn't support C -like specialization. So, how can I achieve it?
CodePudding user response:
As you correctly said, you can do it with unstable specialization
feature.
The only option for stable is not to use generics here at all:
enum Ty {
Ty1(Ty1),
Ty2(Ty2),
}
struct Test {
v1: Ty,
v2: Ty,
}
impl Test {
fn test(&self) {
if let Ty1(v1) = self.v1 {
if let Ty2(v2) = self.v2 {
// specific logic
return;
}
}
// default logic
}
}
CodePudding user response:
I tried to implement this by really tricky and limited way.. If somebody have any better idea, please suggest yours!
use std::marker::PhantomData;
/// Boolean trait for types.
trait Bool {}
struct Ty1 {}
struct NTy1 {}
impl Bool for Ty1 {}
impl Bool for NTy1 {}
struct Ty2 {}
struct NTy2 {}
impl Bool for Ty2 {}
impl Bool for NTy2 {}
struct True {}
struct False {}
/// And operator
struct And<A: Bool, B: Bool> {
v: PhantomData<(A, B)>,
}
trait BoolOp {
type Out;
}
/// Out is True when provided types are Ty1 and Ty2
impl BoolOp for And<Ty1, Ty2> {
type Out = True;
}
/// Otherwise, the Out type is False
impl BoolOp for And<Ty1, NTy2> {
type Out = False;
}
impl BoolOp for And<NTy1, Ty2> {
type Out = False;
}
impl BoolOp for And<NTy1, NTy2> {
type Out = False;
}
/// Helper trait for Test structure.
/// It has *real* implementation for *test* function.
trait TestHelperTrait {
type ReturnType;
fn test() -> Self::ReturnType;
}
/// C is the result of And<A, B>.
struct TestHelper<A, B, C> {
x: PhantomData<(A, B, C)>,
}
/// When A = Ty1, B = Ty2, return i32
impl<A, B> TestHelperTrait for TestHelper<A, B, True> {
type ReturnType = i32;
fn test() -> Self::ReturnType {
3
}
}
/// Otherwise, return &str
impl<A, B> TestHelperTrait for TestHelper<A, B, False> {
type ReturnType = &'static str;
fn test() -> Self::ReturnType {
"false"
}
}
/// *Real* Test structure
struct Test<A, B> {
x: PhantomData<(A, B)>,
}
impl<A: Bool, B: Bool> Test<A, B> {
/// Exposed interface
pub fn run(
self,
) -> <TestHelper<A, B, <And<A, B> as BoolOp>::Out> as TestHelperTrait>::ReturnType
where
And<A, B>: BoolOp,
TestHelper<A, B, <And<A, B> as BoolOp>::Out>: TestHelperTrait,
{
// Call helper's test function
<TestHelper<A, B, <And<A, B> as BoolOp>::Out> as TestHelperTrait>::test()
}
}
fn main() {
let t1 = Test::<Ty1, Ty2> { x: PhantomData };
let v1 = t1.run(); // i32
let t2 = Test::<NTy1, Ty2> { x: PhantomData };
let v2 = t2.run(); // &str
let t3 = Test::<Ty1, NTy2> { x: PhantomData };
let v3 = t3.run(); // &str
let t4 = Test::<NTy1, NTy2> { x: PhantomData };
let v4 = t4.run(); // &str
}