Question
In Rust, what is the difference between self and Self?
I have seen Self used in source code, but most documentation examples use self. I want to understand what each one means, where they are used, and when I should choose one over the other.
Short Answer
By the end of this page, you will understand that self and Self are different concepts in Rust. self usually refers to the current instance inside a method, while Self refers to the current type inside an impl block or trait. You will see where each is used, how their syntax differs, and how they appear in real Rust code.
Concept
In Rust, self and Self are related, but they mean different things.
selfrefers to the current value or instance of a type.Selfrefers to the type itself.
This distinction matters because Rust makes a clear separation between:
- a value created from a type
- the type definition itself
self means the current instance
You use self in methods when you want to work with the object the method was called on.
For example:
struct User {
name: String,
}
impl User {
fn greet(&self) {
println!("Hello, {}", self.name);
}
}
Here:
&selfmeans the method borrows the currentUserself.nameaccesses a field on that specific instance
Self means the current type
You use Self when you want to refer to the type currently being implemented.
struct User {
name: String,
}
impl User {
fn new(name: String) -> Self {
Self { name }
}
}
Here:
-> Selfmeans the function returns aUserSelf { name }constructs aUservalue
Inside impl User, Self is another way to write User.
Why Rust has both
Rust uses both because they solve different problems:
selfhelps you work with the current object instanceSelfhelps you write reusable, type-based code
This is especially useful in traits, where the exact implementing type may not be known in advance.
trait Animal {
fn create() -> Self;
}
In this trait, Self means "whatever type implements this trait." That would not be possible if Rust required a concrete type name here.
Mental Model
Think of a class blueprint and a built object.
Selfis the blueprint name: the type itselfselfis the actual object in your hands
If you build a Car:
SelfmeansCarselfmeans a specific car, such as your blue car parked outside
So:
- use
Selfwhen talking about what kind of thing it is - use
selfwhen talking about the current thing itself
Syntax and Examples
Core syntax
Using self in methods
struct Counter {
value: i32,
}
impl Counter {
fn show(&self) {
println!("{}", self.value);
}
fn increment(&mut self) {
self.value += 1;
}
fn consume(self) {
println!("Final value: {}", self.value);
}
}
What these mean
&selfborrows the current instance immutably&mut selfborrows the current instance mutablyselftakes ownership of the current instance
Using Self in return types and constructors
struct Counter {
value: i32,
}
impl Counter {
fn new() -> Self {
Self { value: 0 }
}
}
This is equivalent to:
impl Counter {
fn new() -> Counter {
Counter { value: 0 }
}
}
Using Self is often preferred because it is shorter and still correct if the type name changes later.
Trait example
trait Resettable {
fn reset() -> Self;
}
struct Counter {
value: i32,
}
impl Resettable for Counter {
fn reset() -> Self {
Self { value: 0 }
}
}
Here Self means the implementing type, which is Counter in this implementation.
Step by Step Execution
Consider this example:
struct Book {
title: String,
}
impl Book {
fn new(title: String) -> Self {
Self { title }
}
fn print_title(&self) {
println!("{}", self.title);
}
}
fn main() {
let book = Book::new(String::from("Rust Basics"));
book.print_title();
}
Step by step:
struct Bookdefines a type namedBook.- Inside
impl Book,Selfrefers to the typeBook. fn new(title: String) -> Selfsays thatnewreturns aBook.Self { title }creates aBookvalue using the provided title.- In
main,Book::new(...)creates a newBookinstance. - The variable
booknow holds that instance. book.print_title()calls a method on that instance.- Inside
print_title,&selfis a reference to thebookinstance. self.titlereads thetitlefield from that specificBook.- The program prints
Rust Basics.
The key idea is:
Selfwas used when referring to the type being createdselfwas used when referring to the specific value being used
Real World Use Cases
You will commonly see self and Self in these situations:
Struct methods
impl Config {
fn validate(&self) -> bool {
!self.path.is_empty()
}
}
Use self because the method checks data on the current instance.
Constructors and factory methods
impl Config {
fn default() -> Self {
Self {
path: String::from("./app.conf"),
}
}
}
Use Self because the function returns a new value of the type.
Trait definitions
trait Parseable {
fn parse(input: &str) -> Self;
}
Use Self because the trait does not know in advance which concrete type will implement it.
Builder-style APIs
impl Query {
fn limit(mut self, amount: usize) -> Self {
self.limit = amount;
self
}
}
Here both appear:
selfis the current value being modifiedSelfis the return type
This is very common in fluent APIs.
Real Codebase Usage
In real Rust projects, developers use self and Self in predictable patterns.
Instance methods
Methods that inspect or modify fields usually take one of these forms:
&selffor read-only access&mut selffor modificationselfwhen consuming the value
impl Session {
fn is_expired(&self) -> bool {
self.expires_at < 1000
}
fn refresh(&mut self) {
self.expires_at += 3600;
}
}
Constructors and named constructors
impl Session {
fn new() -> Self {
Self { expires_at: 0 }
}
}
This keeps code easier to refactor than repeating the type name.
Trait-based abstractions
Self is heavily used in traits for return values and associated behavior.
trait Serializable {
fn from_bytes(bytes: &[u8]) -> Self;
}
Method chaining
Developers often return Self from methods that transform values.
impl Options {
fn enable_logs(mut self) -> Self {
self.logs = true;
self
}
}
Error handling and validation
self is often used with guard clauses to check the current state.
impl Request {
fn send(&self) -> Result<(), String> {
if self.url.is_empty() {
return Err(String::from("URL cannot be empty"));
}
Ok(())
}
}
Common Mistakes
1. Thinking self and Self are interchangeable
They are not.
selfis a valueSelfis a type
Broken example:
struct User;
impl User {
fn make() -> self {
self
}
}
This is invalid because self is not a type.
Correct version:
struct User;
impl User {
fn make() -> Self {
Self
}
}
2. Using self outside a method receiver context incorrectly
Broken example:
struct User {
name: String,
}
impl User {
fn print_name() {
println!("{}", self.name);
}
}
This fails because there is no self parameter.
Correct version:
impl User {
fn print_name(&self) {
println!("{}", self.name);
}
}
3. Forgetting that Self depends on context
Inside impl User, Self means User.
Inside impl Trait for User, Self also means User.
Inside a trait definition, Self means the unknown implementing type.
4. Confusing self with ownership rules
These are different:
fn read(&self)
fn change(&mut self)
fn take(self)
The difference here is not about self versus Self, but about how the instance is passed.
5. Overusing the concrete type name instead of Self
This is valid:
impl User {
fn new() -> User {
User
}
}
But this is often more maintainable:
impl User {
fn new() -> Self {
Self
}
}
If the type name changes later, fewer places need updating.
Comparisons
| Concept | Meaning | Used for | Example |
|---|---|---|---|
self | The current instance/value | Accessing fields and calling instance logic | self.name |
Self | The current type | Return types, constructors, trait definitions | fn new() -> Self |
self receiver forms
| Syntax | Meaning |
|---|---|
&self | Borrow the current instance immutably |
&mut self | Borrow the current instance mutably |
self | Take ownership of the current instance |
Self vs concrete type name
| Option | Example | When to use |
|---|---|---|
Self | fn new() -> Self | Preferred inside impl and traits |
| Concrete type | fn new() -> User | Sometimes used for clarity, but usually less flexible |
Quick rule
- If you mean the object/value, use
self - If you mean the type, use
Self
Cheat Sheet
self= the current instanceSelf= the current type
Common patterns
fn show(&self)
fn update(&mut self)
fn consume(self)
fn new() -> Self
Examples
impl User {
fn new(name: String) -> Self {
Self { name }
}
fn greet(&self) {
println!("Hi, {}", self.name);
}
}
Remember
self.nameaccesses a field on the current valueSelf { ... }constructs a value of the current type- In traits,
Selfmeans "the type that implements this trait" selfcannot be used as a type nameSelfcannot access instance fields directly
Fast decision rule
Ask yourself:
- Am I talking about this object? Use
self - Am I talking about this type? Use
Self
FAQ
What does self mean in Rust?
self refers to the current instance of a type inside a method. It lets you read or modify that specific value.
What does Self mean in Rust?
Self refers to the current type inside an impl block or trait. It is often used in return types and constructors.
Is Self the same as the struct name in Rust?
Inside an impl block, yes. For example, inside impl User, Self means User.
Why does Rust use both self and Self?
Because one is for values and the other is for types. Rust keeps those ideas separate to make code clearer and safer.
When should I use Self instead of the type name?
Usually inside impl blocks and traits, especially for constructors and return types. It makes code easier to refactor.
Can I use self in a function that is not a method?
No. self is only available when it is declared as a method receiver, such as &self, &mut self, or self.
Can a method use both self and Self?
Yes.
impl Item {
fn duplicate(&self) -> Self {
Self {
name: self.name.clone(),
}
}
}
Here self is the current value, and Self is the return type.
Mini Project
Description
Create a small Rust program that models a Rectangle type. This project demonstrates both self and Self in a practical way: Self will be used to create rectangles, and self will be used to inspect and modify a specific rectangle instance.
Goal
Build a Rectangle type with methods that create, read, and update rectangle values using both Self and self correctly.
Requirements
- Define a
Rectanglestruct withwidthandheightfields. - Add a constructor that returns
Self. - Add a method that calculates area using
&self. - Add a method that resizes the rectangle using
&mut self. - Print the rectangle area before and after resizing.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.