← Back to blog

lars Learning Series – Part 3: 3D Vectors in Rust

01 Feb 2026
RustLinear AlgebraLars

Building a 3D Vector Type in Rust


Introduction

In the last part of this series, you built a Vec2 type from scratch. You learned how to represent directions and positions in 2D space. You also added the vector operations you need most: addition, subtraction, scaling, length, normalization, and the dot product. Along the way, you practiced Rust operator traits and wrote unit tests to keep your code correct.

Now you’ll build a 3D vector.

A 3D vector works like a 2D vector, but you add one more component: z. That simple change makes everything alot more powerful. You can now represent real space, not just a flat plane. You can describe movement forward and backward, not only left and right, up and down.

In this guide, you’ll build Vec3 for the lars crate. You’ll implement the same core operations you already know from Vec2. Then you’ll add one operation that only makes sense in 3D: the cross product. It gives you a vector that points perpendicular to two others. You’ll use it later for things like surface normals and orientation.

When you finish, you’ll have a clean, tested Vec3 type you can build on in later parts.


Learning Goals

By the end of this lesson, students will:

  • Understand 3D vectors mathematically and how they extend 2D vectors.
  • Implement a Vec3 struct in Rust with (x, y, z) components.
  • Support operations like addition, subtraction, dot and cross product.
  • Impliment normalization and other utility methods.
  • Write unit tests covering all methods and edge cases.

Recap

In Part 2, you built Vec2, a simple 2D vector type. You used it to represent positions and directions on a flat plane.

You also implemented the basics you need for vector maths. You added addition and subtraction. You added scalar multiply and divide. You wrote functions to calculate length and normalize vectors. You implemented the dot product.

Most importantly, you practiced the workflow you’ll keep using in this series. You wrote clean Rust code. You used operator traits to make your maths feel natural. You wrote unit tests to prove your implementation works.


Background: What is a Vector in 3D

Okay, so the same principles we discussed in the last part of the series also apply in 3D, If you haven't worked through the last part, you can either find it here or you can find a brilliant introduction to 2D vectors here.

The key difference between Vectors in 2D and 3D is simply that in 2D a vector has 2 components whereas in 3D, we must account for the extra possible direction with a third component "z".

v=[xyz]\mathbf v = \begin{bmatrix} x \\ y \\ z \end{bmatrix}

Equivalently, we can notate a 3D vector using the unit vectors i^\hat i, j^\hat j and k^\hat k

Example 1

v=[345]=3i^+4j^+5k^\mathbf v = \begin{bmatrix} 3 \\ 4 \\ 5 \end{bmatrix} = 3\hat i + 4\hat j + 5\hat k

Visual Intuition: But what does it look like?

This is a brilliant question. To me, the simplest way to understand the components visually is to picture yourself as a fly a room.
Firstly, you can move left/right, this represents the x offset (the coefficient of i^\hat i). Next, you can move up and down (the coefficient of j^\hat j).
Now this is the new bit: in 3D, we can also move deeper into the room, this is a represented mathematically as a the value in the coefficient of k^\hat k.
Oh, and to be clear: the coefficient is just the number before the variable (or vector in this case), such as the 3, 4 and 5 in example 1.


Magnitude in 3D

As we discussed in the previous part, the Magnitude (or length) of a vector is the distance from the origin to the point defined by the vector, below we have a recap of this concept in 2 dimensions in the form of a diagram

v|v| = 5.00vx = 3.00vy = 4.00
Diagram showing vector magnitude in 2D.

With 2 dimensions, we use the handy pythagorean theorem to calculate the magnitude, in 3D, we must undertake a similar process.

v=x2+y2+z2|\mathbf v| = \sqrt{x^2 + y^2 +z^2}

The Dot Product in 3D

The dot product in 3D works the same way it did in 2D. It takes two vectors and gives you back a single number (a scalar).

If you have two vectors aa and bb, the dot product looks like this:

ab=axbx+ayby+azbz\mathbf{a} \cdot \mathbf{b} = a_x b_x + a_y b_y + a_z b_z

So you simply multiply matching components and add them up.

The dot product tells you how to what extent two vectors point in the same direction. If the result is positive, they point mostly the same way. If it’s negative, they point in opposite directions. If it’s zero, they’re perpendicular.

You’ll use this a lot. It helps you check angles, test if vectors are perpendicular, and project one vector onto another. In 3D graphics and physics, it shows up everywhere.


A new challenger approaches: The Cross Product

The cross product is where 3D starts to feel different. Unlike the dot product, it doesn’t give you a number. It gives you a new vector.

If you take two vectors a\mathbf{a} and b\mathbf{b}, the cross product returns a vector that points perpendicular to both of them.

Here’s the formula:

a×b=[aybzazbyazbxaxbzaxbyaybx]\mathbf{a} \times \mathbf{b} = \begin{bmatrix} a_y b_z - a_z b_y \\ a_z b_x - a_x b_z \\ a_x b_y - a_y b_x \end{bmatrix}

Admittedly this looks a bit scary at first, but it’s just a pattern. You combine the components in a fixed order to get the new xx, yy, and zz values.

The direction of the result depends on the order. a×b\mathbf{a} \times \mathbf{b} points the opposite way of b×a\mathbf{b} \times \mathbf{a}.

You can use the right hand rule to remember this. Point your index finger in the direction of a\mathbf{a}, your middle finger in the direction of b\mathbf{b}, and your thumb shows the direction of the cross product.

You’ll use the cross product a lot in 3D work. It helps you compute surface normals, build coordinate systems, and figure out which way something is facing.


A handy tool: Normalization

Normalization is one of those things you’ll use all the time. It takes a vector and scales it so its length becomes 1.

When you normalize a vector, you keep its direction but throw away its magnitude. That’s useful when you only care about where something points, not how long the vector is.

To normalize a vector v\mathbf{v}, you divide it by its length:

normalize(v)=vv\text{normalize}(\mathbf{v}) = \frac{\mathbf{v}}{\|\mathbf{v}\|}

You can also write it like this:

v^=vv\hat{\mathbf{v}} = \frac{\mathbf{v}}{\|\mathbf{v}\|}

There’s one catch. You can’t normalize the zero vector. If v=(0,0,0)\mathbf{v} = (0,0,0), then v=0\|\mathbf{v}\| = 0, and dividing by zero breaks everything. So in code, you always need to handle that case.

In practice, normalization shows up everywhere. You’ll use it for directions, surface normals, camera movement, lighting, and lots of physics math.


Summary

Overall, 3D vectors work just like 2D vectors, but you add a third component, zz. That lets you represent real space instead of a flat plane.

You can still add, subtract, and scale vectors the same way. You can still compute length and normalize vectors. The dot product still gives you a number that tells you how closely two vectors point in the same direction.

The big new idea in 3D is the cross product. It gives you a vector perpendicular to two others. You’ll use it constantly in graphics and physics.

With that background out of the way, you’re ready to start building Vec3 in Rust.


Task 1: Defining a 3D Vector Type

Task 1

John Doe

JayCee

JayCee is a UK-based developer, hardware hacker, student and musician

Visit website