Reference 如何避免在Rust中为可变和不可变引用编写重复的访问器函数?

Reference 如何避免在Rust中为可变和不可变引用编写重复的访问器函数?,reference,rust,immutability,Reference,Rust,Immutability,有几次,我遇到了一个场景,其中可变引用和不可变引用都需要访问器方法 对于~3行,复制逻辑不是问题,但是当逻辑变得更复杂时,复制粘贴大块代码是不好的 我希望能够重复使用这两个代码 Rust是否提供了比复制粘贴代码或使用safecast更好的处理方法 e、 g: 下面是一个代码库的更详细摘录,其中一个不可变返回参数被转换为mutable,以支持函数的不可变和可变版本。这使用了一个包装指针类型(consp和MutP用于不可变和可变引用),但是函数的逻辑应该是清晰的 pub fn face_vert_

有几次,我遇到了一个场景,其中可变引用和不可变引用都需要访问器方法

对于~3行,复制逻辑不是问题,但是当逻辑变得更复杂时,复制粘贴大块代码是不好的

我希望能够重复使用这两个代码

Rust是否提供了比复制粘贴代码或使用
safe
cast更好的处理方法

e、 g:


下面是一个代码库的更详细摘录,其中一个不可变返回参数被转换为mutable,以支持函数的不可变和可变版本。这使用了一个包装指针类型(
consp
MutP
用于不可变和可变引用),但是函数的逻辑应该是清晰的

pub fn face_vert_share_loop<V, F>(f: F, v: V) -> LoopConstP
    where V: Into<VertConstP>,
          F: Into<FaceConstP>
{
    into_expand!(f, v);

    let l_first = f.l_first.as_const();
    let mut l_iter = l_first;
    loop {
        if l_iter.v == v {
            return l_iter;
        }

        l_iter = l_iter.next.as_const();
        if l_iter == l_first {
            break;
        }
    }

    return null_const();
}
pub fn face_vert_share_loop_mut(f: FaceMutP, v: VertMutP) -> LoopMutP {
    let l = face_vert_share_loop(f, v);
    return unsafe {
        // Evil! but what are the alternatives?
        // Perform an unsafe `const` to `mut` cast :(
        // While in general this should be avoided,
        // its 'OK' in this case since input is also mutable.
        l.as_mut()
    };
}
pub fn face\u vert\u share\u loop(f:f,v:v)->loopconsp
其中V:Into,
F:进入
{
扩展成(f,v);
设l_first=f.l_first.as_const();
先让mut l_iter=l_;
环路{
如果l_iter.v==v{
返回l_iter;
}
l_iter=l_iter.next.as_const();
如果l_iter==l_first{
打破
}
}
返回null_const();
}
pub fn face_vert_share_loop_mut(f:FaceMutP,v:VertMutP)->LoopMutP{
设l=face_vert_share_loop(f,v);
不安全返回{
//邪恶!但还有什么选择呢?
//执行不安全的“const”到“mut”强制转换:(
//一般来说,这是应该避免的,
//在这种情况下,它是“OK”,因为输入也是可变的。
l、 as_mut()
};
}

你真的不知道。回想一下
t
&t
&mut t
都是不同的类型。在这种情况下,你的问题与问“如何避免为
String
HashMap
编写重复的访问器函数”一样

Matthieu M有正确的术语“易变性之上的抽象”:

  • 等等等等等等

<>太长了,读不下去了,生锈可能需要用新的特征来增强,因为没有人成功,没有人能肯定100%个特征是什么。目前最好的猜测是更高类型的(HKT)。 有一些方法可以实现这一点,尽管它们并不理想:

  • 使用宏来扩展重复的代码,声明宏并在两个函数之间共享-当然,需要构造宏以使其适用于可变和不可变
  • 编写函数的不可变版本(以确保不发生任何更改),然后为可变版本编写一个包装函数,该函数对结果执行
    safe
    强制转换以使其可变
  • 这两种方法都不是很吸引人(宏过于冗长,可读性稍差,添加了一些代码膨胀),
    unsafe
    更具可读性,但最好避免,因为从不可变转换为可变并不是很好地通过代码基进行转换

    目前,就我所知,最好的选择(复制粘贴代码是不可接受的),是编写函数的不可变版本,然后使用输入和输出都可变的函数的
    mut
    版本将其包装

    这需要对函数的输出进行
    不安全的
    强制转换,因此这并不理想


    注意:让immutable函数包含代码体很重要,因为相反的函数将允许对可能是不可变输入的内容进行意外变异。

    (指向使用和的解决方案的链接)

    在这种情况下,
    &T
    &mut T
    只是两种不同的类型。在不同类型(编译时和运行时)上通用的代码通常是使用traits以Rust编写的。例如:

    struct Foo { value: i32 }
    struct Bar { foo: Foo }
    
    假设我们想为
    Bar
    Foo
    数据成员提供一个通用访问器。访问器应该同时处理
    &Bar
    &mut Bar
    两个属性,并适当地返回
    &Foo
    &mut Foo
    。因此我们编写了一个trait
    FooGetter

    trait FooGetter {
        type Output;
        fn get(self) -> Self::Output;
    }
    
    它的工作是在我们所拥有的特定类型的
    Bar
    上通用。它的
    输出
    类型将取决于
    Bar
    ,因为我们希望
    get
    有时返回
    &Foo
    ,有时返回
    &mut-Foo
    。还要注意,它消耗
    self
    类型的
    self
    。因为我们想要
    getode>要在
    &Bar
    &mut Bar
    上具有通用性,我们需要为两者实现
    FooGetter
    ,以便
    Self
    具有适当的类型:

    // FooGetter::Self == &Bar
    impl<'a> FooGetter for &'a Bar {
        type Output = &'a Foo;
        fn get(self) -> Self::Output { & self.foo }
    }
    
    // FooGetter::Self == &mut Bar
    impl<'a> FooGetter for &'a mut Bar {
        type Output = &'a mut Foo;
        fn get(mut self) -> Self::Output { &mut self.foo }
    }
    
    请注意,您还可以为
    Bar
    实现
    FooGetter
    ,这样
    get
    &T
    &mut T T
    T
    本身(通过将其移入)上是通用的。这实际上是
    .iter()
    方法在标准库中实现的方式,也是它总是做“正确的事情”的原因独立于其调用的参数的引用性。

    您可以使用:

    这将扩展到您的第一个示例。但是,通常您可能会在代码中使用各种调用的常量/可变版本。因此,下面是如何编写第二个示例的猜测(必须对命名进行一些猜测):

    使用duplicate::duplicate;
    #[重复(
    face_vert_share_loop VertConstP FaceConstP LoopConstP as_const null_const;
    [face_vert_share_loop][VertConstP][FaceConstP][LoopConstP][as_const][null_const];
    [face_vert_share_loop_mut][VertMutP][FaceMutP][LoopMutP][as_mut][null_mut];
    )]
    pub fn face_vert_share_loop(f:f,v:v)->LoopConstP
    其中V:Into,
    F:进入
    {
    扩展成(f,v);
    设l_first=f.l_first.as_const();
    设mut l_iter=l_
    
    // FooGetter::Self == &Bar
    impl<'a> FooGetter for &'a Bar {
        type Output = &'a Foo;
        fn get(self) -> Self::Output { & self.foo }
    }
    
    // FooGetter::Self == &mut Bar
    impl<'a> FooGetter for &'a mut Bar {
        type Output = &'a mut Foo;
        fn get(mut self) -> Self::Output { &mut self.foo }
    }
    
    // exemplary generic function:
    fn foo<T: FooGetter>(t: T) -> <T as FooGetter>::Output {
        t.get() 
    }
    
    fn main() {
        let x = Bar { foo: Foo {value: 2} };
        let mut y = Bar { foo: Foo {value: 2} };
    
        foo(&mut y).value = 3;
        println!("{} {}\n", foo(&x).value, foo(&mut y).value);
    }
    
    use duplicate::duplicate;
    
    impl MyStruct {
      #[duplicate(
        get_foo         self        return_type;
        [get_foo]       [&self]     [&Bar];
        [get_foo_mut]   [&mut self] [&mut Bar]
      )]
      pub fn get_foo(self) -> return_type {
        // ~20 lines of code
        // --- snip ---
        return bar;
      }
    }
    
    use duplicate::duplicate;
    #[duplicate(
      face_vert_share_loop        VertConstP    FaceConstP    LoopConstP    as_const    null_const;
      [face_vert_share_loop]      [VertConstP]  [FaceConstP]  [LoopConstP]  [as_const]  [null_const];
      [face_vert_share_loop_mut]  [VertMutP]    [FaceMutP]    [LoopMutP]    [as_mut]    [null_mut];
    )]
    pub fn face_vert_share_loop<V, F>(f: F, v: V) -> LoopConstP
        where V: Into<VertConstP>,
              F: Into<FaceConstP>
    {
        into_expand!(f, v);
    
        let l_first = f.l_first.as_const();
        let mut l_iter = l_first;
        loop {
            if l_iter.v == v {
                return l_iter;
            }
    
            l_iter = l_iter.next.as_const();
            if l_iter == l_first {
                break;
            }
        }
    
        return null_const();
    }
    
    pub fn face_vert_share_loop<V, F>(f: F, v: V) -> LoopConstP
    where
        V: Into<VertConstP>,
        F: Into<FaceConstP>,
    {
        into_expand!(f, v);
        let l_first = f.l_first.as_const();
        let mut l_iter = l_first;
        loop {
            if l_iter.v == v {
                return l_iter;
            }
            l_iter = l_iter.next.as_const();
            if l_iter == l_first {
                break;
            }
        }
        return null_const();
    }
    pub fn face_vert_share_loop_mut<V, F>(f: F, v: V) -> LoopMutP
    where
        V: Into<VertMutP>,
        F: Into<FaceMutP>,
    {
        into_expand!(f, v);
        let l_first = f.l_first.as_mut();
        let mut l_iter = l_first;
        loop {
            if l_iter.v == v {
                return l_iter;
            }
            l_iter = l_iter.next.as_mut();
            if l_iter == l_first {
                break;
            }
        }
        return null_mut();
    }