Debug School

rakesh kumar
rakesh kumar

Posted on

Understanding Generic Structs in Rust

For example, you may want a Box-like struct that can store:
What Are Generics in Simple Words?
Why Generic Structs Are Useful
Basic Problem Without Generics
The Solution: Generic Struct
Real-Life Use Case of Generic Structs
Why middleware needs generics
Objective and MCQ

an integer (i32)

a string (String)

a float (f64)

Enter fullscreen mode Exit fullscreen mode

or even a custom struct

If you don’t use generics, you end up writing separate structs for each type, which is repetitive and hard to maintain.

That’s exactly why generics exist in Rust.

What Are Generics in Simple Words?

A generic type is like a placeholder.

Instead of saying:

“This struct only stores i32”
Enter fullscreen mode Exit fullscreen mode

You say:

“This struct can store any type”
Enter fullscreen mode Exit fullscreen mode

And Rust will decide the type when you create the struct.

Why Generic Structs Are Useful

Generic structs help you:
Avoid duplicate code
Make reusable data structures
Write clean and scalable programs
Keep your code type-safe (Rust still checks types strictly)
Enter fullscreen mode Exit fullscreen mode

Basic Problem Without Generics

Let’s say you want a struct to store a value.

Example 1: Struct for only i32

struct IntBox {
    value: i32,
}

fn main() {
    let a = IntBox { value: 10 };
    println!("IntBox value = {}", a.value);
}
Enter fullscreen mode Exit fullscreen mode

Output:

IntBox value = 10
Enter fullscreen mode Exit fullscreen mode

Now if you want to store a string, you must create a new struct:

Example 2: Struct for only String

struct StringBox {
    value: String,
}

fn main() {
    let b = StringBox { value: "Hello".to_string() };
    println!("StringBox value = {}", b.value);
}
Enter fullscreen mode Exit fullscreen mode

Output:

StringBox value = Hello
Enter fullscreen mode Exit fullscreen mode

See the problem?

You are writing two structs doing the same job, just with different types.

The Solution: Generic Struct

Now we create one struct that works for all types:

Generic Struct Syntax

struct BoxValue<T> {
    value: T,
}
Enter fullscreen mode Exit fullscreen mode

Here:

T means any type

BoxValue<T> becomes:

BoxValue<i32>

BoxValue<String>

BoxValue<f64>
Enter fullscreen mode Exit fullscreen mode

etc.

Coding Example: Generic Struct with Different Types

struct BoxValue<T> {
    value: T,
}

fn main() {
    let a = BoxValue { value: 10 };
    let b = BoxValue { value: "Hello" };
    let c = BoxValue { value: 99.5 };

    println!("a = {}", a.value);
    println!("b = {}", b.value);
    println!("c = {}", c.value);
}

Enter fullscreen mode Exit fullscreen mode

Output:

a = 10
b = Hello
c = 99.5
Enter fullscreen mode Exit fullscreen mode

What Happened Here?

Rust automatically decides:

a is BoxValue<i32>

b is BoxValue<&str>

c is BoxValue<f64>

Enter fullscreen mode Exit fullscreen mode

One struct, many types.

Generic Struct with Methods (impl)

Generic structs become more powerful when we add methods.

Example: Add a method get()

struct BoxValue<T> {
    value: T,
}

impl<T> BoxValue<T> {
    fn get(&self) -> &T {
        &self.value
    }
}

fn main() {
    let a = BoxValue { value: 10 };
    let b = BoxValue { value: "Rust" };

    println!("a = {}", a.get());
    println!("b = {}", b.get());
}
Enter fullscreen mode Exit fullscreen mode

Output:

a = 10
b = Rust
Enter fullscreen mode Exit fullscreen mode

Meaning of impl<T>

impl means:

“Implement these methods for BoxValue of any type T”

So get() works for i32, String, &str, and everything else.

Generic Struct with Two Types

Sometimes you want to store two different types in the same struct.

Example: store a pair.

Coding Example

struct Pair<T, U> {
    first: T,
    second: U,
}

fn main() {
    let p1 = Pair { first: 10, second: "Hello" };
    let p2 = Pair { first: "Name", second: 99.9 };

    println!("p1 = {} {}", p1.first, p1.second);
    println!("p2 = {} {}", p2.first, p2.second);
}
Enter fullscreen mode Exit fullscreen mode

Output:

p1 = 10 Hello
p2 = Name 99.9
Enter fullscreen mode Exit fullscreen mode

Here:

p1 is Pair<i32, &str>

p2 is Pair<&str, f64>
Enter fullscreen mode Exit fullscreen mode

Real-Life Use Case of Generic Structs

Generic structs are used everywhere in Rust, such as:

Vec → vector of any type

Option → optional value of any type

Result → success type and error type

Example:

let nums: Vec<i32> = vec![1, 2, 3];
let name: Option<&str> = Some("Ashwani");
Enter fullscreen mode Exit fullscreen mode

Summary

✅ A generic struct uses placeholders like T, U to support many types.

✅ Instead of writing:

IntBox

StringBox

FloatBox

You write one struct:

BoxValue<T>
Enter fullscreen mode Exit fullscreen mode

✅ Generic structs keep code:

reusable

clean

scalable

type-safe
Enter fullscreen mode Exit fullscreen mode

Why middleware needs generics

  1. Because different handlers are different types

In Actix, each handler/service is not the same Rust type.

Example:

async fn dashboard() -> HttpResponse {
    HttpResponse::Ok().body("Dashboard")
}

async fn profile() -> HttpResponse {
    HttpResponse::Ok().body("Profile")
}

Enter fullscreen mode Exit fullscreen mode

Even if both return HttpResponse, internally the service pipeline around them may be different.

If middleware was not generic, you would have to write middleware for one exact service type only.

Generics solve that.

So:

AuthMiddlewareService<S>
Enter fullscreen mode Exit fullscreen mode

means:

wrap any service type S

Because response body types can be different

Different routes may return different body types:

plain text

JSON

HTML

file response

custom responder body
Enter fullscreen mode Exit fullscreen mode

So middleware cannot assume only one body type.

That is why you use:

B: MessageBody
Enter fullscreen mode Exit fullscreen mode

This means:

whatever body type the inner service returns, it must be a valid Actix body

So B keeps the middleware flexible.

Objective and MCQ

1) Why does middleware in Actix commonly use generics like AuthMiddlewareService?

A. To avoid using async/await
B. Because different handlers/services are different Rust types
C. Because Rust does not support traits
D. To make code slower but safer
✅ Answer: B

2) In struct BoxValue { value: T }, what does T represent?

A. A fixed integer type
B. A placeholder for “any type” chosen at compile time
C. A runtime dynamic type
D. A pointer type
✅ Answer: B

3) Without generics, storing i32 and String typically leads to:

A. One struct that auto-converts types
B. Duplicate structs for each type (repetition)
C. Faster compilation only
D. Trait objects everywhere
✅ Answer: B

4) Which is the correct generic struct syntax?

A. struct BoxValue(value: T)
B. struct BoxValue { value: T }
C. struct BoxValue[T] { value: T }
D. struct BoxValue { value: }
✅ Answer: B

5) What does impl BoxValue mean?

A. Implement methods only for BoxValue
B. Implement methods for BoxValue of any type T
C. Implement methods at runtime depending on input
D. Implement methods only for String
✅ Answer: B

6) In a generic method fn get(&self) -> &T, what is returned?

A. A copy of T always
B. A reference to the stored value
C. A mutable reference always
D. A string representation
✅ Answer: B

7) Which generic struct supports storing two different types?

A. struct Pair { first: T, second: U }
B. struct Pair { first: T, second: T }
C. struct Pair { first: T, second: U }
D. struct Pair { ... }
✅ Answer: A

8) If p1 = Pair { first: 10, second: "Hello" }, then p1 is:

A. Pair
B. Pair
C. Pair
D. Pair<&str, &str>
✅ Answer: B

9) In Actix middleware, why is the response body often generic as B?

A. Because every route returns the same body type
B. Because different routes can return different body types (text/JSON/HTML/etc.)
C. Because HttpResponse can’t carry bodies
D. Because generics are required in every Rust file
✅ Answer: B

10) Which constraint ensures the body type is valid for Actix?

A. B: Clone
B. B: Default
C. B: MessageBody
D. B: Send + Sync
✅ Answer: C

11) In AuthMiddlewareService, what does S usually represent?

A. A String
B. The wrapped inner service/handler pipeline type
C. A Session
D. A Server config object
✅ Answer: B

12)Which statement best describes why generics keep middleware reusable?

A. It makes middleware work only for one route
B. It lets middleware wrap “any service type S” instead of one fixed type
C. It removes the need for HTTP responses
D. It converts Rust into a dynamic language
✅ Answer: B

13) Which of these is a real-life generic type example mentioned in the topic?

A. Vec
B. Tree (as a built-in)
C. Json (as Rust standard library)
D. Router (as standard)
✅ Answer: A

14) Option<&str> is an example of:

A. A non-generic type
B. A generic type instantiated with &str
C. A macro expansion only
D. A runtime reflection type
✅ Answer: B

15) Which is TRUE about generics in Rust?

A. Rust decides T only at runtime
B. Generics reduce type safety
C. Generics help reuse code while staying type-safe
D. Generics require garbage collection
✅ Answer: C

16) If middleware returns unauthorized early, but otherwise passes through the inner service, why might Actix use an “either body” approach?

A. Because both branches must have the same exact body type
B. Because unauthorized and normal responses may have different body types, so they’re unified into one response body type
C. Because Rust cannot return a response
D. Because cookies require it
✅ Answer: B (matches the “different body types” idea behind B: MessageBody)

17) In generic structs, what is the main benefit compared to writing IntBox, StringBox, FloatBox separately?

A. More duplication
B. One reusable struct for many types
C. Only works for numbers
D. Only works for strings
✅ Answer: B

18) Which of the following is a correct instantiation idea for BoxValue?

A. BoxValue stores an i32
B. BoxValue stores a String
C. BoxValue stores an i32
D. BoxValue stores a bool only
✅ Answer: A

19) In Actix, even if two handlers both “return HttpResponse”, why can their internal service pipeline types differ?

A. Because Rust randomly changes types
B. Because different routes/middleware stacks can create different composed service types
C. Because HttpResponse is generic
D. Because handlers cannot be async
✅ Answer: B

20) Best summary: “Why middleware needs generics” includes which TWO main reasons?

A. (1) Generics make code longer, (2) generics avoid traits
B. (1) Different handlers/services are different types, (2) response body types can differ
C. (1) Generics remove async, (2) generics remove JSON
D. (1) Generics are only for performance, (2) generics are only for macros
✅ Answer: B

Top comments (0)