Members
Constructors
Rust does not have any notion of constructors. Instead, you just write factory
functions that return an instance of the type. The factory functions can be
stand-alone or associated functions of the type. In Java terms, associated
functions are like having static methods on a type. Conventionally, if there
is just one factory function for a struct
, it's named new
:
struct Rectangle {
length: f64,
width: f64,
}
impl Rectangle {
pub fn new(length: f64, width: f64) -> Self {
Self { length, width }
}
}
Since Rust functions (associated or otherwise) do not support overloading; the
factory functions have to be named uniquely. For example, below are some
examples of so-called constructors or factory functions available on String
:
String::new
: creates an empty string.String::with_capacity
: creates a string with an initial buffer capacity.String::from_utf8
: creates a string from bytes of UTF-8 encoded text.String::from_utf16
: creates a string from bytes of UTF-16 encoded text.
In the case of an enum
type in Rust, the variants act as the constructors.
See the section on enumeration types for more.
See also:
Methods (static & instance-based)
Like Java, Rust types (both enum
and struct
), can have static and
instance-based methods. In Rust-speak, a method is always instance-based and
is identified by the fact that its first parameter is named self
. The self
parameter has no type annotation since it's always the type to which the
method belongs. A static method is called an associated function. In the
example below, new
is an associated function and the rest (length
, width
and area
) are methods of the type:
struct Rectangle {
length: f64,
width: f64,
}
impl Rectangle {
pub fn new(length: f64, width: f64) -> Self {
Self { length, width }
}
pub fn length(&self) -> f64 {
self.length
}
pub fn width(&self) -> f64 {
self.width
}
pub fn area(&self) -> f64 {
self.length() * self.width()
}
}
Properties
In Java, it is generally good practice for fields of a type (e.g. a class) to be private. They are then
protected/encapsulated by property members with accessor methods (getters
and
setters
) to read or write to those fields. The accessor methods can contain extra
logic, for example, to either validate the value when being set or compute a
value when being read. Rust only has methods where a getter is named after the
field (in Rust method names can share the same identifier as a field) and the
setter uses a set_
prefix.
Below is an example showing how property-like accessor methods typically look for a type in Rust:
struct Rectangle {
length: f64,
width: f64,
}
impl Rectangle {
pub fn new(length: f64, width: f64) -> Self {
Self { length, width }
}
// like property getters (each shares the same name as the field)
pub fn length(&self) -> f64 { self.length }
pub fn width(&self) -> f64 { self.width }
// like property setters
pub fn set_length(&mut self, val: f64) { self.length = val }
pub fn set_width(&mut self, val: f64) { self.width = val }
// like computed properties
pub fn area(&self) -> i32 {
self.length() * self.width()
}
}
Visibility/Access modifiers
Java has a number of accessibility or visibility modifiers:
private
protected
package private
public
In Rust, a compilation is built-up of a tree of modules where modules contain
and define items like types, traits, enums, constants and
functions. Almost everything is private by default. One exception is, for
example, associated items in a public trait, which are public by default.
This is similar to how members of a Java interface declared without any public
modifiers in the source code are public by default. Rust only has the pub
modifier to change the visibility with respect to the module tree. There
are variations of pub
that change the scope of the public visibility:
pub(self)
pub(super)
pub(crate)
pub(in PATH)
For more details, see the Visibility and Privacy section of The Rust Reference.
The table below is an approximation of the mapping of Java and Rust modifiers:
Java | Rust | Note |
---|---|---|
private | (default) | See note 1. |
protected | N/A | See note 2. |
package private | pub(crate) | |
public | pub |
-
There is no keyword to denote private visibility; it's the default in Rust.
-
Since there are no class-based type hierarchies in Rust, there is no equivalent of
protected
.
Mutability
When designing a type in Java, it is the responsiblity of the developer to
decide whether the type is mutable or immutable. Java does support an immutable design
for "data carriers" with record classes
.
In Rust, mutability is expressed on methods through the type
of the self
parameter as shown in the example below:
struct Point {
x: f64,
y: f64,
}
impl Point {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
// self is not mutable
pub fn x(&self) -> f64 { self.x }
pub fn y(&self) -> f64 { self.y }
// self is mutable
pub fn set_x(&mut self, val: f64) { self.x = val }
pub fn set_y(&mut self, val: f64) { self.y = val }
}