#specialized #version #dispatching #specialization #proc-macro #concrete #specializations

specialized-dispatch

一个用于分发函数特定版本的库

5次发布

0.2.1 2024年4月13日
0.2.0 2024年4月12日
0.1.2 2024年4月7日
0.1.1 2024年4月7日
0.1.0 2024年4月7日

220过程宏

每月40次下载

MIT 许可证

25KB
383

specialized-dispatch

Github Repository crates.io Version docs.rs Documentation Github Actions Build

此crate提供了一个过程宏specialized_dispatch,它是一种方便的实现基于表达式类型的不同行为的便捷方式。

它通过在调用点创建不同的特化来实现,这是通过在内部使用min_specialization夜间功能来实现的。

因此,调用者需要从调用此宏的库中启用此夜间功能。

简单示例

#![feature(min_specialization)]

use specialized_dispatch::specialized_dispatch;

fn example<E>(expr: E) -> String {
    specialized_dispatch!(
        // Type of the expression -> return type.
        E -> String,
        // Defaut implementation.
        default fn <T>(_: T) => format!("default value"),
        // Specialization for concrete type u8.
        fn (v: u8) => format!("u8: {}", v),
        // Specialization for concrete type u16.
        fn (v: u16) => format!("u16: {}", v),
        // The expression for passing to above specializations.
        expr,
    )
}

fn main() {
    assert_eq!(example(1.0), "default value");
    assert_eq!(example(5u8), "u8: 5");
    assert_eq!(example(10u16), "u16: 10");
    println!("Done!");
}

example 函数大致展开为以下代码。请注意,确切的展开是内部实现细节。此示例提供以演示其工作原理。

fn example<E>(expr: E) -> String {
    trait SpecializedDispatchCall<T> {
        fn dispatch(t: T) -> String;
    }

    impl<T> SpecializedDispatchCall<T> for T {
        default fn dispatch(_: T) -> String {
            format!("default value")
        }
    }

    impl SpecializedDispatchCall<u8> for u8 {
        fn dispatch(v: u8) -> String {
            format!("u8: {}", v)
        }
    }

    impl SpecializedDispatchCall<u8> for u16 {
        fn dispatch(v: u16) -> String {
            format!("u16: {}", v)
        }
    }

    <E as SpecializedDispatchCall<E>>::dispatch(expr)
}

上述示例包含在存储库中

可以使用以下命令运行它:cargo run --example simple_example

可以使用cargo-expand检查展开的代码:cargo expand --example simple_example

特质边界

可以为默认情况提供特质边界

#![feature(min_specialization)]

use std::fmt::Display;

use specialized_dispatch::specialized_dispatch;

// The expression type must also bind to the same trait.
fn example<E: Display>(expr: E) -> String {
    specialized_dispatch!(
        E -> String,
        // Notice the trait bound.
        default fn <T: Display>(v: T) => {
            format!("default value: {}", v)
        },
        // Note that specializations also need to satisfy the same bound.
        fn (v: u8) => format!("u8: {}", v),
        fn (v: u16) => format!("u16: {}", v),
        expr,
    )
}

fn main() {
    assert_eq!(example(1.5), "default value: 1.5");
    assert_eq!(example(5u8), "u8: 5");
    assert_eq!(example(10u16), "u16: 10");
    println!("Done!");
}

同样,上述示例包含在存储库中

可以使用 cargo run --example trait_bound 来运行,或者使用 cargo-expand 来检查。

传递额外参数

可以将额外参数传递给特殊化。参数类型需要显式声明(即,它们不会像闭包那样自动捕获)。

#![feature(min_specialization)]

use std::fmt::Display;

use specialized_dispatch::specialized_dispatch;

fn example<T: Display>(expr: T, arg: &str) -> String {
    specialized_dispatch!(
        T -> String,
        default fn <T: Display>(v: T, arg: &str) => {
            format!("default value: {}, arg: {}", v, arg)
        },
        fn (v: u8, arg: &str) => format!("u8: {}, arg: {}", v, arg),
        fn (v: u16, arg: &str) => format!("u16: {}, arg: {}", v, arg),
        expr, arg,
    )
}

fn main() {
    assert_eq!(example(1.5, "I'm a"), "default value: 1.5, arg: I'm a");
    assert_eq!(example(5u8, "walnut"), "u8: 5, arg: walnut");
    assert_eq!(example(10u16, "tree"), "u16: 10, arg: tree");
    println!("Done!");
}

特殊化仍然仅基于第一个参数。

与前面的示例一样,上面的示例也被包含在存储库中。[链接](https://github.com/ozars/specialized-dispatch/blob/80bbec938876b220ebecf00695acaaa69b01618c/examples/pass_args.rs)。可以使用 cargo run --example pass_args 来运行或使用 cargo-expand 来检查。

高级 Serdelike 示例

假设你正在实现一个反序列化器。可能有一些类型与你的反序列化器配合得很好,而对于泛型反序列化器,它们可能有默认实现(或者默认为 unimplemented!)。

为了简化示例,我们将创建相关 serde 特性的简化版本。

#![feature(min_specialization)]

use specialized_dispatch::specialized_dispatch;

/// A simplified version of `serde::de::Deserializer`.
trait Deserializer<'de> {
    type Error;

    // Some generic deserializer functions...
}

/// A simplified version of `serde::de::Deserialize`.
trait Deserialize<'de>: Sized {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>;
}

/// The node type we want to deserialize.
#[derive(Debug)]
struct MyAwesomeNode;

/// Our custom deserializer.
struct MyAwesomeDeserializer;

impl MyAwesomeDeserializer {
    fn my_awesome_function(&mut self) -> MyAwesomeNode {
        MyAwesomeNode
    }
}

impl Deserializer<'_> for MyAwesomeDeserializer {
    type Error = ();
    // Implement the generic deserializer functions...
}

impl<'de> Deserialize<'de> for MyAwesomeNode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(specialized_dispatch! {
            D -> MyAwesomeNode,
            // TODO(ozars): This causes rustc ICE.
            // default fn <'de, T: Deserializer<'de>>(_deserializer: T) => {
            default fn <T>(_deserializer: T) => {
                unimplemented!()
            },
            fn (mut deserializer: MyAwesomeDeserializer) => {
                deserializer.my_awesome_function()
            },
            deserializer
        })
    }
}

fn main() {
    println!("{:?}", MyAwesomeNode::deserialize(MyAwesomeDeserializer));
}

上面的示例被包含在存储库中。[链接](https://github.com/ozars/specialized-dispatch/blob/80bbec938876b220ebecf00695acaaa69b01618c/examples/serdelike_example.rs)。可以使用 cargo run --example serdelike_example 来运行或使用 cargo-expand 来检查。

限制

需要 nightly 版本

这是因为依赖于 min_specialization 功能。

仅支持具体类型进行特殊化

只能使用具体类型进行特殊化(例如,子特质不能用于特殊化)。这是从 min_specialization 功能当前实现继承的一个现有限制。

变量不会被自动捕获

宏将其臂展开为某些方法实现。因此,它不能引用其调用作用域中的其他变量。

然而,当在宏中显式声明时,可以传递额外参数。请参阅传递额外参数部分。

与生存期的兼容性不佳

我在各个地方尝试实现生存期的支持,但遇到了一些编译器错误,在某些情况下甚至出现了内部编译器错误(ICE)。请参阅高级 Serdelike 示例中的 TODO。

这很可能是因为底层的 min_specialization 实现还不够成熟,尽管也有可能我在某个地方搞错了(如果你找到了问题,请提交一个问题:P)。

依赖项

~265–710KB
~17K SLoC