Кажется, ящик с шумом имеет конструкцию 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. и позже, используя ровно столько, сколько нужно для ввода, в пределах одной функции.