Default constructor implementation in a trait - rust

Point and Vec2 are defined with the same variable and exactly the same constructor function:
pub struct Point {
pub x: f32,
pub y: f32,
}
pub struct Vec2 {
pub x: f32,
pub y: f32,
}
impl Point {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl Vec2 {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
Is it possible to define a trait to implement the constructor function?
So far I found it only possible to define the interface as the internal variables are not known:
pub trait TwoDimensional {
fn new(x: f32, y: f32) -> Self;
}

You can certainly define such a trait, and implement it for your 2 structs, but you will have to write the implementation twice. Even though traits can provide default implementations for functions, the following won't work:
trait TwoDimensional {
fn new(x: f32, y: f32) -> Self {
Self {
x,
y,
}
}
}
The reason why is fairly simple. What happens if you implement this trait for i32 or () or an enum?
Traits fundamentally don't have information about the underlying data structure that implements them. Rust does not support OOP, and trying to force it often leads to ugly, unidiomatic and less performant code.
If however, you have a bunch of structs and want to essentially "write the same impl multiple times without copy/pasting", a macro might be useful. This pattern is common in the standard library, where, for example, there are certain functions that are implemented for all integer types. For example:
macro_rules! impl_constructor {
($name:ty) => {
impl $name {
pub fn new(x: f32, y: f32) -> Self {
Self {
x, y
}
}
}
}
}
impl_constructor!(Point);
impl_constructor!(Vec2);
These macros expand at compile time, so if you do something invalid (e.g. impl_constructor!(i32), you'll get a compilation error, since the macro expansion woudl contain i32 { x, y }.
Personally I only use a macro when there is really a large number of types that need an implementation. This is just personal preference however, there is no runtime difference between a hand-written and a macro-generated impl block.

Related

invoke method implemented to a struct after declared in main

I'm new to rust so I'm not experienced with the way its object-oriented feature functions, I've followed a page but have countered in this situation where I cannot invoke ciao() from the instance of vec but instead, I have to specify by using Vet3::.. but I did assign to vec the type of struct of vet3 so shouldn't it inherit the methods? and if not how to do it in rust, thanks for your patience
pub struct Vet3<T>{
x: T,
y: T,
z: T
}
impl<T> Vet3<T>{
pub fn new(x: T, y: T, z: T) -> Self {
Self { x,y,z }
}
pub fn ciao(){
print!("hello world");
}
}
fn main(){
//let mut sc = ScannerAscii::new(io::stdin());
let vect : Vet3<i64>;
vect = Vet3::new(1, 2, 3);
Vet3::<i64>::ciao();
}
"Instance" methods need to take a self in some way:
impl<T> Vet3<T>{
pub fn new(x: T, y: T, z: T) -> Self {
Self { x,y,z }
}
pub fn ciao(&self) { // take a reference to self
print!("hello world");
}
}
Your new method is "static" since it doesn't have a self parameter. From The Book:
In the signature for area, we use &self instead of rectangle: &Rectangle. The &self is actually short for self: &Self. Within an impl block, the type Self is an alias for the type that the impl block is for. Methods must have a parameter named self of type Self for their first parameter, so Rust lets you abbreviate this with only the name self in the first parameter spot.
Playground

Serde Conditional Deserialization for Binary Formats (Versioning)

I'm trying to deserialize data in a simple non-human readable and non-self describing format to Rust structs. I've implemented a custom Deserializer for this format and it works great when I'm deserializing the data into a struct like this for example:
#[derive(Serialize, Deserialize)]
pub struct Position {
x: f32,
z: f32,
y: f32,
}
However, let's say this Position struct had a new field added (could have been removed too) in a new version:
#[derive(Serialize, Deserialize)]
pub struct Position {
x: f32,
z: f32,
y: f32,
is_visible: bool, // This field was added in a new version
}
But I still need to support both data from both versions of Position. The version of the data (known at runtime) can be given to the Deserializer but how can the Deserializer know the version of a field (known at compile time)?
I've looked at #[serde(deserialize_with)] but it didn't work because I cannot get the needed version information.
I 've also looked at implementing Deserialize manually for Position and I can receive the versions of the fields of Position by implementing something like Position::get_version(field_name: &str).
However, I cannot figure how to get the version of the data currently being deserialized because Deserialize::deserialize only has a trait bound Deserializer<'de> and I cannot make that bound stricter by adding another bound (so it doesn't know about my custom Deserializer).
At this point, I'm thinking about giving the version data of each field when instantiating the Deserializer but I'm not sure if that will work or if there is a better way to go.
Multiple structs implementing a shared trait
If you have several different versions with several different types of struct, and you want a more robust way of handling different variants, it might be a better idea to write structs for each possible format. You can then define and implement a trait for shared behavior.
trait Position {
fn x(&self) -> f32;
fn y(&self) -> f32;
fn z(&self) -> f32;
fn version_number(&self) -> usize;
}
struct PositionV0 {
x: f32,
y: f32,
z: f32
}
impl Position for PositionV0 {
fn x(&self) -> f32 {
self.x
}
// You get the idea for the fn y, fn z implementations
fn version_number(&self) -> usize {
0
}
}
struct PositionV1 {
x: f32,
y: f32,
z: f32,
is_visible: bool,
}
impl Position for PositionV1 {
fn x(&self) -> f32 {
self.x
}
// You get the idea for the fn y, fn z implementations
fn version_number(&self) -> usize {
1
}
}
Carson's answer is great when you do not have a lot of versions but for me I am working with data structures that range over 20 different versions.
I went with a solution that while I don't think is the most idiomatic, is capable of handling an arbitrary number of versions.
In short:
we implement a Version trait which gives the necessary version info to the Deserializer
Deserializer has VersionedSeqAccess (implements serde::de::SeqAccess) that sets a flag
When flag is set, we put None for that field and immediately unset the flag
The idea is to implement the following trait for the struct:
pub trait Version {
/// We must specify the name of the struct so that any of the fields that
/// are structs won't confuse the Deserializer
fn name() -> &'static str;
fn version() -> VersionInfo;
}
#[derive(Debug, Clone)]
pub enum VersionInfo {
/// Present in all versions
All,
/// Present in this version
Version([u16; 4]),
/// Represent Versions of structs
Struct(&'static [VersionInfo]),
// we can add other ways of expressing the version like a version range for ex.
}
Here is how it will be implemented for the example struct Position. This type of manual deriving is error prone so this can be improved with a derive macro (see end):
struct Position {
x: f32,
z: f32,
y: f32,
is_visible: Option<bool>, // With this solution versioned field must be wrapped in Option
}
impl Version for Position {
fn version() -> VersionInfo {
VersionInfo::Struct(&[
VersionInfo::All,
VersionInfo::All,
VersionInfo::All,
VersionInfo::Version([1, 13, 0, 0]),
])
}
fn name() -> &'static str {
"Position"
}
}
Now, the deserializer will be instansiated with the version of the data format we are currently parsing:
pub struct Deserializer<'de> {
input: &'de [u8],
/// The version the `Deserializer` expect the data format to be
de_version: [u16; 4],
/// Versions of each field. (only used when deserialzing to a struct)
version_info: VersionInfo,
/// Whether to skip deserialzing current item. This flag is set by `VersionedSeqAccess`.
/// When set, the current item is deserialized to `None`
skip: bool,
/// Name of struct we are deserialzing into. We use this to make sure we call the correct
/// visitor for children of this struct who are also structs
name: &'static str,
}
pub fn from_slice<'a, T>(input: &'a [u8], de_version: [u16; 4]) -> Result<T, Error>
where
T: Deserialize<'a> + Version,
{
let mut deserializer = Deserializer::from_slice(input, de_version, T::version(), T::name());
let t = T::deserialize(&mut deserializer)?;
Ok(t)
}
Now that the deserializer has the all the information it needs, this is how we define deserialize_struct:
fn deserialize_struct<V>(
self, name: &'static str, fields: &'static [&'static str], visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if name == self.name {
if let VersionInfo::Struct(version_info) = self.version_info {
assert!(version_info.len() == fields.len()); // Make sure the caller implemented version info somewhat correctly. I use a derive macro to implement version so this is not a problem
visitor.visit_seq(VersionedSeqAccess::new(self, fields.len(), &version_info))
} else {
panic!("Struct must always have version info of `Struct` variant")
}
} else {
// This is for children structs of the main struct. We do not support versioning for those
visitor.visit_seq(SequenceAccess::new(self, fields.len()))
}
}
Here is how serde::de::SeqAccess will be implemented for VersionedSeqAccess:
struct VersionedSeqAccess<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
version_info: &'static [VersionInfo],
len: usize,
curr: usize,
}
impl<'de, 'a> SeqAccess<'de> for VersionedSeqAccess<'a, 'de> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Error>
where
T: DeserializeSeed<'de>,
{
if self.curr == self.len {
// We iterated through all fields
Ok(None)
} else {
// Get version of the current field
let version = &self.version_info[self.curr as usize];
self.de.version_info = version.clone();
// Set the flag if the version does not match
if !is_correct_version(&self.de.de_version, &version) {
self.de.skip = true;
}
self.curr += 1;
seed.deserialize(&mut *self.de).map(Some)
}
}
}
The final part of the puzzle is inside deserialize_option. If we are at a field not found in current data format the skip flag will be set here and we will produce None:
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if self.skip == true {
self.skip = false;
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
A lengthy solution but it works great for my usecase dealing with a lot of structs with lots of fields from different versions. Please do let me know how I can make this less verbose/better. I also implemented a derive macro (not shown here) for the Version trait to be able to do this:
#[derive(Debug, Clone, EventPrinter, Version)]
pub struct Position {
x: f32,
z: f32,
y: f32,
#[version([1, 13, 0, 0])]
is_visible: Option<bool>,
}
With this derive macro, I find that this solution tends to scale well for my usecase.

How to make generic type bounds for absolute value?

I'm currently learning rust and thought I'd test myself by implementing Absolute value on a simple generic type. Unfortunately, there is no Abs trait that I can implement myself (or that I can find), and without a trait, I'm not sure how I can make a bound to only accept types that can calculate the absolute value.
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T>
where
T: Abs, // This trait doesn't exist...
{
fn abs(&self) -> Self {
Point {
x: self.x.abs(),
y: self.y.abs(),
}
}
}
As an alternative, I can implement the method for Point<i32>, Point<f32> and all the other native types individually, or I could make a helper function which simply checks if a value is < 0 and manually converts it (using an ordering bound). I just feel I must be missing something obvious and there must be some kind of bound to make the generic function work with all appropriate types.
Is there a trait suitable for implementing? Or is there a way to make a suitable bound?
In either case, is there a way to implement this functionality without the Point<i32>, ... or manual abs helper function boiler plate above?
(Subjective) How would you implement this?
You can by using Signed trait from num:
use num::Signed;
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T>
where
T: Signed,
{
fn abs(&self) -> Self {
Self {
x: self.x.abs(),
y: self.y.abs(),
}
}
}
You can define your own trait:
trait Abs {
fn abs(self) -> Self;
}
impl Abs for f64 {
fn abs(self) -> Self {
f64::abs(self)
}
}
I do similar things in the lowdim crate.

Is there a memory efficient way to change the behavior of an inherent implementation?

Is there a memory efficient way to change the behavior on an inherent implementation? At the moment, I can accomplish the change of behavior by storing a number of function pointers, which are then called by the inherent implementation. My difficulty is that there could potentially be a large number of such functions and a large number of objects that depend on these functions, so I'd like to reduce the amount of memory used. As an example, consider the code:
// Holds the data for some process
struct MyData {
x: f64,
y: f64,
fns: MyFns,
}
impl MyData {
// Create a new object
fn new(x: f64, y: f64) -> MyData {
MyData {
x,
y,
fns: CONFIG1,
}
}
// One of our functions
fn foo(&self) -> f64 {
(self.fns.f)(self.x, self.y)
}
// Other function
fn bar(&self) -> f64 {
(self.fns.g)(self.x, self.y)
}
}
// Holds the functions
struct MyFns {
f: fn(x: f64, y: f64) -> f64,
g: fn(x: f64, y: f64) -> f64,
}
// Some functions to use
fn add(x: f64, y: f64) -> f64 {
x + y
}
fn sub(x: f64, y: f64) -> f64 {
x - y
}
fn mul(x: f64, y: f64) -> f64 {
x * y
}
fn div(x: f64, y: f64) -> f64 {
x / y
}
// Create some configurations
const CONFIG1: MyFns = MyFns {
f: add,
g: mul,
};
const CONFIG2: MyFns = MyFns {
f: sub,
g: div,
};
fn main() {
// Create our structure
let mut data = MyData::new(1., 2.);
// Check our functions
println!(
"1: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
// Change the functions
data.fns = CONFIG2;
// Print the functions again
println!(
"2: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
// Change a single function
data.fns.f = add;
// Print the functions again
println!(
"3: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
}
This code allows the behavior of foo and bar to be changed by editing f and g. However, it also not flexible. I'd rather use a boxed trait object Box<dyn Fn(f64,f64)->f64, but then I can't create some default configurations like CONFIG1 and CONFIG2 because Box can not be used to create a constant object. In addition, if we have a large number of functions and objects, I'd like to share the memory for their implementation. For function pointers, this isn't a big deal, but for closures it is. Here, we can't create a constant Rc for the configuration to share the memory. Finally, we could have a static reference to a configuration, which would save memory, but then we could not change the individual functions. I'd rather we have a situation where most of the time we share memory for the functions, but have the ability hold its own memory and change the functions if desired.
I'm open to a better design if one is available. Ultimately, I'd like to change the behavior of foo and bar at runtime based on a function held, in some form or another, inside of MyData. Further, I'd like a way to do so where the memory is shared when possible and we have the ability to change an individual function and not just the entire configuration.
A plain dyn reference will work here - it allows references to objects that have a certain trait but with type known only at runtime.
(This is exactly what you want for function pointers. Think of it as each function having its own special type, but falling under a trait like Fn(f64,f64)->f64.)
So your struct could be defined as:
struct MyData<'a> {
x: f64,
y: f64,
f: &'a dyn Fn(f64, f64) -> f64,
g: &'a dyn Fn(f64, f64) -> f64,
}
(Notice, you need the lifetime specifier 'a to ensure the the lifetime of that references is not shorter than the struct itself.)
Then your impl could be like:
impl<'a> MyData<'a> {
// Create a new object
fn new(x: f64, y: f64) -> Self {
MyData {
x,
y,
f: &add, // f and g as in CONFIG1
g: &mul,
}
}
fn foo(&self) -> f64 {
(self.f)(self.x, self.y)
}
// etc...
}
Depending on how you want the default configurations to work, you could either make them as more inherent functions such as fn to_config2(&mut self); or you could make a separate struct just with the function pointers and then have a function to copy those function pointers into the MyData struct.

How to implement multiple traits for a struct without repeating methods?

The code below works just fine, but I'm repeating myself a lot
and I don't think this is really Rustic. For example I'm implementing two traits for Square and this doesn’t feel right! Also the function coordinate() is repeated in the trait and in the implementation.
Is there a way to implement this code without repeating myself so often? Is it possible to implement the two traits like:
impl BasicInfo && Sides for Square {
....
}
the above code does not work, it is just an idea. When a function can be applied to multiple structs, is it possible to define it just once in the trait BasicInfo and access it.
fn main() {
let x_1: f64 = 2.5;
let y_1: f64 = 5.2;
let radius_1: f64 = 5.5;
let width_01 = 10.54;
let circle_01 = Circle { x: x_1, y: y_1, radius: radius_1 };
let square_01 = Square { x: x_1, y: y_1, width: width_01, sides: 4 };
println!("circle_01 has an area of {:.3}.",
circle_01.area().round());
println!("{:?}", circle_01);
println!("The coordinate of circle_01 is {:?}.\n", circle_01.coordinate());
println!("coordinate of square_01: {:?} has an area of: {} m2 and also has {} sides.",
square_01.coordinate(),
(square_01.area() * 100.0).round() / 100.0,
square_01.has_sides() );
}
#[derive(Debug)]
struct Circle {
x: f64,
y: f64,
radius: f64,
}
struct Square {
x: f64,
y: f64,
width: f64,
sides: i32,
}
trait BasicInfo {
fn area(&self) -> f64;
// coordinate() is declared here, but not defined. Is it possible to define it here and still be able to access it when I want it.
fn coordinate(&self) -> (f64, f64);
}
trait Sides {
fn has_sides(&self) -> i32;
}
impl BasicInfo for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
// coordinate() gets defined again, and looks like repeating code
fn coordinate(&self) -> (f64, f64) {
(self.x, self.y)
}
}
impl BasicInfo for Square {
fn area(&self) -> f64 {
self.width.powf(2.0)
}
// coordinate() gets defined again, and looks like repeating code
fn coordinate(&self) -> (f64, f64) {
(self.x, self.y)
}
}
impl Sides for Square {
fn has_sides(&self) -> i32 {
self.sides
}
}
for your second question (avoid repeating the identical implementation of coordinate) I wanted to show you the macro-based solution.
Funnily enough, it leaves you with 3 traits instead of 2, so it goes in the exact opposite direction of your first question. I guess you can't have everything! :)
// factoring out the Coordinates trait from BasicInfo
trait Coordinates {
fn coordinate(&self) -> (f64, f64);
}
// but we can require implementors of BasicInfo to also impl Coordinates
trait BasicInfo: Coordinates {
fn area(&self) -> f64;
}
// helper macro to avoid repetition of "basic" impl Coordinates
macro_rules! impl_Coordinates {
($T:ident) => {
impl Coordinates for $T {
fn coordinate(&self) -> (f64, f64) { (self.x, self.y) }
}
}
}
#[derive(Debug)]
struct Circle {
x: f64,
y: f64,
radius: f64,
}
#[derive(Debug)]
struct Square {
x: f64,
y: f64,
width: f64,
sides: i32,
}
// the macro here will expand to identical implementations
// for Circle and Square. There are also more clever (but a bit
// harder to understand) ways to write the macro, so you can
// just do impl_Coordinates!(Circle, Square, Triangle, OtherShape)
// instead of repeating impl_Coordinates!
impl_Coordinates!(Circle);
impl_Coordinates!(Square);
trait Sides {
fn has_sides(&self) -> i32;
}
impl BasicInfo for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
impl BasicInfo for Square {
fn area(&self) -> f64 {
self.width.powf(2.0)
}
}
impl Sides for Square {
fn has_sides(&self) -> i32 {
self.sides
}
}
Disclaimer: at the time of writing, there are two questions bundled in one, and the latter does not match the title. So going by the title...
Is it possible to implement multiple traits at once?
impl BasicInfo && Sides for Square {
....
}
No.
The overhead of implementing them separately is relatively low, and for more complex situations, when constraints are necessary, it might not be possible as you would want different types of constraints for each trait.
That being said, you could potentially open an RFC suggesting this be implemented, and then let the community/developers decide whether they find it worth implementing or not.
There's nothing wrong with implementing multiple traits for one type; it's very common in fact.
I also don't understand what you mean by repeating coordinate in the trait and the impl. The function is declared in the trait and implemented in the impl, just like every other trait function. Did you mean that the function's implementation is identical in the Square and Circle impls? Macros would help you there, although there may be a better way.

Resources