Rust: The Language Redefining Efficiency and Safety in Software Development

When I first delved into Rust, it felt like stepping into a world where safety, performance, and developer experience converge in harmony. As someone who’s often wrestled with the complexities of memory management, runtime bugs, and performance bottlenecks in other languages, Rust has been a game-changer. It has not only helped me reduce running costs for applications but also significantly improved their security and stability.

Let me take you on a journey through what makes Rust truly stand out, chapter by chapter, highlighting its most powerful features.

Zero-Cost Abstractions: Performance Without Sacrifice

One of Rust’s most impressive features is its zero-cost abstractions. High-level constructs like iterators and smart pointers provide clarity and elegance to your code but with zero performance overhead. This means you get the power of abstraction without sacrificing efficiency—a dream come true for anyone managing resource-heavy applications.

For me, this has translated into writing expressive code that’s both readable and as efficient as handcrafted low-level implementations. Rust makes me feel like I’m coding with superpowers, optimizing applications without even breaking a sweat.

[RUST]

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();

    println!("{:?}", doubled); // Output: [2, 4, 6, 8, 10]
}

This example demonstrates how Rust’s iterators provide high-level abstraction for traversing and transforming collections without performance overhead.

Ownership Model: Memory Safety Reinvented

Rust’s ownership model was a revelation for me. It ensures every value has a single owner, and once that owner goes out of scope, the memory is freed—no garbage collector, no fuss.

This elegant approach eliminates bugs like dangling pointers, double frees, and memory leaks. For anyone who’s spent late nights debugging memory issues (like I have), Rust feels like the safety net you didn’t know you needed. It’s not just about reducing bugs; it’s about coding with peace of mind.

[RUST]

fn main() {
    let s = String::from("hello"); // s owns the memory
    let s1 = s;                    // Ownership is transferred to s1
    // println!("{}", s);          // Uncommenting this line causes a compile error

    println!("{}", s1); // Correct, as s1 now owns the memory
}

This shows how Rust ensures memory safety by transferring ownership, preventing use-after-free errors.

Borrowing Rules: Sharing Done Right

Building on the ownership model, Rust introduces borrowing rules, allowing you to share data through references without taking ownership. But there’s a catch (a good one): Rust enforces strict rules around lifetimes and mutability, ensuring your references never outlive the data they point to.

This might sound restrictive at first, but trust me—it’s liberating. Rust helps you avoid data races and other concurrency nightmares, making multi-threaded programming surprisingly smooth.

[RUST]

fn main() {
    let mut x = 10;

    {
        let r1 = &x; // Immutable borrow
        println!("r1: {}", r1);

        // let r2 = &mut x; // Uncommenting this line causes a compile error
    }

    let r3 = &mut x; // Mutable borrow after immutable borrow scope ends
    *r3 += 1;
    println!("x: {}", x);
}

This code highlights borrowing rules, showcasing how Rust prevents data races by enforcing strict borrowing lifetimes.

Algebraic Data Types (ADTs): Expressive and Error-Free

Rust’s Algebraic Data Types (ADTs) are like a Swiss Army knife for designing robust data models. Whether it’s enums, structs, or tuples, Rust lets you express relationships concisely.

The Option and Result types are my personal favorites. They force you to handle errors explicitly, reducing those frustrating runtime surprises. Combined with pattern matching, Rust makes handling edge cases a breeze. ADTs in Rust don’t just make code cleaner—they make it safer.

[RUST]

fn divide(a: i32, b: i32) -> Option<i32> {
    if b == 0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    match divide(10, 2) {
        Some(result) => println!("Result: {}", result),
        None => println!("Cannot divide by zero!"),
    }
}

Rust’s Option type ensures that edge cases like division by zero are handled explicitly, reducing runtime errors.

Polymorphism: Traits Over Classes

Rust’s approach to polymorphism is refreshingly different. Forget bloated inheritance hierarchies—Rust uses traits to define shared behavior across types. This static dispatch ensures zero runtime cost.

When I need flexibility, Rust also offers dynamic dispatch via dyn Trait. It’s the best of both worlds—flexibility where needed and performance everywhere else.

[RUST]

trait Area {
    fn area(&self) -> f64;
}

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Area for Circle {
    fn area(&self) -> f64 {
        3.14 * self.radius * self.radius
    }
}

impl Area for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn print_area<T: Area>(shape: T) {
    println!("Area: {}", shape.area());
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle { width: 4.0, height: 3.0 };

    print_area(circle);
    print_area(rectangle);
}

This demonstrates Rust’s trait-based polymorphism, allowing shared behavior across different types.

Async Programming: Concurrency with Confidence

Rust’s async/await model is a masterpiece of design. Writing asynchronous code that feels synchronous is a joy, especially when you know Rust’s ownership rules are keeping your data safe.

For my high-throughput projects, Rust’s lightweight async tasks have been a game-changer. I’ve seen noticeable improvements in scalability and responsiveness, proving Rust isn’t just about safety—it’s about speed, too.

[RUST]

use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    let task1 = async_task(1);
    let task2 = async_task(2);

    tokio::join!(task1, task2);
}

async fn async_task(id: u32) {
    println!("Task {} started", id);
    sleep(Duration::from_secs(2)).await;
    println!("Task {} completed", id);
}

Using the Tokio runtime, Rust enables asynchronous programming with minimal overhead, ideal for scalable applications.

Meta Programming: Automate the Boring Stuff

Rust’s support for meta-programming is a lifesaver. With procedural macros and attributes, Rust automates repetitive tasks elegantly.

One standout for me has been the serde library, which uses macros to simplify serialization and deserialization. It’s like having an extra pair of hands, ensuring you can focus on logic rather than boilerplate.

Macros: Power and Simplicity

Rust’s macros are not just about simple text replacement; they’re a gateway to reusable, efficient patterns. Whether it’s creating custom DSLs or avoiding code duplication, macros have saved me countless hours.

What I love most is how declarative macros make the complex simple, ensuring my codebase remains DRY (Don’t Repeat Yourself) without becoming cryptic.

[RUST]

macro_rules! say_hello {
    () => {
        println!("Hello, Rustaceans!");
    };
}

fn main() {
    say_hello!(); // Expands to: println!("Hello, Rustaceans!");
}

Rust’s macros simplify repetitive code, boosting productivity without runtime costs.

Cargo: Your New Best Friend

Managing dependencies and builds can often feel like a chore, but Rust’s Cargo turns it into a seamless experience. From managing dependencies to building projects and running tests, Cargo is the all-in-one tool I never knew I needed.

I particularly appreciate its integration with crates.io, Rust’s package registry. Finding and using libraries is intuitive and hassle-free, leaving me more time to focus on building features.

[RUST]

[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

With Cargo, adding dependencies is as simple as updating the Cargo.toml file. Here, tokio is used for asynchronous programming and serde for data serialization.

Why I’m Sticking with Rust

Rust has redefined what I expect from a programming language. Its combination of safety, performance, and developer-friendly tools has allowed me to build applications that are not only faster and more secure but also more cost-efficient.

If you’re still unsure whether Rust is worth the learning curve, let me leave you with this: Rust isn’t just a language—it’s a way to write code you can be proud of. It’s about building software that stands the test of time, with fewer bugs and better resource efficiency.

Here’s to building the future—one safe, efficient, and high-performance Rust program at a time. 🚀

Highlighted Features in Summary: Wrap-up

  • Zero-Cost Abstractions: Write expressive code without runtime overhead.
  • Ownership Model: Memory safety without garbage collection.
  • Borrowing Rules: No data races; safe and efficient memory sharing.
  • Algebraic Data Types (ADTs): Cleaner, safer error handling.
  • Polymorphism: Trait-based, with no runtime cost.
  • Async Programming: Lightweight, high-performance concurrency.
  • Meta Programming: Automate repetitive tasks effortlessly.
  • Macros: Powerful and reusable patterns.
  • Cargo: The all-in-one tool for dependency and project management.

If this resonates with your development goals or sparks curiosity, give Rust a try—you won’t regret it!

Thanks again for returning to my blog!


Sources used for this article:
https://www.rust-lang.org/
https://doc.rust-lang.org/stable/

Hey Devs, is your calculated GST on Xero API generated invoice one cent off?

Is your system working with price items with values more than 2 decimal points long? Are you using rounding as a part of the calculating formula? Have you generated an invoice from Xero API and later found out that the actual total on it is a few cents off?

If the answers to all of these questions are yes, you are at the right place!

Well, you know this story … you have done all that hard work on building Xero API integration, happy with finishing the project on time and with such a masterpiece level source code, and in the first integration test run you discover that your invoice calculated data summary data are different from what Xero has generated in the invoice (ouch!):

Invoice line items total calculation with one cent off

And yes, something is not looking right, and scratching the head does not seem to be helping much …

Well, the truth is that every system does invoice calculation of subtotal, total, and GST differently. The same applies to the Xero backend service (API) and therefore these two ways are good options to get you out of the trouble.

One way of doing it is to add an adjustment line as a part of the Xero API request payload and put the variation value into it to keep source data in alignment with Xero. Personally, I don’t really like this approach. The reason being is that you are going to end up with a more comprehensive solution for not much-added business value as appose to the time spent on building it.

Another way is to follow the Xero calculation formula. Yep, you heard me right…

And the way I would rather suggest you go with. You may be asking why I would do that?

So let me explain my view on this.

Let’s assume that Xero as a business is on the market for several decades now. You may be getting some sense about the overall knowledge Xero as a company must have gained from such a long time history, of providing comprehensive financial services to customers.

I also know that Xero has gone through several business validation iterations and internal system refactoring processes to build as much accurate tax calculation business logic on the API backend as possible. All these company journeys supported by customer feedback and over time accumulating domain knowledge helped Xero build a great service reputation in the current market worldwide.

And the question is, why wouldn’t I use this knowledge to my advantage? And just btw – I am not participating in any affiliate programs running by Xero!

Do you have another thought about it? – leave me a comment below 😉

Ok, let’s go ahead and talk about the calculation formula…you can start to calculate GST from the prices either GST inclusive or exclusive. These are the types of line items on request payload.

Types of line items to be used in the invoice request payload

1. Line item price with GST exclusive

  1. Round line-item-price to 2 DP (decimal places)
    Round(line-item-price) => Round2DP(10.5456)
  2. Calculate line-item GST from rounded line-item-price, line-item-quantity, and GST rate, and round the result to 2 DP for each line-item
    Round(line-item-price * [GST rate] * line-item-quantity) => Round2DP(10.55*(0.155) *5)
  3. Sum up the rounded line-item-price(s) as Subtotal
    (line-item-price * line-item-quantity)+…N(row)…+(line-item-price * line-item-quantity)
  4. Sum-up the line-item calculated GST (step2) as GST Total
    (step2)+…N(row)…+(step2)
  5. Add Subtotal and GST Total as invoice Total
    (step3)+(step4)

Feels difficult? That is ok. For simplicity and quick integration reasons, I have created the NuGet package XeroGSTTaxCalculation (NET 5) for you, free to use.

A short demonstration of how to use the XeroGSTTaxCalculation NuGet package:

class Program
    {
        static void Main(string[] args)
        {         

            IXeroTaxCalculationService service = new XeroTaxCalculationService();

            var data = new[] { 
                new LineItem { Code = "code_1", Price = 12m, Quantity = 10 }, 
                new LineItem { Code = "code_2", Price = 8.7998m, Quantity = 8 } 
            };

            var invoiceDetails = service.CalculateGSTFromPriceGSTExclusive(data, 0.25);

            Console.WriteLine(invoiceDetails);
            Console.ReadLine();
        }
    }

2. Line item price with GST inclusive

  1. Add 1 to the GST rate
    1+[GST rate] => 1 + 0.15
  2. Calculate and Round to 2 DP line-item-price as line-item-price-total
    Round(line-item-price*line-item-quantity) => Round2DP(10.5456*5)
  3. Divide rounded line-item-price-total by GST rate (for each line-item) and round to 2 DP as line-item-price-lessTax
    Round(line-item-price-total/[GST rate] )=> Round2DP(52.73/1.15)
  4. Subtract line-item-price-lessTax from line-item-price-total as line-item-gst
    (line-item-price-total ) – (line-item-price-lessTax)
  5. Sum-up the line-item calculated GST (line-item-gst) as invoice GST total
    (step4)+…N(row)…+(step4)
  6. Sum-up line-item-price-total as invoice Total
    (step2)+…N(row)…+(step2)
  7. Subtract GST total from Total to get invoice Subtotal
    (step6) – (step5)

Feel free to use NuGet package XeroGSTTaxCalculation for this as shown in one code example from above. I bet you’re gonna need this saved time to spend on beer sessions with your mates instead .). Cheers!

For more information about rounding, visit the Xero documentation site for developers.

Thanks for staying, subscribe to my blog, and leave me a comment below.

cheers\