Как использовать OwningRef для программного создания цепочек шумов :: NoiseFns? - PullRequest
1 голос
/ 04 февраля 2020

Кажется, ящик с шумом имеет конструкцию API, которая делает невозможным программное построение цепочек noise::NoiseFn s. Правила заимствования, похоже, ограничивают меня в использовании и создании цепочки NoiseFn s внутри одной функции, но не через границу возврата функции.

Я получил совет, что это в некоторой степени неизбежно, но что можно обойти такую ​​проблему самоссылочной конструкции, используя аренда или владение-ref ящики. Я выбрал последнее из-за того, что у него немного более простой API и примеры использования. Я потерпел неудачу в его применении, возможно, из-за дополнительного усложнения NoiseFn, являющегося признаком, и API ящика шума, являющегося инвариантом.

Я выделил некоторые части noise-rs в минимальную программу, в которой обнаружена проблема:

extern crate owning_ref;
use owning_ref::OwningRef;

// INVARIANT start, copied from noise-rs crate
pub trait NoiseFn<T> {
    fn get(&self, point: T) -> f64;
}

impl<'a, T, M: NoiseFn<T>> NoiseFn<T> for &'a M {
    #[inline]
    fn get(&self, point: T) -> f64 {
        M::get(*self, point)
    }
}

pub struct Abs<'a, T: 'a> {
    pub source: &'a dyn NoiseFn<T>,
}

impl<'a, T> Abs<'a, T> {
    pub fn new(source: &'a dyn NoiseFn<T>) -> Self {
        Self { source }
    }
}

impl<'a, T> NoiseFn<T> for Abs<'a, T> {
    fn get(&self, point: T) -> f64 {
        (self.source.get(point)).abs()
    }
}

#[derive(Clone, Copy, Debug)]
pub struct Constant {
    /// Constant value.
    pub value: f64,
}

impl Constant {
    pub fn new(value: f64) -> Self {
        Self { value }
    }
}

impl<T: Copy> NoiseFn<T> for Constant {
    fn get(&self, _point: T) -> f64 {
        self.value
    }
}
// INVARIANT end

// if we can pass Abs::new's argument to the function, we do not encounter borrowing problems
fn make_function_chain_from<'a>(from: &'a (dyn NoiseFn<f64> + 'a)) -> Box<(dyn NoiseFn<f64> + 'a)> {
    Box::new(Abs::new(from))
}

// but if we want to make a function to initialize Abs and Constant all at once, we run into
// unavoidable (?) borrowing problems
fn make_function_chain<'a>() -> Box<(dyn NoiseFn<f64> + 'a)> {
    Box::new(Abs::new(&Constant::new(-6.0))) // error
}

// attempt at using owningref as a solution
fn make_owned_function_chain<'a>() -> (
    Box<(dyn NoiseFn<f64> + 'a)>,
    OwningRef<&'a Constant, Constant>,
) {
    let constant = Constant::new(-6.0);
    let owned = OwningRef::new(&constant);
    // How do you apply owningref here to solve the borrowing problem?
    (Box::new(Abs::new(&*owned)), owned)
}

fn main() {
    // let foo = Constant::new(-6.0);
    // let bar: Abs<f64> = Abs::new(&foo);
    // let baz: Box<dyn NoiseFn<f64>> = Box::new(Abs::new(&foo));
    // let bap = make_function_chain_from(&foo);
    let bak = make_owned_function_chain();
}

Я удивлен, что API-интерфейс Rust Crate, который выглядит прямо на его лице, может поднять неизбежно проблемы заимствования у потребителя API на случай, если вы введете немного косвенности. Конечная цель моей программы - построить NoiseFn цепочки на основе пользовательского ввода, поэтому использование некоторой функции косвенности или al oop для построения цепочки неизбежно до некоторой степени.

Одно несовершенное решение, которое я подумал ограничивает глубину цепи до фиксированной величины и использует let step1: Option<Box<dyn NoiseFn>> = None; let step2: Option<Box<dyn NoiseFn>> = None; et c. и позже, используя ровно столько, сколько нужно для ввода, в пределах одной функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...