# \[Pitch\] Allow Property Wrappers on Let Declarations

**URL:** <https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750>\
**Category:** Pitches\
**Tags:** propertywrapper\
**Created:** [December 1, 2022, 9:21pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750 "2022-12-01T21:21:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![amritpan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/amritpan/32/39281_2.png) [@amritpan](https://forums.swift.org/u/amritpan)\
**Post date:** [December 1, 2022, 9:21pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/1 "2022-12-01T21:21:24Z")

</div>

Hello everyone!

The following is a pitch for allowing let declared property wrappers in the Swift language, a topic that has come up in discussions here on the Swift forums. We welcome any feedback or questions about this pitch!

* * *

# Allow Property Wrappers on Let Declarations

**Authors:** [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin)  
**Status:** Implemented  
**Implementation:** [[Sema/SILGen] Allow property wrappers on `let` declarations by amritpan · Pull Request #62342 · swiftlang/swift · GitHub](https://github.com/apple/swift/pull/62342)

## Introduction

SE-0258 Property Wrappers models both mutable and immutable variants of a property wrapper but does not allow wrapped properties explicitly marked as a `let`. This proposal builds on the original vision by expanding property wrapper usage to include let declared properties with a wrapper attribute.

## Motivation

Allowing property wrappers to be applied to `let` properties improves Swift language consistency and code safety.

Today, a property wrapper can be applied to a property of any type but the rules for declaring these wrapped properties are varied and singular to property wrappers. For example, a struct instance without the property wrapper attribute can be declared with either a `var` or a `let` property. However, mark the struct as a property wrapper type and the compiler no longer allows it to be written as a `let` wrapped property:

```swift
@propertyWrapper
struct Wrapper {
  var wrappedValue: Int { 0 }
  init(wrappedValue: Int) {}
}

struct S {
  @Wrapper let value: Int // Error: Property wrapper can only be applied to a ‘var’
}

```

Permitting wrapped properties to mimic the rules for other type instances that can be written with either a `var` or a `let` will simplify the Swift language.

Additionally, `let` wrapped properties add code safety where a user wants to expressly remove access to a property’s mutators after initializing the property once or simply does not need a mutable property wrapper. This could be useful for property wrappers that do not change or are reference types.

## Proposed solution

We propose to allow the application of property wrappers to let declared properties, which will permit the wrapper type to be initialized only once without affecting the implementation of the backing storage.

For example, [TSPL](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617) defines a `SmallNumber` property wrapper and applies it to `UnitRectangle` properties:

```swift
@propertyWrapper
struct SmallNumber {
  private var maximum: Int
  private var number: Int

  var wrappedValue: Int {
    get { return number }
    set { number = min(newValue, maximum) }
  }

  init(wrappedValue: Int) {
    maximum = 12
    number = min(wrappedValue, maximum)
  }
}

struct UnitRectangle {
  @SmallNumber var height: Int = 1
  @SmallNumber var width: Int = 1
}

```

Initial values for `height` and `width` are set using the `init(wrappedValue:)` initializer of the `SmallNumber` property wrapper type. To ensure that `height` and `width` are not changed again, we could add logic to the `wrappedValue` setter to check if the property was already initialized and prevent re-assignment. However, this is an inconvenient solution.

Instead, we could declare these properties with a `let`, synthesize a local `let` constant for the backing storage property (prefixed with an underscore), and only allow the property wrapper to be initialized once, passing the assigned value to `init(wrappedValue:)`. Now, rewriting `UnitRectangle`’s properties as `let` constants will translate to:

```swift
private let _height: SmallNumber = SmallNumber(wrappedValue: 1)
var height: Int {
  get { return _height.wrappedValue }
}

private let _weight: SmallNumber = SmallNumber(wrappedValue: 1)
var weight: Int {
  get { return _weight.wrappedValue }
}

```

and results in code that is easy to write and understand:

```swift
struct UnitRectangle {
  @SmallNumber let height: Int = 1
  @SmallNumber let width: Int = 1
}

```

Property wrappers with `let` declarations will be allowed both as members and local declarations, as envisioned by SE-0258 for `var` declared property wrappers. All other property wrapper traits also remain unchanged from SE-0258.

### Property wrappers with `nonmutating set`

Property wrappers that have a `wrappedValue` property with a [`nonmutating set`](https://developer.apple.com/documentation/swiftui/state/wrappedvalue) (e.g., SwiftUI's `@State` and `@Binding`) will preserve the reference semantics of the `wrappedValue` implementation even when marked as a `let` declaration. For example:

```swift
@State let weekday: String = "Monday"

```

Here, `weekday` is an immutable instance of the `@State` property wrapper, but its `wrappedValue` storage will retain its mutability and reference type traits. This will translate to:

```swift
private let _weekday: State<String> = State<String>(wrappedValue: "Monday")
var weekday : String {
  get { 
    return _weekday.wrappedValue 
  }
  nonmutating set {
    self._weekday.wrappedValue = value
  }
  nonmutating _modify { 
    yield () 
  }
}
var $weekday: Binding<String> {
  get { return _weekday.projectedValue }
}

```

## Detailed design

Here are three examples of how a `let` wrapped property can make current iterations more effortless.

The `Clamping` property wrapper from [SE-0258 Property Wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds) can be rewritten to use `let` as its storage and implementation do not change, no matter its application.

```swift
struct Color {
  @Clamping(min: 0, max: 255) let red: Int = 127
}

```

A `let` wrapped property could be useful for reference types like a property wrapper class. Typically property wrappers are written for value types but occasionally a protocol like `NSObject` may require the use of a class. For example:

```swift
@propertyWrapper
class Wrapper<T> : NSObject {
  var wrappedValue: T

  init(wrappedValue: T) {
    self.wrappedValue = wrappedValue
  }
}

class C {
  @Wrapper let value: Int

  init(v: Int) {
    value = v
  }
}

```

can be made an immutable property wrapper class instance, preventing any future unintentional changes to the property wrapper class type in this context.

SwiftUI property wrappers may also benefit from a let declaration. For example, `@ScaledMetric` in its simplest usage can be written with a `let` instead:

```swift
struct ContentView: View {
  @ScaledMetric let imageSize = 10

  var body: some View {
    Image(systemName: "heart.fill")
    .resizable()
    .frame(width: imageSize, height: imageSize)
  }
}

```

Similarly, other SwiftUI property wrappers could be `let` declared when they do not require more than a single initialization.

## Source compatibility

This is an additive feature that does not impact source compatibility.

## Effect on ABI stability/API resilience

This is an additive change that has no direct impact that compromises ABI stability.

## Effect on API resilience

This is an additive change that has no impact on API resilience.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 1, 2022, 10:58pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/2 "2022-12-01T22:58:21Z")

</div>

> [@amritpan](#):
>
> The immutable variant from [SE-0258 Property Wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#delayed-initialization) can be rewritten to use `let` instead of a custom setter that checks if `_value` has already been initialized before assignment.

This is not compatible with the expansion to `let _x: DelayedImmutable<Int>`. That’s still a stored property that has to be initialized in the containing type’s `init` (whether by wrapped value or not), and then never modified again (because `let` does not allow mutation).

EDIT: and stepping back, what happens if I call `initializeX` twice?

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [December 2, 2022, 1:01am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/3 "2022-12-02T01:01:05Z")

</div>

Toolchains:

- macOS - [https://ci.swift.org/job/swift-PR-toolchain-macos/423/artifact/branch-main/swift-PR-62342-423-osx.tar.gz](https://ci.swift.org/job/swift-PR-toolchain-macos/423/artifact/branch-main/swift-PR-62342-423-osx.tar.gz)
- Linux - [https://download.swift.org/tmp/pull-request/62342/262/ubuntu2004/PR-ubuntu2004.tar.gz](https://download.swift.org/tmp/pull-request/62342/262/ubuntu2004/PR-ubuntu2004.tar.gz)

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [December 2, 2022, 2:16am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/4 "2022-12-02T02:16:42Z")

</div>

Very nice, this has been an annoying inconsistency.

Could you please double-check if things work well with this and `@TaskLocal` or if there's some extra work we need to do there? [Apple Developer Documentation](https://developer.apple.com/documentation/swift/tasklocal)

Actually, in the docs we even have...

```swift
enum TracingExample {
    @TaskLocal
    static let traceID: TraceID?
}

```

which is wrong (and docs were fixed), since this must be a var nowadays. So, will this actually resolve this issue and such `let` is now possible? (Would be good to add some tests for this explicitly perhaps in `async_task_locals_basic.swift`).

---

<div class="post-metadata">

**Author:** ![GreatApe](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/greatape/32/2640_2.png) [@GreatApe](https://forums.swift.org/u/GreatApe)\
**Post date:** [December 2, 2022, 11:54am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/5 "2022-12-02T11:54:39Z")

</div>

> [@amritpan](#):
>
> Similarly, `@SceneStorage`, `@Namespace`, or other SwiftUI property wrapper instances could be let declared when the backing wrapper does not change or these wrapped properties do not require more than a single initialization.

As much as I like the concept in general, it seems like it can't be used very often. For example, could SceneStorage really be used like this? How can the compiler know that the underlying value won't be mutated? The brute force method would be to disallow property wrappers with computed getters, or only allow them when it can be proven that they yield the same value on every access.

As far as I can see the return value needs to be calculated when the property wrapper is initialised, which would seem to severely limit the utility.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 2, 2022, 1:50pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/6 "2022-12-02T13:50:10Z")

</div>

I have some issues understanding the pitch, so here's my feedback:

> [@amritpan](#):
>
> ```swift
> struct UnitRectangle {
> let height: Int = 1
> private let _height: SmallNumber = SmallNumber(wrappedValue: 1)
> let width: Int = 1
> private let _width: SmallNumber = SmallNumber(wrappedValue: 1)
> }
> 
> ```

This synthesis does not make much sense to me. Where does the `1` in `let height: Int = 1` come from?  
Property wrappers aren't magic, they are pure syntactic transformations.

You say this, but I'm having a feeling there is more to this.

> Initial values for `height` and `width` are set using the `init(wrappedValue:)` initializer of the `SmallNumber` property wrapper type.

What does the transformation exactly look like in the above case?

```swift
init(...) {
  let heightWrapper = SmallNumber(wrappedValue: 1)
  // you have to read the value from somewhere!?
  self.height = heightWrapper.wrappedValue 
  self._height = heightWrapper
  ...
}

```

I expect a `let` property to not be a computed property, therefore there is no way to access the property wrapper through the wrapper property anymore except for going the direct `_height` route.

* * *

How does this affect `projectedValue` properties?

* * *

> [@amritpan](#):
>
> ```swift
> @propertyWrapper
> struct DelayedImmutable<Value> {
> private var _value: Value? = nil
> 
> var wrappedValue: Value {
> get { return value }
> set { _value = newValue }
> }
> }
> 
> class Foo {
> @DelayedImmutable let x: Int
> 
> init() {
> // We don't know "x" yet, and we don't have to set it
> }
> 
> func initializeX(x: Int) {
> self.x = x
> self.x = x // Will show an error due to the attempted re-assignment of a let property
> }
> }
> 
> ```

This example makes even less sense to me.

1. `get { return value }` what is `value` and where does it come from?
2. `set { _value = newValue }` what is the setter for? It's totally useless on a wrapper that itself becomes a constant as per your initial example with `SmallNumber`.
3. How is this transformation technically possible without introducing some kind of laziness?

```swift
@DelayedImmutable let x: Int

init() {
  // We don't know "x" yet, and we don't have to set it
}

```

1. How can you even assign to `x` when the property wrapper itself is a `let`? Again the syntactic transformation isn't showed in your examples nor properly explained.

I have hard times understanding how `DelayedImmutable` can be a constant. If anything it should be `lazy let`.

* * *

Okay `class Wrapper<T>` can become an immutable reference, but how useful it it? What will you do with the single time initialized `wrappedValue`, which also just a duplicate?!

* * *

Please patch the indentation in your code samples.

* * *

How about the `let` marker on a property rather being a projection onto the wrapper's underscored property? If the wrapper has a `nonmutating set` on `wrappedValue` and `projectedValue`, those will be exposed through the computed property, which will still remain a `var`. In other cases it will be excluded from the computed property.

In SwiftUI I'd mark all `@State` properties with `let`.

An example of that idea:

```swift
@propertyWrapper
struct A<V> {
  var wrapperValue: V {
    get { ... }
    nonmutating set { ... }
  }
}

@A let p1: Int = 1
// translates to
let _p1: A<Int> = A(wrappedValue: 1)
var p1: Int {
  get { _p1.wrappedValue }
  nonmutating set { _p1.wrappedValue = newValue }
}

```

```swift
@propertyWrapper
struct B<V> {
  var wrapperValue: V {
    get { ... }
    set { ... }
  }
}

@B let p2: Int = 2
// translates to
let _p2: B<Int> = B(wrappedValue: 2)
var p2: Int {
  get { _p1.wrappedValue }
}

```

Same applies to `projectedValue` and the `$` prefixed projections.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 2, 2022, 2:13pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/7 "2022-12-02T14:13:55Z")

</div>

Based on what I said at the end here's a potential real world example. Let's look at `Clamped` PW.

```swift
@propertyWrapper 
struct Clamped<Value> where Value: Comparable {
  var _value: Value
  var _range: ClosedRange<Value>

  init(wrappedValue: Value, range: ClosedRange<Value>) {
    self._value = min(max(range.lowerBound, wrappedValue), range.upperBound)
    self._range = range
  }
    
  var wrappedValue: Value {
    get { value }
    set { value = min(max(range.lowerBound, newValue), range.upperBound) }
  }
}

```

Here's how I imagine it to transform:

```swift
// example with VAR
@Clampped(range: 1 ... 10)
var a: Int = 0
// `var` translates to
var _a = Clampped(wrappedValue: 0, range: 1 ... 10)
var a: Int {
  get { _a.wrappedValue }
  set { _a.wrappedValue = newValue }
}
// `a` returns 1 initially and can mutate as expected

// example with LET
@Clampped(range: 1 ... 10)
let b: Int = 0
// `let` translates to
let _b = Clampped(wrappedValue: 0, range: 1 ... 10)
var b: Int {
  get { _b.wrappedValue }
}
// `b` returns 1 initially but cannot be mutated as the PW is immutable and the
// setter from the commuted property `b` is stripped is it's `mutating` in this case.

```

IMHO the computed property cannot go anywhere and it still should look up the value from the property wrapper itself. Transforming the property wrapper into a constant based on the `let` vs `var` keyword declaration seems totally reasonable to me.

* * *

Using `SwiftUI`'s `State` next:

```swift
@State
let value: String = "swift"
// translates to 
let _value = State(wrappedValue: "swift")
var value: String {
  get { _value.wrappedValue }
  // nonmutating set remains valid
  nonmutating set { _value.wrappedValue = newValue }
}

```

---

<div class="post-metadata">

**Author:** ![amritpan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/amritpan/32/39281_2.png) [@amritpan](https://forums.swift.org/u/amritpan)\
**Post date:** [December 2, 2022, 10:42pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/8 "2022-12-02T22:42:48Z")

</div>

Thank you for catching this. You're right - this example is an overreach. Instead, the immutable variant that allows for a single initialization should be:

```swift
@propertyWrapper
struct DelayedImmutable<Value> {
  var wrappedValue: Value

  init(wrappedValue: Value) {
    self.wrappedValue = wrappedValue
  }
}

class Foo {
  @DelayedImmutable let x: Int

  init(x: Int) {
    self.x = x
  }
}

let c = Foo(x: 10)

```

And the multiphase initialization in SE-0258’s example would not be possible here because the wrapped property is a let. I will update the pitch to reflect this.

---

<div class="post-metadata">

**Author:** ![amritpan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/amritpan/32/39281_2.png) [@amritpan](https://forums.swift.org/u/amritpan)\
**Post date:** [December 2, 2022, 10:45pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/9 "2022-12-02T22:45:03Z")

</div>

Thank you for this feedback. The let wrapped properties transformation has some resemblance to what you've described for Clampped. I will update the pitch to make this clearer as well.

For UnitRectangle with let wrapped height and width, the transformation with the current implementation is:

```swift
internal struct UnitRectangle {
  @SmallNumber internal let height: Int {
    get {
      return self._height.wrappedValue
    }
  }
  private let _height: SmallNumber = SmallNumber(wrappedValue: 1)
  @SmallNumber internal let width: Int {
    get {
      return self._width.wrappedValue
    }
  }
  private let _width: SmallNumber = SmallNumber(wrappedValue: 1)
  internal init() {}
}

```

where `1` is assigned via SmallNumber's wrappedValue init:

```swift
_height = SmallNumber(wrappedValue: 1)
_width = SmallNumber(wrappedValue: 1)

```

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 3, 2022, 4:56am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/10 "2022-12-03T04:56:12Z")

</div>

> [@amritpan](#):
>
> ```swift
> @SmallNumber internal let height: Int {
> get {
> return self._height.wrappedValue
> }
> }
> 
> ```

Is this some kind of a typo, because this is illegal code in Swift?! let properties cannot have a computed getter. Furthermore, why is there a property wrapper at this position still?

> [@amritpan](#):
>
> where `1` is assigned via SmallNumber's wrappedValue init:

How so? This is illegal. You cannot extract the `wrappedValue` from the `init` and assign it to the main wrapped properly unless it's `inout` or something.

---

<div class="post-metadata">

**Author:** ![mayoff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mayoff/32/613_2.png) [@mayoff](https://forums.swift.org/u/mayoff)\
**Post date:** [December 3, 2022, 4:57am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/11 "2022-12-03T04:57:49Z")

</div>

A `let x` declaration is (among other things) a guarantee to the reader that `x` will have the same value for its lifetime. I don't like that this pitch removes that guarantee:

```swift
@propertyWrapper
struct Random {
    var wrappedValue: UInt64 {
        var rng = SystemRandomNumberGenerator()
        return rng.next()
    }
}

struct MyType {
    @Random let r
}

let t = MyType()
print(t.r)
print(t.r)
print(t.r)

```

It's not obvious that the `let` keyword here is only transferred to the backing property and not to the wrapped property.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 3, 2022, 5:09am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/12 "2022-12-03T05:09:12Z")

</div>

That is an interesting example. 🤔 Would it be so critical if `let` in context of a property wrapper should signal that it's only applied to the PW storage? I mean, then we have to expect the result to be whatever the PW's getter returns, which makes your example still valid and legal. It's a consequence of the fact that `wrappedValue` itself can be computed which allows us to inject code like in your example.

> [@mayoff](#):
>
> It's not obvious that the `let` keyword here is only transferred to the backing property

That would basically become a new rule and permit `let` on wrapped properties.

At least in the context of what I have pitched above as an alternative to the main pitch, which I still fail to fully grasp.

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [December 3, 2022, 6:07am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/13 "2022-12-03T06:07:00Z")

</div>

The code @amritpan shared shows synthesized version produced by the type-checker where `let` keyword doesn’t play the same role as in surface language.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 3, 2022, 6:10am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/14 "2022-12-03T06:10:31Z")

</div>

Thank you for shining a bit of light into it, but this still does not make it more understandable to the reader. From the readers perspective this code is illegal for several reasons I previously already have mentioned.

If the type checker still needs the property wrapper annotation, even for the current PWs, fine but this does not belong into the proposal examples as it‘s a source for confusion. Regarding the `let` keyword. How is the reader without any knowledge about how the type checker works should guess that‘s a ‘different let‘?

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [December 3, 2022, 6:18am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/15 "2022-12-03T06:18:29Z")

</div>

You asked how exactly does the transformation look like and that code snippet does exactly that :) That said, my understanding is that @amritpan is going to update the pitch to show equivalent representation of the transformation is the surface language.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 3, 2022, 6:22am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/16 "2022-12-03T06:22:30Z")

</div>

Heh, that‘s a bit picky on my wording. 😏 I am basically only interested on what the legal swift syntactic transformation looks like, not what other toolings see this like, because I as a reader am not that tool and I don‘t necessarily understand how they work. Long story short, I can only provide better feedback if I understand the pitch. 😉

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [December 3, 2022, 6:24am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/17 "2022-12-03T06:24:55Z")

</div>

It depends on what you mean by “the same” in this context `let` properties that refer to reference types for example might not fit that.

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [December 3, 2022, 6:27am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/18 "2022-12-03T06:27:21Z")

</div>

No worries, I am just trying to point out the source of misunderstanding 🙂

---

<div class="post-metadata">

**Author:** ![amritpan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/amritpan/32/39281_2.png) [@amritpan](https://forums.swift.org/u/amritpan)\
**Post date:** [December 7, 2022, 11:51pm UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/19 "2022-12-07T23:51:48Z")

</div>

My mistake! I did not catch the alternative let in the code sample. Let me try again.

The Swift translation for

```swift
@SmallNumber let height: Int = 1

```

should be

```swift
private let _height: SmallNumber = SmallNumber(wrappedValue: 1)
var height: Int {
  get { return _height.wrappedValue }
}

```

If we used an instance init to set a value to `height` instead

```swift
@SmallNumber let height: Int

```

then the translation will be

```swift
private let _height: SmallNumber
var height: Int {
  get { return _height.wrappedValue }
}
init() {
  _height = SmallNumber(wrappedValue: 1)
}

```

For SwiftUI's `@State` property wrapper, the let translation does not have a nonmutating set. So,

```swift
@State let weekday = "Monday"

```

will translate to the following and should not be able to be reassigned:

```swift
private let _weekday: State<String> = State<String>(wrappedValue: "Monday")
var weekday : String {
  get { return _weekday.wrappedValue }
}
var $weekday: Binding<String> {
  get { return _weekday.projectedValue }
}

```

If we wanted to mutate a wrapped property inside a `ContentView` body, then `@State` would need be attached to a var property. The following would result in an error:

```swift
struct ContentView: View {
  @State let weekday: String // change 'let' to 'var' to make it mutable
  
  init(text: String) {
    self.weekday = text
  }
  
  var body: some View {
    VStack {
      Text(weekday)
      
      Button("Weekday") {
        weekday = "Tuesday" // error: cannot assign to property: 'weekday' is a 'let' constant
      }
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [December 8, 2022, 4:38am UTC](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750/20 "2022-12-08T04:38:41Z")

</div>

Okay it basically looks like to what I said. This starts to make way more sense now. :)  
However I do disagree on the `nonmutating set` part. I strongly feel that it should stay as it signals that there is some reference semantics hidden there somewhere. In your original examples you do have an object as a property wrapper, which you can still reference and potentially mutate its other variables. It's the same behavior for `nonmutating set`, there is a reference inside the PW that is used to delagate the set operation through some kind of an object.

This possibility of the mutating in case of `State` is still unavoidable regardless of `let`, because the user can still write `_weekday.wrappedValue = "Sunday"` and perform a non-mutating set operation.

Furthermore I think the error message from your example should be different:

> // error: cannot assign to property: 'text' is a 'let' constant

We should signal that `text` is a computed variable without a `mutating get` (aka immutable). The wrapped property isn't itself a constant and as I mentioned above is not even guaranteed to be one in certain cases like if `nonmutating set` is involved or if the `wrappedValue`'s getter on the PW returns random values as it was mentioned upthread.

* * *

P.S. tiny nitpick for all SwiftUI readers. The hypothetical example in `ContentView` above is not recommended, as you shouldn't assign a value to `State` from the init unless you fully understand the outcome of this operation.

[Next page](https://forums.swift.org/t/pitch-allow-property-wrappers-on-let-declarations/61750.md?page=2)
