【问题标题】:Partial application at compile-time in Rust?Rust 编译时的部分应用程序?
【发布时间】:2019-07-24 08:39:02
【问题描述】:

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

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

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

如何在不为每个x 复制粘贴函数的情况下以最大效率编写此代码?

【问题讨论】:

  • 除了将xy 的类型更改为&str 并避免String 的无用构造之外,我不确定这里在编译时有很多需要优化的地方.
  • 所以你想要x的每个版本的你的版本的功能?我怀疑这就是我所说的部分应用。
  • 您确定这是您代码中的性能瓶颈吗?如果是的话,我会非常惊讶。无论如何,如果确实如此,我建议将foo() 标记为#[inline(always)] 并使用正常的闭包。即使没有该属性,编译器也可能会内联该函数,因此无需担心,但如果该属性让您放心,那么请继续添加它。 :)
  • 我怎样才能写出最高效率——为什么你认为你写的不是最高效率?
  • 嗯,我不知道,这就是我问的原因。

标签: rust compile-time


【解决方案1】:

请注意,您的函数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 会发光的另一个用例。有了这个特性,人们确实可以在&amp;'static str 上单形化这个函数。截至nightly-2021-09-06,能够使用&amp;'static str作为const参数仍然不稳定,但compiles and works as intended下面的代码。

#![feature(adt_const_params)]

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

let s = "string".to_string();
println!("{}", foo::<"I am ">(&s));

唉,应用于字符串切片的 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");    

另见:

【讨论】:

    【解决方案2】:

    我可以在夜间使用返回闭包的 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 2019-11-24
      • 1970-01-01
      • 2013-05-16
      相关资源
      最近更新 更多