1个不稳定版本

0.1.0 2020年1月12日

#19 in #specialization

Download history 14/week @ 2024-03-31

1,963 每月下载次数

BSD-2-Clause

6KB
161

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);

作为输入的表达式

不仅仅是变量名,完整的表达式也可以作为输入。但是,如果您想从match主体中引用它们,您需要将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);

依赖项

~1.5MB
~35K SLoC