我尝试使用返回某些数据的方法来定义特征,但impl
不能更改此数据,即它只能设置一次并在生命周期内保留该值impl
的。{有什么方法可以确保吗?
以下是我如何在C#中完成此操作,以供参考:
public abstract class Foo
{
private readonly uint number;
public Foo(uint number) { this.number = numbers; }
public uint GetNumber() { return number; }
}
答案 0 :(得分:2)
您的问题的简短回答是否。没有办法以类似于C#方法的方式实现这一点。幸运的是,Rust提供了比C#更好的可控性控制。
了解Rust中不变性的工作原理以及它与C#和Java等语言的区别非常重要。
C#中的不变性
class Foo {
readonly Bar bar = new Bar();
uint lives;
}
有些注意事项:
bar
的引用是不可变的,bar
引用的值仍然是可变的。Rust的不变性
struct Foo {
bar: Bar,
lives: u32
}
首先要注意的是结构定义没有说明其字段的不变性。这是因为没有像生锈中的场级变异那样的东西。 Rust中的可变性在绑定上定义为值:
// Declare an immutable binding to a Foo
let foo = Foo { bar: Bar::new(), lives: 10 };
// Attempting to mutate the value that foo points to is a compile error
foo.lives = 5; // compile error!
foo.bar.baz = 6; // Also a compile error, foo is deeply immutable
// We can redefine the binding to be mutable
let foo = mut foo; // foo is now mutable!
foo.lives = 5; // mutating foo here would be valid
foo.bar.baz = 6; // this is also valid, foo is deeply mutable
正如您所看到的,Rust中的可变性比C#更简单,更简洁:它与一个值的绑定决定了它是否可变,并且它是深度可变的或者非常不可改变的 * 。
尽管如此,让我们尝试在Rust中为您的问题建模。
首先,我们使用等效的GetNumber()
方法定义特征:
trait Bar {
fn number(&self) -> u32;
}
由于number()
对self
采用不可变绑定,因此任何实现Bar
的类型都无法通过调用number()
来改变自身:
struct Foo {
number: u32,
oranges: u32
}
impl Bar for Foo {
fn number(&self) -> u32 {
self.number += 1; // Compile error. We have an immutable binding to self
self.number
}
}
正如您所看到的,控制Rust中的可变性就是控制绑定的定义方式。
让我们为我们的特征引入一种方法,该方法定义与self
的可变绑定,并在Foo
上更新我们的实现:
trait Bar {
fn number(&self) -> u32;
fn inc_oranges(&mut self);
}
impl Bar for Foo {
fn number(&self) -> u32 {
self.number
}
fn inc_oranges(&mut self) {
// We have a mutable reference to self. We can mutate any part of self:
self.oranges += 1;
self.number += 1; // We can *also* mutate number
}
}
在这里你可能会开始支持C#方法:在C#中,你可以将number
声明为只读字段,同时让oranges
保持可变,但是在Rust中,如果一个特征声明了一个可变的绑定到self
,self
的任何部分都可以变异。幸运的是,有一种解决方法。
* 内部可变性
Rust通过cell模块提供了一种改变值的方法,这些值是不可变绑定的一部分。总而言之,这些类型允许突变,同时仍然适应使用不可变绑定所提供的保证。它通过将原本可能的编译时(零成本)检查移动到运行时检查来完成此操作。
现在让我们把它们放在一起:
struct Foo {
number: u32,
orange: Cell(u32) // allow mutation via an immutable binding
}
trait Bar {
fn number(&self) -> u32;
fn inc_oranges(&self); // self is now an immutable binding
}
impl Bar for Foo {
fn number(&self) -> u32 {
self.number
}
fn inc_oranges(&self) {
// We can mutate oranges via cell functions even though self is immutable
let cur_oranges = self.oranges.get();
self.oranges.set(cur_oranges + 1);
self.number += 1; // This would be a compile error
}
}
总之,我们可以通过以下方式有效地实现与您的C#示例等效:
self
的不可变绑定尽管如此,以这种方式对所有类型进行建模并不是惯用的或高效的。更重要的是知道何时何地允许变异,而不是通过细胞类型微观管理特定领域的变异。