在Rust编译时部分应用?

时间:2019-03-02 14:16:14

标签: rust compile-time

我有一个带有两个参数的函数(假设两个字符串):

fn foo(x: String, y: String) -> String {
    x + y
}

我总是在编译时知道x,但是直到运行时才知道y

如何在不复制粘贴每个x的功能的情况下写出最大效率的代码?

2 个答案:

答案 0 :(得分:4)

请注意,函数foo当前不需要两个堆分配的字符串。这是另一个版本,它更具通用性和效率(尽管我将在下面介绍的YMMV):

fn foo<T>(x: T, y: &str) -> String
where
    T: Into<String>,
{
    x.into() + y
}

assert_eq!(foo("mobile ", "phones"), "mobile phones");

串联几乎总是需要在某处进行内存分配,但是这种连接可以采用堆分配的字符串以及任意字符串切片。 如果x的容量足够大,它也可以避免重新分配,尽管考虑到x是从编译时已知的字符串获得的,尽管情况不太可能。 String::insert_str将允许我们还原类型参数的位置,但是在字符串的前面插入会产生O(n)的开销。知道字符串连接的第一个操作数一个先验对于编译器可以采用的优化方式不是很有益。


让我们假设我们仍然想在编译时执行部分功能。这似乎是const generics发光的另一个用例。有了这项功能,您确实可以使用尚未实现的语法在&'static str上实现此功能的单一化:

fn foo<const X: &'static str>(y: &str) -> String {
    x.to_string() + y
}

可惜,const泛型目前正在开发中,尚未为此做好充分准备。尽管符合人体工程学,但我们可以使用基于规则的宏来复制为每个字符串文字实例化一个函数的效果:

macro_rules! define_foo {
    ($fname: ident, $x: literal) => {
        fn $fname (y: &str) -> String {
            $x.to_string() + y
        }
    }
}

使用:

define_foo!(bar, "Conan ");

assert_eq!(bar("Osíris"), "Conan Osíris");    

另请参阅:

答案 1 :(得分:1)

我每晚都能使用const函数返回闭包来完成它:

#![feature(const_fn)]

fn foo(x: String, y: String) -> String {
    x + &y
}

const fn foo_applied(x: String) -> impl Fn(String) -> String {
    move |y| foo(x.clone(), y)
}

fn main() {
    let foo_1 = foo_applied("1 ".into());
    println!("{}", foo_1("2".into()));
    let foo_2 = foo_applied("2 ".into());
    println!("{}", foo_2("1".into()));
}

Playground