#specialization #generics #autoref #specialisation #autoderef

no-std spez

宏,用于针对表达式的类型进行特殊化

3个版本

0.1.2 2023年4月26日
0.1.1 2020年1月19日
0.1.0 2020年1月12日

#148 in 过程宏

Download history 2887/week @ 2024-03-14 2877/week @ 2024-03-21 2691/week @ 2024-03-28 2582/week @ 2024-04-04 3019/week @ 2024-04-11 2707/week @ 2024-04-18 2783/week @ 2024-04-25 3900/week @ 2024-05-02 3063/week @ 2024-05-09 3834/week @ 2024-05-16 3495/week @ 2024-05-23 3254/week @ 2024-05-30 3914/week @ 2024-06-06 4291/week @ 2024-06-13 5654/week @ 2024-06-20 5183/week @ 2024-06-27

19,670次每月下载
2个crates中使用 (通过 autometrics)

BSD-2-Clause

17KB
162

spez

宏,用于对表达式的类型进行特殊化。

此crate实现了自动(去)引用特殊化:在稳定版Rust中非泛型上下文中进行特殊化的技巧。

有关此技术的详细信息,请参阅

它可以做什么,不能做什么

auto(de)ref技术——因此这个宏——在泛型函数中无用,因为Rust根据泛型上下文定义的界限来解析特殊化,而不是根据实例化的实际类型。(请参见下面的示例以演示这一点。)

在非泛型上下文中,它也基本上无用,因为您可能已经知道所有变量的确切类型。

唯一使使用它有意义的地方是在需要根据传入值的类型具有不同行为的宏的实现中。例如,一个打印值的Debug输出的宏,但如果它没有实现Debug则回退到默认值。(请参见下面的示例以演示这一点。)

如何使用它

宏的基本语法是

spez! {
    for <expression>;
    match <type> { <body> }
    [match <type> { <body> }]
    [...]
}

下面的示例显示了更多细节。

简单特殊化

在最简单的情况下,您使用此宏来匹配特定类型

let x = 0;
spez! {
    for x;
    match i32 {
        println!("x is a 32-bit integer!");
    }
    match &str {
        println!("x is a string slice!");
        assert!(false);
    }
}

返回类型

可以返回匹配的值,但必须为每个match显式指定。它们不需要对每个match都相同。

let x = 0;
let result = spez! {
    for x;
    match i32 -> &'static str {
        "x is a 32-bit integer!"
    }
    match &str -> i32 {
        123
    }
};
assert_eq!(result, "x is a 32-bit integer!");

泛型匹配

通配符匹配也是可能的。可以在 match 上定义通用变量,并在类型后面添加一个 where 子句。

匹配尝试的顺序。第一个匹配优先于后面的匹配,即使后面的匹配是完美匹配。

let x = 123i32;
let result = spez! {
    for x;
    match<T> T where i8: From<T> -> i32 {
        0
    }
    match<T: std::fmt::Debug> T -> i32 {
        1
    }
    match i32 -> i32 {
        2
    }
};
assert_eq!(result, 1);

消耗输入

输入(在 for 之后)被消耗并可供 match 主体使用。

(如果你不想消耗输入,可以取一个引用,并在你要匹配的类型前添加一个 &。)

let x = Box::new(123);
let result = spez! {
    for x;
    match<T: Deref<Target = i32>> T -> i32 {
        *x
    }
    match i32 -> i32 {
        x
    }
};
assert_eq!(result, 123);

表达式作为输入

不仅可以提供变量名,还可以提供完整表达式作为输入。然而,如果你想从匹配主体中引用它们,你需要添加 name = 来为输入命名。

let result = spez! {
    for 1 + 1;
    match i32 -> i32 { 0 }
    match i64 -> i32 { 1 }
};
assert_eq!(result, 0);
let result = spez! {
    for x = 1 + 1;
    match i32 -> i32 { x }
    match i64 -> i32 { 1 }
};
assert_eq!(result, 2);

捕获变量

不幸的是,你不能在 spez! {} 宏的作用域中引用变量

let a = 1;
let result = spez! {
    for x = 1;
    match i32 {
        println!("{}", a); // ERROR
    }
};

在通用函数中

如上所述,在通用上下文中,该宏没有太大的作用,因为特殊化是根据边界而不是通用函数实例化中的实际类型来解决的

fn f<T: Debug>(v: T) -> &'static str {
    spez! {
        for v;
        match i32 -> &'static str {
            ":)"
        }
        match<T: Debug> T -> &'static str {
            ":("
        }
        match<T> T -> &'static str {
            ":(("
        }
    }
}
assert_eq!(f(0i32), ":(");

在宏中

这是一个打印值的 Debug 输出的宏示例,但如果没有实现 Debug,则回退到 "<object of type ...>"

macro_rules! debug {
    ($e:expr) => {
        spez! {
            for x = $e;
            match<T: Debug> T {
                println!("{:?}", x);
            }
            match<T> T {
                println!("<object of type {}>", std::any::type_name::<T>());
            }
        }
    }
}
debug!(123);
debug!(NoDebugType);

依赖关系

~285–740KB
~18K SLoC