0Pricing
Kotlin Academy · Lesson

@DslMarker: Preventing Receiver Leakage

Use @DslMarker annotations to enforce DSL nesting rules at compile time.

@DslMarker: Preventing Receiver Leakage is a free Kotlin Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Receiver Leakage Problem

In nested DSLs, an inner builder lambda can accidentally access members from an outer receiver. For example, inside a td { } block you might call tr { } from the outer table scope — which is almost never intended.

What Leakage Looks Like

Without @DslMarker, the compiler allows calling outer-scope functions from any inner lambda, which leads to confusing, error-prone DSL usage:

// Without @DslMarker — this compiles but is wrong:
html {
    body {
        table {
            tr {                // outer scope leaks in
                tr {  }         // calling tr inside tr — nonsensical
            }
        }
    }
}

Introducing @DslMarker

@DslMarker is a meta-annotation you apply to your own annotation class. Any builder class annotated with your DSL annotation will have its implicit receiver hidden in nested lambdas that use the same DSL annotation.

@DslMarker
annotation class HtmlDsl

@HtmlDsl
class Table

@HtmlDsl
class Tr

How Implicit Receiver Hiding Works

When two nested lambdas both have a receiver marked with the same @DslMarker annotation, the outer receiver is hidden inside the inner lambda. Accessing it directly is a compile error.

@DslMarker
annotation class HtmlDsl

@HtmlDsl class Html
@HtmlDsl class Body
@HtmlDsl class Div

Full Example with @DslMarker

Declare the annotation, mark builder classes, then nested calls to outer-scope methods are caught at compile time:

@DslMarker
annotation class MyCfgDsl

@MyCfgDsl
class ServerBuilder {
    var port: Int = 8080
    fun database(block: DbBuilder.() -> Unit) { /*...*/ }
}

@MyCfgDsl
class DbBuilder {
    var url: String = ""
    // Cannot call ServerBuilder.port here — compiler error
}

Escaping the Restriction with this@

If you genuinely need to access an outer receiver from an inner lambda, use a labeled this@OuterClass reference. This is explicit and intentional, unlike accidental leakage.

fun ServerBuilder.database(block: DbBuilder.() -> Unit) {
    val db = DbBuilder()
    db.block()
    val serverPort = this@ServerBuilder.port  // explicit outer access
}

@DslMarker with Multiple DSLs

Different DSLs should use different @DslMarker annotations. Two unrelated DSLs with different markers do not interfere with each other even when nested.

@DslMarker annotation class HtmlDsl
@DslMarker annotation class GradleDsl
// HtmlDsl and GradleDsl receivers do not hide each other

Applying to Builders and Receivers

Apply your DSL annotation to the builder class itself. The annotation propagates to any lambda parameter whose receiver type is that class. No further annotation of individual functions is needed.

IDE Support

IntelliJ IDEA and Android Studio highlight outer-receiver access as an error immediately when @DslMarker is used. This provides real-time feedback while authoring or consuming your DSL.

Designing for Safety

Good DSL design pairs each builder class with @DslMarker. It is a small annotation overhead that dramatically improves DSL ergonomics and prevents entire categories of bugs for DSL users.

Standard Library Usage

Kotlin's own @DslMarker is used in kotlinx.html, Ktor routing DSL, Gradle Kotlin script, and the buildMap/buildList builders. It is a fundamental tool in any serious DSL toolkit.

Quick Check

What does @DslMarker prevent?

Recap: @DslMarker

Key takeaways:

  • Without @DslMarker, outer receivers leak into inner DSL lambdas
  • Define a meta-annotated annotation class and apply it to all builder classes in your DSL
  • Nested lambdas with the same marker cannot accidentally call outer-scope members
  • Use this@OuterClass when you genuinely need outer-scope access

Frequently asked questions

Is the “@DslMarker: Preventing Receiver Leakage” lesson free?

Yes — the full text of “@DslMarker: Preventing Receiver Leakage” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “@DslMarker: Preventing Receiver Leakage”?

Use @DslMarker annotations to enforce DSL nesting rules at compile time. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “@DslMarker: Preventing Receiver Leakage” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Lambda with Receiver: The DSL Foundation
  2. @DslMarker: Preventing Receiver Leakage
  3. Building a Type-Safe HTML/Config DSL
  4. Testing and Evolving DSLs Without Breaking Users
← Back to Kotlin Academy