我有两个函数,我希望在方法链中使用它们。它们基本上都做同样的事情,除了其中一个覆盖自己而另一个返回克隆。我来自Ruby,我以前只是在破坏性方法中调用self.dup.mutable_method
。
我相信我在Rust中有一个解决方案,但我不确定它是否在某处有额外的分配,而且我担心它会消耗掉自己。这是音频DSP代码,所以我想确保在mutable方法中没有分配。 (我在Rust工作了三天,所以非常普遍的特质就是一个人。)
impl Filter for DVec<f64> {
fn preemphasis_mut<'a>(&'a mut self, freq: f64, sample_rate: f64) -> &'a mut DVec<f64> {
let filter = (-2.0 * PI * freq / sample_rate).exp();
for i in (1..self.len()).rev() {
self[i] -= self[i-1] * filter;
};
self
}
fn preemphasis(&self, freq: f64, sample_rate: f64) -> DVec<f64> {
let mut new = self.clone();
new.preemphasis_mut(freq, sample_rate);
new
}
}
// Ideal code:
let mut sample: DVec<f64> = method_that_loads_sample();
let copy_of_sample = sample.preemphasis(75.0, 44100.0); // this mutates and copies, with one allocation
sample.preemphasis_mut(75.0, 44100.0); // this mutates in-place, with no allocations
copy_of_sample.preemphasis_mut(75.0, 44100.0)
.preemphasis_mut(150.0, 44100.0); // this mutates twice in a row, with no allocations
答案 0 :(得分:1)
在自我突变方面,我没有看到任何库遵循类似于Ruby的foo
和foo!
方法对的任何模式。我认为这主要是因为Rust将可变性放在前后中心,因此不小心&&#34;意外地&#34;变异的东西。为此,我可能会删除您的一个方法,并允许用户决定什么时候应该变异:
use std::f64::consts::PI;
trait Filter {
fn preemphasis<'a>(&'a mut self, freq: f64, sample_rate: f64) -> &'a mut Self;
}
impl Filter for Vec<f64> {
fn preemphasis<'a>(&'a mut self, freq: f64, sample_rate: f64) -> &'a mut Self {
let filter = (-2.0 * PI * freq / sample_rate).exp();
for i in (1..self.len()).rev() {
self[i] -= self[i-1] * filter;
};
self
}
}
fn main() {
let mut sample = vec![1.0, 2.0];
// this copies then mutates, with one allocation
let mut copy_of_sample = sample.clone();
copy_of_sample
.preemphasis(75.0, 44100.0);
// this mutates in-place, with no allocations
sample
.preemphasis(75.0, 44100.0);
// this mutates twice in a row, with no allocations
copy_of_sample
.preemphasis(75.0, 44100.0)
.preemphasis(150.0, 44100.0);
}
我认为这里的关键是代码的调用者可以很容易地看到什么时候会发生变异(因为&mut
对self
的引用)。 调用者还可以确定clone
发生的时间和地点。