Conditional compilation of setter

public subscript(key: String) -> String? {
    get {
        rawValues[key]
    }
    #if DEBUG
    set {
        rawValues[key] = newValue
    }
    #endif
}

I’d like to allow setting values only in debug build. I tried above and got an error saying:

Expected 'get', 'set', 'willSet', or 'didSet' keyword to start an accessor definition

I looked up language reference and it indeed says that it conditionally compiles statements, with added support for explicit member expressions. But then, conditionally compiling whole subscript works ok.

Is this something that can be improved? or is it better to just wrap whole subscript with the flag?

3 Likes

Some other attempts:

public subscript(key: String) -> String? {
    #if DEBUG
    get {
        ""
    }
    set {
    }
    #else
    get {
        ""
    }
    #endif
}

This is mostly same as just duplicating whole subscript but it results in different error message.

Cannot find 'get' in scope
Cannot find 'set' in scope

Because you can omit get when there’s no set, this might be parsing as

public subscript(key: String) -> String? {
  get {
    #if DEBUG
    get {
        ""
    }
    set {
    }
    #else
    get {
        ""
    }
    #endif
  }
}

In which case the inner get and set are treated as function calls, not keywords.

2 Likes

Indeed that was what was happening. I added methods named get and set (with closure parameter to enable trailing closure syntax) in the scope and it resulted in different error :slight_smile:

It ultimately depends on how you want your behavior to work, but something quick and easy like the following would suffice:

public subscript(key: String) -> String? {
    get {
        rawValues[key]
    }
    
    set {
        #if DEBUG
        rawValues[key] = newValue
        #else
        fatalError("property can only be set in DEBUG mode")
        #endif
    }

}

You get no compile safety with this and it will crash your program during runtime when called if not running in debug.

1 Like

You could also experiment with swift macros to codegen two subscript operators: one with DEBUG and one without.

2 Likes

You will with this:

#if !DEBUG
@available(*, unavailable)
#endif
set {
2 Likes

Off the top of my head I can't think of the last time a new accessorSpecifier was added. I do know we have the upcoming coroutine read and modify accessors shipping in 6.3. That means that an engineer shipping a package that supports compiling from 6.3 and 6.2 might choose this pattern for supporting accessorSpecifier values that might not be available.

Hmm… is this pattern already being widely used in the ecosystem for conditionally compiling around accessorSpecifier values that might not always be available?

1 Like

Wow this works like a charm. I can even give custom error messages. Thank you all for the help!

public subscript(key: String) -> String? {
    get {
        ""
    }
    #if DEBUG
    @available(*, unavailable, message: "Setter is only available in debug builds")
    #endif
    set {
    }
}
Setter for 'subscript(_:)' is unavailable: Setter is only available in debug builds

Also found out that conditionally compiling attribute was introduced quite recently.

2 Likes