Почему генератор, работавший в ночной Rust 1.29, выдает ошибку в ночной 1.34.0? - PullRequest
1 голос
/ 19 июня 2019

Я использую генератор ржавчины, и он работал нормально с ночной 1.29.0:

#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};

fn main() {
    let mut generator: Box<dyn Generator<Yield = u64, Return = &str>> = Box::new(move || {
        yield 1;
        return "foo";
    });

    unsafe {
        match generator.resume() {
            GeneratorState::Yielded(1) => {
                println!("Yielded");
            }
            _ => panic!("unexpected return from resume"),
        }
        match generator.resume() {
            GeneratorState::Complete("foo") => {
                println!("Completed");
            }
            _ => panic!("unexpected return from resume"),
        }
    }
}

Однако, ночная версия 1.34.0 требует, чтобы Generator был обернут в Pin, нокогда я обертываю Generator в Pin, код больше не работает.

#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};
use std::pin::Pin;

fn main() {
    let mut generator: Box<dyn Generator<Yield = u64, Return = &str>> = Box::new(move || {
        yield 1;
        return "foo";
    });

    match Pin::new(&mut generator).resume() {
        GeneratorState::Yielded(1) => {
            println!("Yielded");
        }
        _ => panic!("unexpected return from resume"),
    }
    match Pin::new(&mut generator).resume() {
        GeneratorState::Complete("foo") => {
            println!("Completed");
        }
        _ => panic!("unexpected return from resume"),
    }
}

И код выдает следующую ошибку.

error[E0599]: no method named `resume` found for type `std::pin::Pin<&mut std::boxed::Box<dyn std::ops::Generator<Yield = u64, Return = &str>>>` in the current scope
  --> src/main.rs:12:36
   |
12 |     match Pin::new(&mut generator).resume() {
   |                                    ^^^^^^
   |
   = note: the method `resume` exists but the following trait bounds were not satisfied:
           `std::boxed::Box<dyn std::ops::Generator<Yield = u64, Return = &str>> : std::ops::Generator`

Когда я удаляю типгенератора, он работает нормально:

let mut generator = Box::new(move || {
        yield 1;
        return "foo"
});

У меня есть несколько вопросов:

  • Почему перенос Generator в Pin дает ошибку в последней версии?
  • Какая польза от использования Pin для генератора (так как он предотвращает перемещение значения указателя), я не понимаю связи между Pin и Generator?
  • Почему код работает, когда мы удаляем тип для переменной generator?
  • Почему метод resume больше не unsafe?

1 Ответ

4 голосов
/ 19 июня 2019

Почему код генератора, работавший в ночной Rust 1.29, выдает ошибку в Nightly 1.34.0?

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

См. также:

Почему перенос Generator в Pin приводит к ошибке в последней версии?

Как говорится в сообщении об ошибке, Box<dyn Generator<Yield = u64, Return = &str>> не реализует Generator.

См. Также:

Какая пользаиспользования Pin для генератора?

Почему метод resume больше не небезопасен?

Метод resume был небезопасным, поскольку вы могли бы ввести небезопасную память, еслиу генератора была переменная с собственной ссылкой, и он был перемещен между двумя вызовами в resume.Pin предотвращает движение, тем самым устраняя небезопасность.Это классический пример оборачивания небезопасной логики в API, который предоставляет только безопасный интерфейс.

См. Также:

Почему код работает, когда мы удаляемтип переменной генератора?

Потому что это уже не объект-черта (Box<dyn Generator>), а просто генератор в штучной упаковке.

См. также:


Я бы написал это как

#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};

fn main() {
    let mut generator = Box::pin(move || {
        yield 1;
        "foo"
    });

    match generator.as_mut().resume() {
        GeneratorState::Yielded(1) => println!("Yielded"),
        _ => panic!("unexpected return from resume"),
    }
    match generator.as_mut().resume() {
        GeneratorState::Complete("foo") => println!("Completed"),
        _ => panic!("unexpected return from resume"),
    }
}
...