在 Rust 的编译时部分应用?
Partial application at compile-time in Rust?
我有一个函数接受两个参数(比方说两个字符串):
fn foo(x: String, y: String) -> String {
x + y
}
我总是在编译时知道 x
,但直到 运行 时我才知道 y
。
如何在不为每个 x
复制粘贴函数的情况下以最大效率编写此代码?
我能够在每晚使用返回闭包的 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()));
}
请注意,您的函数 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 would shine. With this feature, one could indeed monomorphize this function over a &'static str
. As of nightly-2021-09-06
, being able to use a &'static str
as a const parameter is still unstable, but the code below 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");
另请参阅:
我有一个函数接受两个参数(比方说两个字符串):
fn foo(x: String, y: String) -> String {
x + y
}
我总是在编译时知道 x
,但直到 运行 时我才知道 y
。
如何在不为每个 x
复制粘贴函数的情况下以最大效率编写此代码?
我能够在每晚使用返回闭包的 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()));
}
请注意,您的函数 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 would shine. With this feature, one could indeed monomorphize this function over a &'static str
. As of nightly-2021-09-06
, being able to use a &'static str
as a const parameter is still unstable, but the code below 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");
另请参阅: