在Rust中,您没有在struct
内指定可变性,但它是从变量绑定继承的。这很好,但有可能强制一个字段始终不可变,即使根是可变的吗?
类似于这种假设的语法:
struct A {
immut s: Shape, // immutable by design
bla: Bla, // this field inheriting (im)mutability
}
let mut a = make_a();
a.s = x/*...*/; // illegal
这有助于在程序中保持良好的语义限制,就像Java的final
一样(以非常有限的方式)。
另外,我们可以想象这种struct
对内部不可变数据有一些非拥有的引用,利用这种不变性......
答案 0 :(得分:5)
您可以创建一个结构,并且仅为其实现Deref特征。如果没有DerefMut特征,将无法对包含的值进行突变。
https://doc.rust-lang.org/std/ops/trait.Deref.html
这样,编译器将使该成员可用,就好像它没有被包装在另一个结构中一样,不需要任何书面的getter方法调用。
use std::ops::Deref;
/// A container for values that can only be deref'd immutably.
struct Immutable<T> {
value: T,
}
impl<T> Immutable<T> {
pub fn new(value: T) -> Self {
Immutable { value }
}
}
impl<T> Deref for Immutable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
struct Foo {
bar: Immutable<Vec<u8>>,
baz: usize,
}
impl Foo {
pub fn new(vec: Vec<u8>) -> Self {
Foo {
bar: Immutable::new(vec),
baz: 1337,
}
}
pub fn mutate(&mut self) {
self.bar.push(0); // This will cause a compiler error
}
}
|
| self.bar.push(0);
| ^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `runnable::immutable::Immutable<std::vec::Vec<u8>>`
答案 1 :(得分:4)
单个领域的不可变性是不可能的。这是古代version of Rust中的一个选项(在0.8之前考虑),但由于规则混淆了很多人,因此它被删除了。你可能会问,这有多令人困惑?想想这样:如果一个字段被声明为可变,并且struct被声明为可变,并且使用的引用是一个不可变引用(&
),则该字段为_______
。
最好的是Lily Ballard noted,您可以将Shape
字段声明为私有字段,并使用impl A {...}
制作一个getter方法。
mod inner {
pub struct A {
s: i32, // can't be seen outside of module
pub bla: i32,
}
impl A {
pub fn new() -> Self {
Self { s: 0, bla: 42 }
}
pub fn get_s(&self) -> i32 {
self.s
}
}
}
let mut a = inner::A::new();
a.s = 42; // illegal
println!("{}", a.s); // also illegal
println!("{}", a.get_s()); // could be made to serve as a read-only method
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:20:5
|
20 | a.s = 42; // illegal
| ^^^
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:21:20
|
21 | println!("{}", a.s); // also illegal
| ^^^
有一个命题可能会完全忽略可变性和不变性的概念(你不能说结构永远不会改变)。有关此更改,请参阅Niko's explanation。
答案 2 :(得分:3)
你不能强迫场上的不变性。结构如何在必要时改变自己的值?
您可以做的是将该字段设为私有,并公开getter方法以返回对它的引用(或复制/克隆该值)。
答案 3 :(得分:0)
解决方案可能是采用更通用的方法:
pub struct Immutable<T> {
value : T ,
}
impl<T> Immutable<T> {
pub fn new(value : T) -> Immutable<T> {
Immutable { value : value }
}
pub fn get( &self) -> &T { &self.value }
}
现在可以在每种情况下都将Immutable结构用于其他类型。
将其提供给模块可避免更改不可变对象的内容。仍然可以通过用新的Object覆盖持有Immutable对象本身的变量来更改它,但是您应该通过Immutable :: new语句注意到它,因此可以避免使用它。
答案 4 :(得分:0)
在许多情况下,关联常量会实现所需的行为:
struct Foo { blah: Blah }
impl Foo {
const S: Shape = Shape { x: 1, y: 1 };
}
当然,常量不能在创建时设置,它们是在编译时设置的。如果需要动态性,可以按照其他答案中的说明将字段设为私有。