Trame2771
@Trame2771

Как в rust конвертировать place expression в value expressions?

У замыканий одна неприятная очень особенность – если в их теле использовать идентификатор из окружения, то оно будет его захватывать как lvalue неявно, потому что я не указывал в || мой идентификатор
fn main() {
    let mut funcs: [fn() -> u8; 3] = [|| -> u8 {0}; 3]; // инициализация "нулями"
    
    for i in std::ops::RangeInclusive::<u8>::new(1, 3) {
        funcs[TryInto::<usize>::try_into(i).unwrap()] = || -> u8 {to_rvalue("i")}; // может есть какой-нибудь macro или функция?
    }
    for i in std::ops::RangeInclusive::<usize>::new(1, 3) {
        println!("{}", funcs[i - 1]());
    }
    
}

Или может я дурак и не знаю что такое замыкания? Может так сделано специально чтоб все вариации замыканий были известны в сompile-time?
  • Вопрос задан
  • 91 просмотр
Решения вопроса 1
bingo347
@bingo347
Crazy on performance...
Проблема с типами тут.
Тип fn() -> u8 - это указатель на функцию. В него можно записать или обычную функцию с подходящей сигнатурой или замыкание, которое ничего не замыкает:
fn some_func() -> u8 {
    0
}

let f: fn() -> u8 = some_func;
let f: fn() -> u8 = || 0;

Если же замыкание что-то замыкает (захватывает переменные из окружения), то это уже анонимная структура, хранящая в себе все захваченные значения (или ссылки на них), и у этой структуры просто перегружен оператор круглые скобки через трейты Fn/FnMut/FnOnce. Притом, так как компилятор генерирует для каждого замыкания в коде свою анонимную структуру, у каждого замыкания будет свой уникальный тип.

Если массив замыканий полностью формируется в цикле, то замыкания будут одного типа и все скомпилируется:
fn main() {
    let mut funcs = Vec::with_capacity(3);
    for i in 1u8..=3 {
        funcs.push(move || i);
    }
    
    for i in 0..3 {
        println!("{}", funcs[i]());
    }
}


P.S. для рэнжей есть литералы, не нужно их создавать через std::ops::RangeInclusive::<u8>::new
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
vabka
@vabka Куратор тега Rust
Попробуй так
fn main() {
    let funcs = {
        // Можно использовать не FnOnce, а что-то другое. Например Fn или FnMut. Но в любом случае придётся использовать dyn, тк наша лямбда берёт что-то из окружающего контекста.

        let mut funcs: Vec<Box<dyn FnOnce() -> usize>> = Vec::with_capacity(3);

        for i in 0..3 {
            let i_copy = i.clone(); // вообще clone() тут не нужен, тк usize реализует трейт Copy. Оставлено для примера
            funcs.push(Box::new(move || i_copy));
        }
        funcs
    };
    
    for func in funcs {
        let result = func();
        println!("{result}");
    }
}

https://play.rust-lang.org/?version=stable&mode=de...

Другой вариант - делать не массив лямбд, а сделать структуру с методом и сделать массив структур.
В твоём случае это будет эффективнее:
#[derive(Copy, Clone, Debug)]
struct Something(usize);
impl Something {
  fn value(&self) -> usize {
    self.0
  }
}

fn main() {
    let items = {
        let mut items = Vec::with_capacity(3);

        for i in 0..3 {
            items.push(Something(i));
        }
        items
    };
    
    for item in items {
        let result = item.value();
        println!("{result}");
    }
}

https://play.rust-lang.org/?version=stable&mode=de...
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы