Что такое Rust-эквивалент хранения C ++ std :: function в векторе? - PullRequest
2 голосов
/ 24 сентября 2019

Это эквивалент C ++ того, чего я пытаюсь достичь:

std::vector<std::function<int(int)>> funcs;
funcs.emplace_back([](int n) -> int { return n + 1; });
int result = funcs[0](33);

Как мне написать приведенный выше код на Rust?

1 Ответ

4 голосов
/ 24 сентября 2019

Если вы не планируете перемещать функцию в каком-либо конкретном месте, вы можете позволить выводу типа сделать работу за вас внутри блока кода и буквально определить ваше закрытие, как будто оно является нормальной переменной (на практике это - этореализует либо Fn, либо FnMut):

let my_lambda = |n| n+1;
println!("{}", my_lambda(33));

Playground

Если вы собираетесь вывести эту лямбду из стека, вам нужно будетэто:

let my_lambda: Box<dyn Fn(u32) -> u32> = Box::new(|n| n + 1);
println!("{}", my_lambda(33));

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

Принцип здесь тот же, единственное реальное отличие состоит в том, что лямбда теперь в куче.

Хранение их в Vec затем становится относительно простым, поскольку к настоящему моменту мы доказали, что это «нормальные» типы.Ничто не мешает вам создать вектор замыканий, но вам нужно их пометить (Vec требует, чтобы каждый элемент был Sized, и нет способа нацелить два разных замыкания по сигнатуре), и они должны иметьта же подпись:

let my_vector: Vec<Box<dyn Fn(u16) -> u16>> = vec![
    Box::new(|i| i + 1),
    Box::new(|i| i - 1),
];
println!("{}", my_vector[0](33))

Если они имеют разрозненные подписи, вам нужно написать свою собственную структуру, которая будет содержать их, и поместить их в подпись, и это не простая задача.


Тестовый корпус:

  • C ++:

    #include <vector>
    #include <iostream>
    #include <functional>
    int main(int argc, char **argv) {
      std::vector<std::function<int(int)>> funcs;
      for (int i = 0; i < 10000000; i++) {
        funcs.push_back([&] (int n) { return n + 1; });
        int result = funcs[i](33);
      }
    }
    
  • Rust:

    fn main() {
       let mut lambdas:Vec<Box<Fn(u32) -> u32>> = vec![];
       for i in 0..10000000 {
         lambdas.push(Box::new(|i| i+1));
         lambdas[i](3);
       }
    }
    

Варианты компиляции:

  • G ++: -O3 --std=c++0x
  • Груз: --release

Результаты:

  • C ++ пиковый размер кучи:

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
     68    383,911,899      268,512,208      268,508,160         4,048            0
     69    555,878,425      805,387,160      805,379,072         8,088            0
     70    765,593,697      805,387,160      805,379,072         8,088            0
    
  • Размер пиковой кучи ржавчины:

    --------------------------------------------------------------------------------
      n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
    --------------------------------------------------------------------------------
     46    210,486,321      268,440,920      268,436,765         4,155            0
     47    210,486,371      268,440,976      268,436,805         4,171            0
     48    210,486,496      268,441,064      268,436,885         4,179            0
    

Выводы:

Оболочки
  • C ++ std::function размещаются в куче.Сама необработанная лямбда, которую она содержит, распределяется по стеку, и это поведение соответствует Rust
  • Rust лучше при оптимизации памяти, потребляя треть размера кучи C ++
  • Rust такжебыстрее с точки зрения сырого времени
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...