我不懂Rust中的一些基础知识。我想计算一个函数sinc(x)
,其中x
是标量或切片,它会修改值。我可以为这两种类型实现方法,用x.sinc()
调用它们,但我发现它更方便(并且更容易在长公式中读取)来创建一个函数,例如sinc(&mut x)
。那么你如何正确地做到这一点?
pub trait ToSinc<T> {
fn sinc(self: &mut Self) -> &mut Self;
}
pub fn sinc<T: ToSinc<T>>(y: &mut T) -> &mut T {
y.sinc()
}
impl ToSinc<f64> for f64 {
fn sinc(self: &mut Self) -> &mut Self {
*self = // omitted
self
}
}
impl<'a> ToSinc<&'a mut [f64]> for &'a mut [f64] {
fn sinc(self: &mut Self) -> &mut Self {
for yi in (**self).iter_mut() { ... }
self
}
}
这似乎有效,但不是最后impl
昂贵的“双重间接”?我也想过做
pub trait ToSinc<T> {
fn sinc(self: Self) -> Self;
}
pub fn sinc<T: ToSinc<T>>(y: T) -> T {
y.sinc()
}
impl<'a> ToSinc<&'a mut f64> for &'a mut f64 {
fn sinc(self) -> Self {
*self = ...
self
}
}
impl<'a> ToSinc<&'a mut [f64]> for &'a mut [f64] {
fn sinc(self) -> Self {
for yi in (*self).iter_mut() { ... }
self
}
}
这也有效,不同之处在于如果x
是&mut [f64]
切片,我可以拨打sinc(x)
而不是sinc(&mut x)
。所以我觉得第二个方面的间接性较少,我觉得这很好。我在这里错了吗?
答案 0 :(得分:3)
我发现双重间接的任何差异都不太可能在这种情况下中,但你是正确的,第二个是首选。
您有ToSinc<T>
,但请勿使用T
。删除模板参数。
尽管如此,ToSinc
几乎肯定是f64
s的值:
impl ToSinc for f64 {
fn sinc(self) -> Self {
...
}
}
您可能还需要ToSinc for &mut [T] where T: ToSinc
。
你可能会说,“啊 - 其中一个是有价值的,另一个是可变的引用;是不是不一致?”
答案取决于你实际上打算使用什么特性。
sinc
- 能力类型如果你的界面代表那些你可以运行sinc
的类型,因为打算使用这种特征,目标就是编写函数
fn do_stuff<T: ToSinc>(value: T) { ... }
现在请注意界面是按值。 ToSinc
获取self
并返回Self
:这是一个值到价值的函数。实际上,即使T
被实例化为某些可变引用,例如&mut [f64]
,,该函数也无法观察任何突变到底层内存
本质上,这些函数将底层内存视为分配源,并对这些分配中保存的数据进行值转换,就像Box → Box
操作是堆内存的按值转换一样。只有调用者能够观察到内存的突变,但即使这样,将其输入视为值类型的实现也将返回一个指针,该指针阻止需要访问该内存中的数据。调用者可以像分配器一样将源数据视为不透明。
依赖于可变性的操作,比如写入缓冲区,可能不应该使用这样的接口。有时为了支持这些情况,构建变异基础和方便的按值访问器是有意义的。 ToString
是一个有趣的例子,因为它只是Display
的包装。
pub trait ToSinc: Sized {
fn sinc_in_place(&mut self);
fn sinc(mut self) -> Self {
self.sinc_in_place();
self
}
}
其中impl
主要是实施sinc_in_place
,用户倾向于sinc
。
在这种情况下,人们不关心特征是否实际上可以普遍使用,或者甚至是否一致。 sinc("foo")
可能会为我们所关心的所有人唱歌跳舞。
因此,虽然需要特质,但应尽可能将其定义为弱:
pub trait Sincable {
type Out;
fn sinc(self) -> Self::Out;
}
然后你的功能更通用:
pub fn sinc<T: Sincable>(val: T) -> T::Out {
val.sinc()
}
要实现按值函数
impl Sincable for f64 {
type Out = f64;
fn sinc(self) -> f64 {
0.4324
}
}
和by-mut-reference只是
impl<'a, T> Sincable for &'a mut [T]
where T: Sincable<Out=T> + Copy
{
type Out = ();
fn sinc(self) {
for i in self {
*i = sinc(*i);
}
}
}
因为()
是默认的空类型。这就像ad-hoc重载一样。