Частичное применение во время компиляции в Rust? - PullRequest
0 голосов
/ 02 марта 2019

У меня есть функция, которая принимает два параметра (скажем, две строки):

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

Я всегда знаю x во время компиляции, но я не знаю y до времени выполнения,

Как я могу написать это для максимальной эффективности, без копирования-вставки функции для каждого x?

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Обратите внимание, что ваша функция 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).Знание первого операнда конкатенации строк a priori не очень полезно для компилятора с точки зрения того, какие оптимизации он может использовать.


Предположим, что мы все еще хотим выполнитьчастичная функция во время компиляции.Похоже, это еще один случай, когда 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");    

См. Также:

0 голосов
/ 02 марта 2019

Я смог сделать это ночью, используя функцию 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()));
}

Детская площадка

...