---
title: "10.6 Associated functions and constructors"
description: "Associated functions belong to a type without taking self; ::new is a convention not a keyword, and Default supplies sensible starting values."
url: "https://learnrust.net/chapter-10/associated-functions-and-constructors/"
last_updated: "2026-06-13"
---

# 10.6 Associated functions and constructors

A method needs an instance to operate on, which raises a chicken-and-egg question: how do you make the first one? In many languages the answer is a *constructor*, a special language feature with its own rules. Rust doesn't have constructors. It has something simpler that you already understand: ordinary functions that live inside an `impl` block but don't take `self`.

## Associated functions

A function defined in an `impl` block that has no `self` parameter is an **associated function**. It's associated with the type but doesn't operate on a particular instance, so you call it on the *type* with `::`, not on a value with `.`:

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

impl Rectangle {
    fn square(side: f64) -> Rectangle {
        Rectangle { width: side, height: side }
    }
}

fn main() {
    let sq = Rectangle::square(5.0);
    println!("{} x {}", sq.width, sq.height);
}
```

```
5 x 5
```

`Rectangle::square(5.0)` reads "call the `square` function that belongs to `Rectangle`." You've used this syntax for ages without a name for it. `String::from("hi")` and `String::new()` are associated functions on `String`; so is `rand::random_range` in spirit. The `::` is the same path separator that joins a type to the things defined on it.

The line between a method and an associated function is exactly the `self` parameter. Has `self`: it's a method, called with `value.method()`. No `self`: it's an associated function, called with `Type::function()`. Both live in `impl` blocks; both belong to the type.

## ::new is a convention, not a keyword

The most common associated function builds and returns an instance, and Rust's convention is to call it `new`:

```rust
impl Rectangle {
    fn new(width: f64, height: f64) -> Rectangle {
        Rectangle { width, height }
    }
}

fn main() {
    let r = Rectangle::new(4.0, 2.5);
    println!("area: {}", r.width * r.height);
}
```

```
area: 10
```

Here's the thing worth internalizing: `new` is not special. It is not a keyword, the compiler has never heard of it, and nothing forces it to return `Self` or to take any particular arguments. It's a plain associated function that programmers agreed to name `new` when its job is "make a fresh instance." You could call it `make` or `build` and it would work identically; you call it `new` so that every other Rust programmer knows what it does at a glance.

This is the chapter's biggest entry in the C++ bloat ledger. Constructors in C++ are a language feature with their own chapter-sized rulebook: initialization lists, default constructors, copy constructors, converting constructors, delegating constructors, and the order they all run in. Rust deletes the entire feature. A constructor is just a function you write, named by convention, and you've known how to write functions since chapter 2.

> **Author's note**
>
> The first time I reached for a constructor in Rust and found there wasn't one, I assumed I was missing something. I wasn't. `::new` really is just a function that returns `Self`, and the relief of not having seventeen lessons of initialization rules to learn is hard to overstate. When you see `Vec::new()`, `String::new()`, `HashMap::new()` later, they're all the same humble pattern: an associated function that hands you an empty one.

> **Best practice**
>
> Name the primary constructor `new`. If a type has several ways to be built, name the others descriptively: `Rectangle::square`, `String::from`, `Vec::with_capacity`. The `with_` and `from_` prefixes are common conventions for "build one, given this." Readers lean on these names heavily, so follow them.

## Default values

Sometimes "a fresh instance" means "all fields at their natural starting value." Rather than write a `new` that fills in zeros, Rust offers a standard piece of behavior for exactly this, called `Default`, which you opt into with a derive (the same `#[derive(...)]` machinery the next lesson covers for printing):

```rust
#[derive(Default)]
struct Settings {
    volume: u32,
    muted: bool,
    brightness: u32,
}

fn main() {
    let s = Settings::default();
    println!("volume {}, muted {}, brightness {}", s.volume, s.muted, s.brightness);
}
```

```
volume 0, muted false, brightness 0
```

`#[derive(Default)]` asks the compiler to write a `default()` associated function that fills every field with *its* default: `0` for numbers, `false` for `bool`, an empty `String` for `String`. This pairs beautifully with struct update syntax from lesson [10.3](https://learnrust.net/chapter-10/field-init-and-update-syntax/): start from the default, override only what you care about.

```rust
let quiet = Settings { muted: true, ..Default::default() };
```

That reads "muted, and everything else at its default," which is often exactly the instance you want. We'll meet derives properly next lesson and the full story of what `Default` and `derive` really are in chapter 16; for now, treat `#[derive(Default)]` as "give me a `::default()` that zeroes everything out."

> **Key insight**
>
> Rust has no built-in "construct an object" step, so there's no half-built instance to worry about, no constructor that can throw partway through, and no rules about field initialization order to memorize. An instance springs into existence fully formed the moment you write `Type { ... }`, and `::new` is just a function that does that for you and hands it back. The lesson [1.4](https://learnrust.net/chapter-1/variables-mutability-initialization/) ban on uninitialized values holds all the way up to your own types.

## Quiz time

**Question #1**

Add an associated function `Point::origin()` that returns a `Point { x: 0.0, y: 0.0 }`, and call it.

<details class="solution">
<summary>Show solution</summary>

```rust
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn origin() -> Point {
        Point { x: 0.0, y: 0.0 }
    }
}

fn main() {
    let o = Point::origin();
    println!("({}, {})", o.x, o.y);
}
```

It takes no `self`, so it's an associated function, called `Point::origin()` with `::`. Prints `(0, 0)`.

</details>

**Question #2**

What distinguishes an associated function from a method, and how does the call site differ?

<details class="solution">
<summary>Show solution</summary>

A method takes `self` (in some form) as its first parameter and is called on an instance with a dot: `r.area()`. An associated function has no `self` and is called on the type with a path: `Rectangle::new(...)`. Both are defined in `impl` blocks.

</details>

**Question #3**

Is `new` a Rust keyword? What would happen if you named your constructor `create` instead?

<details class="solution">
<summary>Show solution</summary>

No, `new` is not a keyword; it's a plain associated function named by convention. Naming it `create` would compile and work identically. You name it `new` so other Rust programmers recognize the "make a fresh instance" function instantly. The convention is for humans, not the compiler.

</details>

You can now build instances and give them behavior. But try to `println!("{}", rectangle)` and the compiler stops you. The next lesson explains why, and how `#[derive(Debug)]` makes any struct printable for the price of one line.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
