How can I force a struct's field to always be immutable in Rust? - immutability

In Rust, you don't specify mutability inside a struct, but it is inherited from the variable binding. That's great, but is it possible to force a field to be always immutable, even when the root is mutable?
Something like this hypothetical syntax:
struct A {
immut s: Shape, // immutable by design
bla: Bla, // this field inheriting (im)mutability
}
let mut a = make_a();
a.s = x/*...*/; // illegal
This would help to maintain nice semantic restrictions in a program, just like Java's final does (in a very limited way).
Also, we could imagine this kind of struct having some non-owning references to internal immutable data, taking advantage of this immutability...

It's impossible to have immutability of a single field. That was an option in an ancient version of Rust (think before 0.8), but it was dropped because the rules confused a LOT of people. How was it confusing, you might ask? Think about it like this: if a field is declared mutable and struct is declared mutable and the reference used was an immutable reference (&) then the field is _______.
The best, as Lily Ballard noted, is that you can declare your Shape field as private and make a getter method using impl A {...}.
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
| ^^^
There is proposition that might drop notions of mutability and immutability completely (you can't say a struct never changes). See Niko's explanation for that change.

You can create a struct and only implement the Deref trait for it. Without the DerefMut trait it won't be possible for contained values to be mutated.
https://doc.rust-lang.org/std/ops/trait.Deref.html
This way the compiler will make the member usable as if it's not wrapped in another struct, no need for any written getter method call.
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>>`

You can't force immutability on a field. How would the struct mutate its own value when necessary?
What you can do is make the field private and expose a getter method to return a reference to it (or to copy/clone the value).

A Solution could be to have a more general approach:
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 }
}
Now it is possible to use the Immutable struct in every case for every other type.
Given this into a module avoids changing the content of the Immutable object. It is still possible to change the variable which holds the Immutable object itself by overwriting it by a new Object but you should notice it by the Immutable::new statement and so you can avoid using it.

In many cases an associated constant achieves the desired behaviour:
struct Foo { blah: Blah }
impl Foo {
const S: Shape = Shape { x: 1, y: 1 };
}
Of course, constants cannot be set on creation, they are set at compile-time. Making the field private as explained in other answers will work if dynamicism is required.

Probably the easiest way to achieve this today is by using the readonly crate from prolific developer David Tolnay (author of serde and many others). From the github documentation:
This crate provides an attribute macro to expose struct fields that are readable and writable from within the same module but readable only outside the module.
#[readonly::make]
pub struct S {
// This field can be read (but not written) by super.
#[readonly]
pub(super) readable: i32,
// This field can be neither read nor written by other modules.
private: i32,
}

Related

Initialize data structure's field in Rust with another data structure

I have recently started to program in Rust and would like now to create a structure, which fields I would like to assign using other structures. In the code base, which I am working with, there is the following example:
pub struct StType1 {
f1: String,
f2: bool,
f3: usize,
}
pub struct StType2 {
// ...
field1: StType1,
// ...
}
impl StType2 {
// ...
pub fn build(mut self) {
// ...
let st = StType3 {
field1: self.field1,
};
// ...
}
pub fn field1(&self) -> &StType1 {
&self.field1
}
}
struct StType3 {
field1: StType1,
// ...
}
StType1 is the type of the field, which is contained in both structures StType2 and StType3. StType2 while running build() function creates StType3 and they should have the same value of field1.
In this code there is no move and seems no copy in this assignment field1: self.field1;. Why? What happens then?
I am asking because when I try to create a similar to StType3 - ObjType - structure outside this file:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: object1.field1().clone(),
}
within my functions, I get a reference to StType1 after clone(), not an object, and when I am doing this:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: *object1.field1().clone(),
}
I get an error Can't move:
move occurs because value has type
StType1, which does
not implement the Copy trait
I solve it by adding to the StType1 #[derive(Clone)]. But it worked before with StType2 without #[derive(Clone)]. Why should I add it now? Why there was no move happenning (or no clone required) for field1 when this StType3 was created inside StType2 and the field1 was initialized?
In this code there is no move and seems no copy in this assignment field1: self.field1. Why? What happens then?
This is incorrect – there is a move here, because Rust moves by default.
And this is possible because in the declaration above:
pub fn build(mut self) {
there is no & before the self, and so this build method takes self by-move.
In contrast, the method below:
pub fn field1(&self) -> &StType1 {
&self.field1
}
takes &self, i.e. a reference, and returns &StType1, another reference. So to get a StType1 to construct your object, you need to make it owned by calling .clone(). But that will only work if StType1 implements Clone, which is why you had to add #[derive(Clone)] to it.
Alternatively, you can change the field1() method to take self by-move as well:
pub fn field1(self) -> StType1 {
self.field1
}
The disadvantage of this approach is that since it consumes self, i.e. the entire struct, you won't be able to access any of the other fields after it's called.

How to implement a trait like `Debug` specifically for certain enum variants? [duplicate]

Let's say I have the following Enum
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
I can derive the PartialEq trait for the whole enum by doing so
#[derive(PartialEq)]
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
Assuming you have a setup like:
#[derive(PartialEq)]
struct VarB{
pub value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(VarA),
VarB(VarB)
}
VarA comes from a different crate and you can't compile due to it not having derived PartialEq (or any other external trait).
You can solve that with the newtype pattern (assuming you have access to the relevent fields / accessors)
struct MyVarA(VarA);
impl PartialEq for MyVarA{
fn eq(&self, other: &MyVarA) -> bool {
self.0.value == other.0.value
}
fn ne(&self, other: &MyVarA) -> bool {
self.0.value != other.0.value
}
}
#[derive(PartialEq)]
struct VarB{
value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(MyVarA),
VarB(VarB)
}
further informations:
https://doc.rust-lang.org/rust-by-example/generics/new_types.html
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
This doesn't really make sense.
Traits are implemented for types. An enum is a type and its variants are its values. Your question is equivalent to asking if you could implement a trait for some Strings but not others.
If it is acceptable for unsupported variants to always return false, similar to how f32's PartialEq implementation returns false whenever you compare a NaN value, then you can write that impl by hand:
impl PartialEq for MyEnum {
fn eq(&self, other: &MyEnum) -> bool {
use MyEnum::*;
match (self, other) {
// VariantA and VariantB are supported
(VariantA(value), VariantA(other_value)) => value == other_value,
(VariantB(value), VariantB(other_value)) => value == other_value,
// Any other combinations of variants end up here
_ => false,
}
}
}
Note that you must not implement Eq this way, since implementations of Eq may be assumed to be total, which this is not.

Derive a Trait for particular variants

Let's say I have the following Enum
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
I can derive the PartialEq trait for the whole enum by doing so
#[derive(PartialEq)]
enum MyEnum {
VariantA,
VariantB,
VariantC,
}
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
Assuming you have a setup like:
#[derive(PartialEq)]
struct VarB{
pub value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(VarA),
VarB(VarB)
}
VarA comes from a different crate and you can't compile due to it not having derived PartialEq (or any other external trait).
You can solve that with the newtype pattern (assuming you have access to the relevent fields / accessors)
struct MyVarA(VarA);
impl PartialEq for MyVarA{
fn eq(&self, other: &MyVarA) -> bool {
self.0.value == other.0.value
}
fn ne(&self, other: &MyVarA) -> bool {
self.0.value != other.0.value
}
}
#[derive(PartialEq)]
struct VarB{
value: u32,
}
#[derive(PartialEq)]
enum MyEnum{
VarA(MyVarA),
VarB(VarB)
}
further informations:
https://doc.rust-lang.org/rust-by-example/generics/new_types.html
What I want to do, is derive the trait but only to particular variants and not the whole enum. Is that possible? (or does it even make sense?).
This doesn't really make sense.
Traits are implemented for types. An enum is a type and its variants are its values. Your question is equivalent to asking if you could implement a trait for some Strings but not others.
If it is acceptable for unsupported variants to always return false, similar to how f32's PartialEq implementation returns false whenever you compare a NaN value, then you can write that impl by hand:
impl PartialEq for MyEnum {
fn eq(&self, other: &MyEnum) -> bool {
use MyEnum::*;
match (self, other) {
// VariantA and VariantB are supported
(VariantA(value), VariantA(other_value)) => value == other_value,
(VariantB(value), VariantB(other_value)) => value == other_value,
// Any other combinations of variants end up here
_ => false,
}
}
}
Note that you must not implement Eq this way, since implementations of Eq may be assumed to be total, which this is not.

How can I implement a trait in scope for an enum of existing types for which the trait is implemented?

How can I implement a trait in scope for an enum of existing types for which the trait is implemented?
I have this:
extern crate pnet;
use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::ipv6::Ipv6Packet;
enum EthernetType {
IPv4,
ARP,
VLAN,
IPv6,
Unknown(u16),
}
enum IPPacket<'a> {
IPv4(Ipv4Packet<'a>),
IPv6(Ipv6Packet<'a>),
}
fn ip_decode(pkt: &[u8]) -> IPPacket {
let version = (pkt[0] & 0xf0) >> 4;
if version == 4 {
IPPacket::IPv4(Ipv4Packet::new(&pkt).unwrap())
} else {
IPPacket::IPv6(Ipv6Packet::new(&pkt).unwrap())
}
}
fn main() {
// Parse ethernet packet here...
// ...
let ip_packet = ip_decode(b"deadbeef");
println!("{:?}", ip_packet.payload());
}
The compiler complains that I have not implemented the Packet trait for my enum:
error[E0599]: no method named `payload` found for type `IPPacket<'_>` in the current scope
--> src/main.rs:32:32
|
14 | enum IPPacket<'a> {
| ----------------- method `payload` not found for this
...
32 | println!("{:?}", ip_packet.payload());
| ^^^^^^^
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `payload`, perhaps you need to implement it:
candidate #1: `pnet::packet::Packet`
I thought that the Packet trait would be derived through Ipv4Packet<'a> and Ipv6Packet<'a>?
How can I implement a trait in scope for an enum of existing types
You implement a trait for an enum the same way you implement a trait for a struct:
trait Noise {
fn noise(&self);
}
enum Foo {
Bar,
Baz,
}
impl Noise for Foo {
fn noise(&self) {
match self {
Foo::Bar => println!("bar bar bar"),
Foo::Baz => println!("baz baz baz"),
}
}
}
an enum of existing types for which the trait is implemented
I thought that the Packet trait would be derived
It is not. Doing so would prevent people from implementing their own code for the trait if they needed. It also wouldn't work in all cases, such as when one variant didn't implement it.
trait Noise {
fn noise(&self);
}
struct Bar;
impl Noise for Bar {
fn noise(&self) {
println!("bar bar bar");
}
}
struct Baz;
impl Noise for Baz {
fn noise(&self) {
println!("baz baz baz");
}
}
enum Foo {
Bar(Bar),
Baz(Baz),
}
impl Noise for Foo {
fn noise(&self) {
match self {
Foo::Bar(bar) => bar.noise(),
Foo::Baz(baz) => baz.noise(),
}
}
}
It's conceptually possible that the language could be extended to support some annotation to do this, but I've never heard of anyone suggesting it. You could consider creating an RFC to add it.
Perhaps you can go back to the source that taught you this and correct the problem at the root to prevent other peopl from being confused in the same manner.
See also:
Can traits be used on enum types?

"error: trait bounds are not allowed in structure definitions" when attempting to use polymorphism

Editor's note: This question was asked before Rust 1.0 and before certain features were implemented. The code as-is works today.
I'm writing a board game AI in Rust. There are multiple rulesets for the game and I'd like to have the rules logic separated from the board layout (they are currently mixed). In a language like Ruby, I'd have the separate rule sets implement the same interface. In Rust I thought about using a trait and parameterizing the Board with the ruleset I want to use (e.g. Board<Rules1>::new()).
Saving an object that implements this trait in a struct (like Board) is not allowed. I could turn the Rules into an enum, but it looks a bit messy to me because I can't define separate implementations for the members of the enum. Using pattern matching would work, but that splits the functionality along the function axis and not along the struct axis. Is this just something I have to live with or there some way?
The following code is what I'd like to use:
pub struct Rules1;
pub struct Rules2;
trait Rules {
fn move_allowed() -> bool;
}
impl Rules for Rules1 {
fn move_allowed() -> bool {
true
}
}
impl Rules for Rules2 {
fn move_allowed() -> bool {
false
}
}
struct Board<R: Rules> {
rules: R
}
fn main() {}
It produces the following error:
test.rs:20:1: 22:2 error: trait bounds are not allowed in structure definitions
test.rs:20 struct Board<R: Rules> {
test.rs:21 rules: R
test.rs:22 }
error: aborting due to previous error
The code presented in the question works on all recent versions of Rust, trait bounds on structs are now allowed. The original answer is also still valid.
You need to refine that in the trait implementation, not the struct definition.
pub struct Rules1;
pub struct Rules2;
trait Rules {
fn move_allowed(&self) -> bool;
}
impl Rules for Rules1 {
fn move_allowed(&self) -> bool {
true
}
}
impl Rules for Rules2 {
fn move_allowed(&self) -> bool {
false
}
}
struct Board<R> {
rules: R,
}
impl<R: Rules> Board<R> {
fn move_allowed(&self) -> bool {
self.rules.move_allowed()
}
}
fn main() {
let board = Board { rules: Rules2 };
assert!(!board.move_allowed());
}

Resources