Rust (self and super)
Java (this and super)
Difference between self vs &self vs &mut self
Different way to use self in rust
When SHOULD you use self (rule of thumb
Rust (self and super)
self refers to current module
super refers to parent module
Rust (Struct + self)
self = current object
&self = read-only borrow
&mut self = mutable borrow
Self (capital S) = current type (like class name)
mod a {
pub fn hello() { println!("a::hello"); }
pub mod b {
pub fn call_parent() {
super::hello(); // parent module a
self::inside_b(); // current module b
}
fn inside_b() {
println!("b::inside_b");
}
}
}
fn main() {
a::b::call_parent();
}
Output
a::hello
b::inside_b
Java (this and super)
this refers to current object
super refers to parent class
class Parent {
void hello() { System.out.println("Parent.hello"); }
}
class Child extends Parent {
void callParent() {
super.hello(); // parent class method
System.out.println(this.getClass().getSimpleName());
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.callParent();
}
}
Output
Parent.hello
Child
Difference between self vs &self vs &mut self
self vs &self vs &mut self (meaning)
self = current object by value (ownership move). Method call ke baad object usually usable nahi रहता (unless Copy).
&self = current object ka read-only borrow. Safe read, no change, object usable rehta hai.
&mut self = current object ka mutable borrow. You can change fields, but borrow ke दौरान same object ka koi aur borrow allowed nahi (safety).
&self (read-only) — when & why
✅ Use when: you only want to read data / compute something / print, without changing object.
#[derive(Debug)]
struct Wallet {
owner: String,
balance: i32,
}
impl Wallet {
fn show(&self) {
println!("Owner: {}, Balance: {}", self.owner, self.balance);
}
fn is_rich(&self) -> bool {
self.balance >= 1000
}
}
fn main() {
let w = Wallet { owner: "Ashwani".to_string(), balance: 1200 };
w.show();
println!("Rich? {}", w.is_rich());
w.show(); // still usable
}
Output
Owner: Ashwani, Balance: 1200
Rich? true
Owner: Ashwani, Balance: 1200
Why: no mutation needed, so &self is simplest + allows multiple readers.
B) &mut self (mutable) — when & why
✅ Use when: object ki state update करनी हो (increment, set, push, etc.)
struct Counter {
value: i32,
}
impl Counter {
fn inc(&mut self) {
self.value += 1;
println!("Incremented to {}", self.value);
}
fn add(&mut self, n: i32) {
self.value += n;
println!("Added {}, now {}", n, self.value);
}
}
fn main() {
let mut c = Counter { value: 0 };
c.inc();
c.add(5);
c.inc();
}
Output
Incremented to 1
Added 5, now 6
Incremented to 7
Why: mutation needs exclusive access, so Rust forces &mut self.
C) self (by value / move) — when & why
✅ Use when: method should consume the object (ownership take), often:
builder-style chaining
converting to another type
“close / finalize” type behavior
return a new modified object (without mutable borrow)
#[derive(Debug)]
struct User {
name: String,
active: bool,
}
impl User {
fn deactivate(self) -> Self {
// consumes self, returns new Self
Self { active: false, ..self }
}
}
fn main() {
let u1 = User { name: "Ashwani".to_string(), active: true };
let u2 = u1.deactivate();
println!("u2 = {:?}", u2);
// println!("{:?}", u1); // ❌ u1 moved (not usable)
}
Output
u2 = User { name: "Ashwani", active: false }
Why: you want to ensure old object cannot be used anymore (safety + intent).
Quick “when to use” cheat sheet
&self → read-only methods: show(), len(), to_string_view(), is_valid()
&mut self → state change methods: push(), set_*(), inc(), update()
self → consume/finish/transform: build(), into_*(), close(), deactivate(self)->Self
Bonus: same method name, different receivers (common pattern)
Builder pattern:
#[derive(Debug)]
struct Request {
url: String,
timeout: u64,
}
impl Request {
fn new(url: &str) -> Self {
Self { url: url.to_string(), timeout: 30 }
}
fn timeout(mut self, t: u64) -> Self {
// takes self, modifies locally, returns self (chainable)
self.timeout = t;
self
}
fn send(&self) {
println!("Sending to {} with timeout {}", self.url, self.timeout);
}
}
fn main() {
let r = Request::new("https://api.example.com").timeout(10);
r.send();
}
Output
Sending to https://api.example.com with timeout 10
Different way to use self in rust
Basic self (object is consumed)
Rust code
struct File {
name: String,
}
impl File {
fn close(self) {
println!("File {} closed", self.name);
}
}
fn main() {
let f = File { name: "data.txt".to_string() };
f.close();
// f.close(); ❌ not allowed, f is moved
}
Output
File data.txt closed
Why use self here?
✔ After closing a file, it should not be used again
✔ Rust enforces this at compile time
Example 2️⃣ self returning Self (consume & return)
Rust code
#[derive(Debug)]
struct User {
name: String,
active: bool,
}
impl User {
fn deactivate(self) -> Self {
Self {
active: false,
..self
}
}
}
fn main() {
let u1 = User {
name: "Ashwani".to_string(),
active: true,
};
let u2 = u1.deactivate();
println!("{:?}", u2);
// println!("{:?}", u1); ❌ u1 moved
}
Output
User { name: "Ashwani", active: false }
Why use self?
✔ You want to transform the object
✔ Old version should not exist anymore
Example 3️⃣ Builder / chaining style (self)
Rust code
#[derive(Debug)]
struct Request {
url: String,
timeout: u64,
}
impl Request {
fn new(url: &str) -> Self {
Self { url: url.to_string(), timeout: 30 }
}
fn timeout(self, t: u64) -> Self {
Self { timeout: t, ..self }
}
}
fn main() {
let r = Request::new("https://api.example.com")
.timeout(10);
println!("{:?}", r);
}
Output
Request { url: "https://api.example.com", timeout: 10 }
Why use self here?
✔ Enables method chaining
✔ Avoids mutable references
✔ Very common in Rust APIs
Example 4️⃣ self with enums (consume variant)
Rust code
enum Payment {
Cash,
Card(String),
}
impl Payment {
fn process(self) {
match self {
Payment::Cash => println!("Paid by cash"),
Payment::Card(num) => println!("Paid by card {}", num),
}
}
}
fn main() {
let p = Payment::Card("1234".to_string());
p.process();
// p.process(); ❌ moved
}
Output
Paid by card 1234
Why use self?
✔ Payment should be used once
✔ Prevents double processing
Example 5️⃣ self inside trait method
Rust code
trait Shutdown {
fn shutdown(self);
}
struct Server {
id: u32,
}
impl Shutdown for Server {
fn shutdown(self) {
println!("Server {} stopped", self.id);
}
}
fn main() {
let s = Server { id: 1 };
s.shutdown();
// s.shutdown(); ❌ cannot reuse
}
Output
Server 1 stopped
Why use self?
✔ Trait enforces one-time action
✔ Safe resource handling
When SHOULD you use self (rule of thumb)
Use self when:
Object must be consumed
One-time action (close, shutdown, process)
Builder / chaining pattern
Transform object into another object
Prevent reuse bugs
self in Rust means the method takes ownership of the current object, ensuring it cannot be used again after the call.
Top comments (0)