• image
  • image
  • image
logo logo
  • Home
  • View Jobs
  • Services
  • About Us
  • Blog
  • Contact Us
img img

Looking inside traits (Part 2)

September 27, 2021 The Editorial Board- Teamware Solutions

The Rust programming language

Journey so far

In our earlier dispatch we talked about the proposal and our ambitious intent to present a working prototype to what our customer perceived as pressing challenge that the software will solve. We started with modelling the domain with type ecosystem of Rust. We modelled a contract as struct and types of a contract as enum to express the variety more declaratively. We started thinking about modelling the behavior for the contracts. There were two options ahead of us –

1. Define behavior as trait
2. Define behavior as methods of the type

We continue from this point onwards on understanding the impact of those choices.

Traits

If you are of the type who learn new concept by simplifying them first, let us have a high-five so are we. In simpler way of saying – Traits are kind of interface from the world of C# or such equivalent object-oriented language. Now you get it why we stated an implementation of trait will look like –

 

impl enforce_contract for contract {

   …

}

 

Let us put the equivalent in a language like C#

 

public struct contract : enforce_contract {

   …

}

 

Other than the syntactic sugar the prime difference between the way a type implements the interface is in the order of occurrence of the type and interface. In C# the type appears first and the type’s contract i.e., the interface appears afterwards thus easing into an interpretation that you would have heard multiple times that interface is a contract of assured behavior implemented by a type; which emphasizes the type centricity.

Rust on the other hand focuses on the behavior and assurance of the behavior instead of the emphasizing the type. Trait is in-fact interpreted as behaviors that are shared by different types. Instead of emphasizing on a contract a type adheres to Rust by using traits allow us to model a type very early and evolve by adding new behaviors that are good abstractions across an implemented code.

In case you are lost by reading the above paragraph let us break down the evolution of C# code and play one frame at time.

Time clip # 1

In the beginning customer was not much interested in the impact and wanted to capture the details of contracts in a system. This will naturally lead to a type definition similar to –

 

public class contract {

   public string supplier {get;set;}

   public DateTime enforced_date {get;set;}

   public DateTime expiry_date {get;set;}

   public uint contract_value {get;set;}

}

 

Time clip # 2

Now true to the object-oriented principle the type needs to deal with its own state and should define behavior which modifies the state.

This can be easily implemented by bringing in the change like this –

 

public class contract {

   public string supplier {get;set;}

   public DateTime enforced_date {get;set;}

   public DateTime expiry_date {get;set;}

   public uint contract_value {get;set;}

   

   public bool is_active() {

       if(DateTime.Now > expiry_date) {

           return false;

       } else {

           return true;

       }

   }

}

 

There was this class called logistics_trip which was created earlier with a definition that looks like –

 

public class logistic_trip {

   public int ladden_weight {get;set;}

   public long origin {get;set;}

   public long destination {get;set;}

   public DateTime start {get;set;}

   public DateTime end {get;set;}

   

   public bool is_active() {

       if(DateTime.Now > end) {

           return false;

       } else {

           return true;

       }

   }

}

 

Please notice the similarity for the is_active method. Now our developers have packaged these base types to a library and many versions have shipped with much of proprietary code implementations.

Time clip # 3

We now introduce after many versions introduce a third-party vendor to whom we share the library and not the code. We want them to use the types but not modify it in their code. An example usage of the code above would be like

 

using customer.sourcelibrary.types;

 

contract shipping_contract = new contract();

…

logistic_trip stage_material_shipping = new logistic_trip();

 

if(shipping_contract.is_active()) {

   if(stage_material_shipping.is_active()) {

       …

   }

   …

}

 

public class some_new_type {

   public int bulk_close(time_bound instance) {

       if(instance.is_active()) {

           //Implementation

       }

   }

}

 

The namespace customer.sourcelibrary.types is assumed to be the core library with contract and logistic_trip type. The code sample is random sample and not meant to be part of a single type or namespace. Crux of the scenario above is when the third-party vendor intends to define a method that could accept both contract and logistic_trip as parameter to a method in the example above method is named as bulk_close. The third-party vendor wishes to define an interface named time_bound and make both contract and logistic_trip implement the interface.

For the third-party vendor this is not possible without modifying the source library which we created. Thus, leading to the next snap shot which typically introduces debt in code. The debt which cannot be lowered without refactoring the source implementation.

Time clip # 4

Create two different methods with differing signatures to handle the situation

 

public class some_new_type {

   public int bulk_close(contract instance) {

       if(instance.is_active()) {

           bulk_close(true);

       } else {

           bulk_close(false);

       }

   }

 

   public int bulk_close(logistic_trip instance) {

       if(instance.is_active()) {

           bulk_close(true);

       } else {

           bulk_close(false);

       }

   }

 

   public int bulk_close(bool currently_active) {

       if(currently_active) {

           bulk_close(true);

       }  else {

           bulk_close(false);

       }

   }

}

 

 

We had to write such a code because we could not do something like this –

 

public interface time_bound {

   public bool is_active();

}

 

contract : time_bound

 

logistic_trip: time_bound

 

This is simply not possible in languages like C# or for that reason Java. Thus, introducing the tech debt and for situations like ours where we are evolving the model along the way as we learn or discover new concepts it becomes increasingly taxing because we need to refactor code.

Trait on the other hand simplifies the evolution for us so that our Time clip #4 could be written as

 

pub trait time_bound {

   fn is_active(&self) -> bool;

}

 

 

impl time_bound for contract {

   …

}

impl time_bound for logistic_trip {

   …

}

 

 

This will work even if the struct and its implementation have evolved in a different crate (library) than that of the third-party vendor’s crate (library).

Now did you notice the simple difference in philosophy by changing the order of type and trait the impact it has on evolutionary modelling of types.

This was very powerful concept for us and along the way we also observed how the reference to &self makes it more javascriptstyle binding to a type at runtime. This characteristic of Rust language made us evolve the enforce_contract trait have a assess_impact method which helped us achieve the goal we set for ourselves in mind and we could implement the assess_impactmethod on many types. Now for any event which has list of contracts we could simplify and distribute the implementation of impact in case of failure to execute the contract. So much so that same code could live from the early stage of proposal flow into latter stages of development and eventually in production.

Post navigation

Previous Article
Next Article

Recent post

  • The Swiss Army Knife for developer
  • Time Management Techniques
  • Habits of Successful Leaders
  • Error in probabilities
  • Another gem from the past

Archives

  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • October 2023
  • June 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • January 2021
  • December 2020
  • October 2020
  • August 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • Home
  • View Jobs
  • Services
  • About Us
  • Contact Us
img img