General

How to get into Rust as a TypeScript Developer

Matthew Pagan
9 min read
How to get into Rust as a TypeScript Developer
This article was written over 18 months ago and may contain information that is out of date. Some content may still be relevant, but please refer to official documentation for the latest information.

How to get into Rust as a JavaScript/TypeScript Developer

Rust is the language of my dreams. I work primarily as a JavaScript developer, so my skillset largely includes the web, as well as NodeJS environments. I love JavaScript, but I wanted a new language to dive into. That new language needed to check off a few boxes:

  • Rich Type System (TypeScript made me see the value of this)
  • Fast / Low Level (JavaScript is SO SLOW, I want speed next)
  • Great Documentation
  • Community

I originally thought my new language was going to be C++. It has the speed I wanted, and is a strongly typed language that is also statically typed. The big problem with C++, though, was that there wasn't any sign of official documentation, and the community seems a lot less beginner-friendly than I was hoping.

Then came Rust. Rust checks all the boxes. Speaking about their documentation alone, it is OUTSTANDING! It's a one-stop shop for all of my language needs. As a beginner, this is SUCH a time saver. Shout-out to mdbook for having one of the cleanest documentation formats I have ever seen.

The Speed of Rust

I briefly mentioned my desire for great documentation, but now, let's look at speed!

This blog post by Sean Wragg sums up the speed of Rust in a comparison of web frameworks. In his article, he compared Rocket (a web framework written in Rust) to Restify (another web framework written in NodeJS). He benchmarked how many requests per second each service was able to handle, and the results were staggering.

Rocket vs Restify Responses per Second

The requests/second each service could handle:

  • Restify: 7,996.19
  • Rocket: 72,133.75

Based on these benchmarking results, the Rocket-powered service was able to handle 9x the amount of requests than Restify. This is wild!

Let's dive into Rust and see how we can switch our trains of thoughts from JavaScript to Rust.

Prerequisites

We'll need to install Rust onto our computer for local Rust development. This process is incredibly easy. Navigate here and their documentation will give you a way to install Rust, tailored to whatever OS you use when you click that link.

Executing the install process will install the entire rust tool-chain onto your computer:

  • rustc: The Rust compiler, Usually not invoked directly, but instead, through cargo.
  • cargo: Think of this as a Rust's version of a super-powered npm. It downloads dependencies, compiles and runs our code, and can distribute our packages to the Rust Community Crate Registry.
  • rustup: This installs and updates Rust.

The most important component of this tool-chain is Cargo.

Your new npm, Cargo

From the Cargo Book

Cargo is the Rust package manager. Cargo downloads your Rust package's dependencies, compiles your packages, makes distributable packages, and uploads them to crates.io, the Rust community’s package registry. You can contribute to this book on GitHub.

We are going to mainly use 3 cargo commands:

  • cargo new
  • cargo build
  • cargo run

As a JavaScript developer, I feel right at home with this. It feels like I'm working with npm scripts in a package.json file.

Hello World

Let's finally create a Rust project. Run the following command to initialize a new package.

cargo new hello_world

Cargo new in VSCode

This will create our folder structure for the rust package, and create two important files:

  • Cargo.toml
  • main.rs

Think of Cargo.toml as Rust's version of package.json. The main file we're interested in, however, is the main.rs file. This file contains the main function- the starting point in all Rust programs.

fn main() {
    println!("Hello, world!");
}

In the span of a few seconds, we've already created our Hello World program. This is a great foundation with which we can begin diving deeper into Rust fundamentals.

Types

One of the tools in the JavaScript ecosystem that I fell in love with is TypeScript. There was recently a time I worked on a time-sensitive project, and there just was NOT the time to test the application as much as I wanted to.

I had chosen to use TypeScript instead of JavaScript to write out the front-end, and it caught SO many errors as I typed out my code. I felt confident that I understood the structure of all of my data and overall, I felt more confident in the codebase with it being strongly typed.

Here are some simple types from TypeScript:

const anInt: number = 17;
const aString: string = "Hello";
const aBoolean: boolean = true;
const anArray: number[] = [1, 2, 3, 4, 5];

The typing system of Rust feels just like writing TypeScript code. Here is a similar declaration of variables, but this time, in Rust.

let _an_int: i32 = 32;
let _a_string: &str = "Hello, world!";
let _a_bool: bool = true;
let _an_array: [i32; 3] = [1, 2, 3];

Writing this code gives me the same feel of writing TypeScript, a huge plus considering how much I love using it.

Structs and Enums

Let's check out a more complicated type. Let's say I wanted to declare the shape of an object representing a person. Here's an example of how I would implement that object type in TypeScript.

export interface Person {
    name: string;
    age: number;
    hobby: string;
    job_title: string;
}

Here is how I could implement a similar object using Rust structs.

#[derive(Debug)]
pub struct Person {
    pub name: String,
    pub age: i32,
    pub hobby: String,
    pub job_title: String,
}

Again, this gives me a very similar experience to writing TypeScript.

Let's instantiate an object of the Person type using Rust, and print it to standard output.

let me: Person = Person {
    name: "Matthew Pagan".to_string(),
    age: 32,
    hobby: "Coding".to_string(),
    job_title: "Software Developer".to_string(),
};

println!("{:?}", me);

Rust Development Environment

This is Rust, yet it's so similar to what I already use on a day-to-day basis. I love TypeScript, and Rust scratches that itch.

Let's implement a favorite color field to the Person struct, and restrict available colors by implementing a Color enum for that type.

Adding Color Enum

Options and Matching

Rust really starts to shine when we start working with logic that involves optional values, and how to handle optional data.

Let's expand our Person type. Here is how our Person type is currently defined:

use super::Color;

#[derive(Debug)]
pub struct Person {
    pub name: String,
    pub age: i32,
    pub hobby: String,
    pub job_title: String,
    pub favorite_color: Color,
}

Not all Person's have jobs. Maybe some of those objects represent a child who may not be old enough to enter the workforce, or someone older who has already retired.

TypeScript has one way of dealing with this scenario, using optional properties.

Here is how we could implement an optional job_title in a Person interface in TypeScript.

export interface Person {
    name: string;
    age: number;
    hobby: string;
    job_title?: string;
    favorite_color: Color;
}

In the above implementation, TypeScript says that job_title is either a string or null. Rust's solution is brilliantly elegant. There is no concept of null. Instead, we'll use the Option enum to handle the implementation.

Here is how we would define Person with an optional job_title.

pub struct Person {
    pub name: String,
    pub age: i32,
    pub hobby: String,
    pub job_title: Option<String>,
    pub favorite_color: Color,
}

Now, we can write logic that depends on the state of the job_title.

let me: Person = Person {
    name: "Matthew Pagan".to_string(),
    age: 32,
    hobby: "Coding".to_string(),
    job_title: Some("Software Developer".to_string()),
    favorite_color: Color::Cyan,
};

let noah: Person = Person {
    name: "Noah Pagan".to_string(),
    age: 5,
    hobby: "Lego".to_string(),
    job_title: None,
    favorite_color: Color::Blue,
};

match me.job_title {
    Some(title) => println!("{} is a {}", me.name, title),
    None => println!("{} doesn't have a job", me.name),
}

match noah.job_title {
    Some(title) => println!("{} is a {}", noah.name, title),
    None => println!("{} doesn't have a job", noah.name),
}

Option Enum

Option<T> enums can either be 1 of 2 variants

  • Some<T>: Represent the presence of a value, of type T
  • None: Represents the lack of a value

Using match to perform actions, depending on the value of an enum, is one of the most common patterns I've experienced diving into the world of Rust, and provides an effective way for handling control flow with enums.

Functions

Like other languages, we can encapsulate repeated logic in functions. We're repeating our logic when we're handling what to print depending on the value of job_title. Let's create a function that takes in a Person, and logs to standard out the correct message.

fn handle_job_title(person: Person) -> () {
    match person.job_title {
        Some(title) => println!("{} is a {}", person.name, title),
        None => println!("{} doesn't have a job", person.name),
    }
}

let me: Person = Person {
    name: "Matthew Pagan".to_string(),
    age: 32,
    hobby: "Coding".to_string(),
    job_title: Some("Software Developer".to_string()),
    favorite_color: Color::Cyan,
};

let noah: Person = Person {
    name: "Noah Pagan".to_string(),
    age: 5,
    hobby: "Lego".to_string(),
    job_title: None,
    favorite_color: Color::Blue,
};

handle_job_title(me);
handle_job_title(noah);

I even get some nice IntelliSense for everything so far using VSCode if I import this function from a module.

Function Intellisense

Arrays and Ownership

We can clean up this code even more. We are declaring and initializing 2 Person objects. We could store this in an Array, and then iterate through that Array, running the function on each iteration.

Here is an example of how to implement this using Rust.

let people: [Person; 2] = [
    Person {
        name: "Matthew Pagan".to_string(),
        age: 32,
        hobby: "Coding".to_string(),
        job_title: Some("Software Developer".to_string()),
        favorite_color: Color::Cyan,
    },
    Person {
        name: "Noah Pagan".to_string(),
        age: 5,
        hobby: "Lego".to_string(),
        job_title: None,
        favorite_color: Color::Blue,
    },
];

for person in people.iter() {
    handle_job_title(person);
}

Array Implementation

To get this working though, we need to make some minor changes to our function implementation, and instruct the function to borrow the Person argument by adding a & in front of the argument type.

use super::super::types::Person;

pub fn handle_job_title(person: &Person) -> () {
    match &person.job_title {
        Some(title) => println!("{} is a {}", person.name, title),
        None => println!("{} doesn't have a job", person.name),
    }
}

This broaches a core Rust feature, the subject of Ownership. Ownership has 3 basic rules:

  1. Each value in Rust has a variable that’s called its owner.
  2. There can only be one owner at a time.
  3. When the owner goes out of scope, the value will be dropped.

Let's create a pair of examples to show the ideas of Ownership.

fn add_numbers(x: i32, y: i32) -> i32 {
    x + y
}

let number = 3;

println!("{} + {} = {}", 1, x, add_numbers(1, number));
println!("{} + {} = {}", 1, x, add_numbers(5, number));

This works just as we would expect it to work, printing out to console the results of both function calls. Because integers are simple values with a known, fixed size, this works by pushing a copy of the i32 integer onto the stack for the value of the function argument.

Because we're making copies of the integer number onto the stack, we adhere to the rules of Ownership and number is the sole owner of its copy of the integer 3.

Let's try this with a function that takes a String.

fn print_string(x: String) {
    println!("{}", x);
}

let name: String = String::from("Matthew Pagan");

print_string(name);
print_string(name);

This does NOT work. VS Code throws an error right away when we try this on the second call of the print_string function.

Ownership Error

The error says that we used a moved value. If we look at the third rule of Ownership, it states, "When the owner goes out of scope, the value will be dropped."

Unlike integers, the String type is a growable, mutable, owned, UTF-8 encoded string type. Unlike the scalar integer, a `String' is pushed onto the heap, a much more expensive place to keep memory.

Because of this expense, the Rust compiler doesn't just copy the value into the function argument. Instead, the function arg becomes the new owner of that data.

To repeat, when we called print_string, we left the scope where we declared the name String, and the function argument x became the new owner of the value "Matthew Pagan". After the function logged my name, and we left its scope, name no longer was the owner of the data, throwing an error when we tried to use it again.

To fix this, we need to tell the function to only borrow the value of the String, and to return ownership of that value to the name variable once we leave the function scope. We do that by pre-pending a & in front of the function argument type. Below is the implementation to fix our move error.

fn print_string(x: &String) {
    println!("{}", x);
}

let name = String::from("Matthew Pagan");
print_string(&name);
print_string(&name);

Conclusion

Rust seems like it took the best parts of the JavaScript ecosystem, and supercharged them into a feature-packed programming language, purpose-built to get developers into systems programming. I will heavily admit that it has certainly worked with me and has drawn an unhealthy amount of my attention.

There remains so much more left to do with our growing knowledge of Rust. In my next blog post, we'll explore how to build on this foundation of knowledge, and start working with external Rust packages (crates) to create a webserver to handle the back-end role of a full-stack application.

Until then, install if you haven't done so yet, install Rust and start playing around with it today! It's seriously the easiest way to get into lower-level programming, especially if you're from the world of web development, like me.

About the author

Matthew Pagan

Matthew Pagan

Software Engineer

Keep reading

View all posts →

This Dot AI Field Notes - Anatomy of a Coding Harness

A coding agent is not magic, it’s a loop. We call this a harness. The harness is a deterministic layer of code that wraps an LLM....

1 min
AI

AI Is Speeding Up Development. But Where Are the New Bottlenecks?

AI is accelerating development, but it’s also exposing everything else that’s broken. At the Leadership Exchange, leaders unpacked how AI is reshaping the SDLC and what organizations need to address beyond just coding to make adoption successful. Moderated by Rob Ocel, VP of Innovation at This Dot Labs, the panel featured Itai Gerchikov at Anthropic and Harald Kirschner, Principal Product Manager for GitHub Copilot & VS Code at Microsoft. Panelists explored the current state of AI adoption across the software development lifecycle and shared practical insights into how organizations can effectively integrate AI tools. Panelists discussed how companies are investing in AI tools, skills, and managed competency programs to support developers. While AI can dramatically accelerate coding, the panel emphasized that adoption affects every stage of the SDLC. Bottlenecks now appear in testing, DevOps, product delivery, and marketing as AI speeds up development. Organizations that address technical debt and process inefficiencies are better positioned to extract maximum value from AI tools. The conversation also focused on opportunities and risks. Security, governance, and workforce education were highlighted as critical factors for adoption. Panelists stressed that AI initiatives should be aligned with broader business goals rather than pursued in isolation. They noted that companies experimenting at the cutting edge need to consider organizational readiness just as carefully as technical capabilities. Panelists also explored how leading organizations are navigating the early stages of adoption. Those ahead of the curve are using structured experimentation, prioritizing process improvements, and continuously evaluating outcomes to refine their AI strategies. Learning from these early adopters allows other organizations to anticipate emerging trends and prepare for the next phase of AI adoption rather than simply replicating past approaches. Key Takeaways Investing in AI skills and tools should be done thoughtfully, with clear alignment to business objectives. Examining the full SDLC helps identify bottlenecks that AI may accelerate or expose. Organizations can gain a competitive advantage by learning from early adopters and planning for where AI adoption is heading. AI adoption is not just a technical initiative; it is a strategic transformation that requires attention to people, process, and technology. Organizations that balance innovation with operational discipline will be best positioned to capture the full potential of AI across the software lifecycle. Seeing similar challenges in your own SDLC? Let’s compare notes. Join us at an upcoming Leadership Exchange or reach out to continue the conversation. Tracy can be reached at tlee@thisdot.co....

Calypso Hernandez2 mins
AI AdoptionAILeadership ExchangeEngineering Leadership

Making AI Deliver: From Pilots to Measurable Business Impact

A lot of organizations have experimented with AI, but far fewer are seeing real business results. At the Leadership Exchange, this panel focused on what it actually takes to move beyond experimentation and turn AI into measurable ROI. Over the past few years, many organizations have experimented with AI, but the challenge today is translating experimentation into measurable business value. Moderated by Tracy Lee, CEO at This Dot Labs, panelists featured Dorren Schmitt, Vice President IT Strategy & Innovation at Allen Media Group, Greg Geodakyan, CTO at Client Command, and Elliott Fouts, CAIO & CTO at This Dot Labs. Panelists discussed how companies are moving from early AI experiments to initiatives that deliver real results. They began by examining how experimentation has evolved over the past year. While many organizations did not fully utilize AI experimentation budgets in 2025, 2026 is showing a shift toward more intentional investment. Structured budgets and clearly defined frameworks are enabling companies to explore AI strategically and identify initiatives with high potential impact. The conversation then turned to alignment and ROI. Panelists highlighted the importance of connecting AI projects to corporate strategy and leadership priorities. Ensuring that AI initiatives translate into operational efficiency, productivity gains, and measurable business impact is essential. Companies that successfully align AI efforts with organizational goals are better equipped to demonstrate tangible outcomes from their investments. Moving from pilots and proofs of concept to production was another major focus. Governance, prioritization, and workflow integration were cited as essential for scaling AI initiatives. One panelist shared that out of nine proofs of concept, eight successfully launched, resulting in improvements in quality and operational efficiency. Panelists also explored the future of AI within organizations, including the potential for agentic workflows and reduced human in the loop processes. New capabilities are emerging that extend beyond coding tasks, reshaping how teams collaborate and how work is structured across departments. Key Takeaways Structured experimentation and defined budgets allow organizations to explore AI strategically and safely. Alignment with business priorities is essential for translating AI capabilities into measurable outcomes. Governance and workflow integration are critical to moving AI initiatives from pilot stages to production deployment. Successfully leveraging AI requires a balance between experimentation, strategic alignment, and operational discipline. Organizations that approach AI as a structured, measurable initiative can capture meaningful results and unlock new opportunities for innovation. Curious how your organization can move from AI experimentation to real impact? Let’s talk. Reach out to continue the conversation or join us at an upcoming Leadership Exchange. Tracy can be reached at tlee@thisdot.co....

Calypso Hernandez2 mins
AI AdoptionAILeadership ExchangeEngineering Leadership

What does it actually look like to build software with AI today? Not in theory, but in practice.

What does it actually look like to build software with AI today? Not in theory, but in practice. At the Leadership Exchange, this was the question at the center of the Developer Panel, where leaders from across the industry unpacked what’s really changing inside engineering teams and what organizations need to do right now to keep up. The Developer Panel at the Leadership Exchange explored the cutting edge of AI in software engineering and examined what organizations should focus on today to prepare for the future. Moderated by Jeff Cross, Co Founder & CEO at Nx, the panel featured Victor Savkin, Cofounder & CTO at Nx, Alex Sover, Vice President of Engineering at OpenAP, Brent Zucker, Senior Director of Engineering at Visa, and Jonathan Fontanez, AI Engineering Lead at This Dot Labs. Panelists shared insights into how AI is transforming the software development lifecycle and how teams can adopt tools effectively while preparing for organizational change. Panelists discussed emerging workflows, including CI in the loop, agentic healing, and context engineering. They examined how validation, code reviews, and PRDs are evolving alongside AI capabilities and how teams are integrating external sources such as production traces to improve quality and reliability. The discussion also covered what the next generation of agentic tools might look like and how these capabilities will shape engineering practices in the near future. Adoption of AI comes with challenges. Teams often rely on plugins or extensions without foundational understanding, and individual contributors may fear displacement. Panelists emphasized that education, governance, and skill building are essential for teams to manage AI agents effectively while maintaining quality. They also highlighted the need to standardize workflows and ensure organizational alignment to fully leverage AI capabilities. The conversation extended beyond technical challenges to organizational implications. Panelists discussed how teams can avoid issues like Conway’s Law, manage distributed teams effectively, and evolve engineering practices alongside AI adoption. Leadership and management strategies play a crucial role in ensuring that AI integration delivers meaningful outcomes while maintaining efficiency and alignment with business objectives. Key Takeaways AI workflows require both technical and organizational preparation. Education, governance, and skill development are essential for successful implementation. Forward looking teams are rethinking validation, CI pipelines, and context management to fully leverage agentic AI. The discussion highlighted that adopting AI at the cutting edge is not just about new tools it is about rethinking processes, workflows, and organizational culture. Companies that embrace this holistic approach are most likely to succeed in leveraging AI to its full potential. Are you interested in more conversations like this? Message us for an invite to the next, or for a private discussion around these topics. Tracy can be reached at tlee@thisdot.co....

Calypso Hernandez2 mins
AI AdoptionAILeadership ExchangeEngineering Leadership