Set-only subscripts

That would be great!

I think both properties and subscripts should be included. They’re both useful, and keeping them consistent is valuable too.

Sounds good! Once you have a draft proposal (or basically just something that explains how this feature works), let me know and I’ll take a look at implementing it!

I'm not sure this is a good idea. Semantically, a set-only subscript or property is just a mutating method, so this amounts to a different syntax for writing a mutating method. By expanding the constraints on subscripts and properties, we weaken their meaning. For example, seeing x[b] = y in valid code no longer means you can write x[b].mutatingMethod(). So it's not obvious to me that this change improves the language overall. I can see that it increases symmetry (between get and set) but that is a benefit to the writer that potentially hurts the reader.


Seems like a few of the examples here amount to wanting the getter to return a Whatever? without allowing you to set the value to nil.

Do you think that’s a bad idea? If not, how would you characterize the difference between it and the example you gave of what you don’t think is a good idea?

Yeah, I think it's a bad idea. It has essentially the same problem: it weakens the meaning of subscripts and properties by expanding the apparent relationships between setters and getter. Again you wouldn't be able to write x[b].mutatingMethod() or x[b]?.mutatingMethod()

1 Like

As a non-controversial first step, what about if we only allow writing set-only properties/subscripts when there is a corresponding getter available (basically, just allowing the two halves to be broken up)?

That would solve both the conditional conformance to MutableCollection scenario, and @Nevin’s floating point exponent scenario, without disrupting existing subscript semantics.

But really, I’m not sure there is such a conceptual difference. The availability of mutating methods today also depends on the presence of a setter and it isn’t always clear at the point of use whether that exists. Especially for subscripts - users could already assume that all subscripts have setters (otherwise, it would be a property or function. It only needs in-out access if it can mutate).

This feature would just generalise that to say that the property/subscript must have both a getter and setter.

Do you have a concrete example where that increases expressivity, rather than just code organization?

I admit, I find it frustrating that I have to switch from my usual practice of declaring each level of conformance a protocol at a time when defining a MutableCollection, but I have yet to hit a time when this was materially limiting.

"This is already a problem, let's generalize that problem" doesn't seem very persuasive to me.


No, but that suggestion is just intended to tackle the non-controversial parts. It’s still a nice thing to have if we can do it.

I mean it more like “this is already a fact of life”. Even if we want users to be able to assume certain things, the reality is that working with properties/subscripts already has certain levels of complexity. This doesn’t substantially add to it, IMO.

One wrinkle that did just occur to me though is when developers use erased properties and subscripts - we always assume that you can read from a KeyPath :grimacing:

1 Like

I believe that @Karl's minimal suggestion would also allow this example, for which it would increase expressivity relative to current Swift:

Wait, I’m confused. Why wouldn’t x[b]?.mutatingMethod() work?

I'm not sure that's a good goal. We already have a mechanism for doing this e.g. the replaceSubrange method (though we don't have such a method on non-RRC collections, but could). Why is it important to facilitate that operation through subscript assignment?

The purpose of get/set properties and subscripts is to allow them to behave to the user like l-values that can be mutated "in-place"*, as if you were mutating a regular stored property. This means you can, for example, write swap(&a[i], &b[j]).

But what's proposed is not doing that. It's just providing a new way to write mutating methods using subscript syntax. I agree with Dave that this weakens the meaning of subscripts.

*in use, if not in practice, pending language enhancements and elusive solutions)


You cannot mutate that which you cannot read

But you can read it. Maybe I didn’t communicate it clearly, but the context is a situation where the getter must exist. It’s just that if the getter returns a T?, the setter can take a non-optional T.

I see. You’re suggesting something that would be very confusing to me.

It’s come up a few times for situations where the data might not exist, but you don’t want allow explicitly setting the value to nil. I think “returning nil instead of crashing if you try subscript an array with an invalid index” was the first use-case I remember seeing. (I’m not saying this is or isn’t the best way to do that, just explaining a motivating example that I remember seeing.)

The big question is, what is the type of the entire subscript.

subscript(...) {
  get -> Get { ... }
  set -> Set { ... }

It may make sense in some scenarios if Get is a subtype of Set or vice-versa. However, I could not write foo(&x[b]) regardless of whether it is foo(_: inout Get) or foo(_: inout Set), not even with a common supertype or a common subtype (if we can even somehow have one). So it already can not do what normal l-value can, on a syntax that, as @Ben_Cohen said, suppose to behave like l-values. We may be able to expand the type system to accommodate this, but that's a huge undertaking for something of this size.

Also x[b]?.mutate() is (syntactically) just a sugar for

if var tmp = x[b] {
  x[b] = tmp

At best, we could say it should work with Getter == Optional<Setter>, but that does not apply to general syntax x[b].mutate(). And I do question its utility if we only limit the feature to extra Optional on the getter side.


Well, lessee. That's equivalent to:

{ opt: inout Optional in opt?.mutatingMethod() }(&x[b])

But in the scenario you posit, there's no Optional that you can inout (you can only “out” the Optional). So it would at least require some special rules in the language to describe how this works. Furthermore, to get it to work you'd have to read with one subscript's getter and write back with a different subscript's setter. That adds a lot of language complexity and breaks coherence with modify accessors.

If you want to create an API that allows you to write the semantic equivalent of x[b]?.mutatingMethod() but doesn't allow the semantic equivalent of x[b] = nil, it's easy to write that as a mutating method. Here's how you could add such an API to Dictionary, for example:

extension Dictionary {
  /// Returns the result of calling `body` on the value corresponding to `k`
  /// if `k` is a key in `self`; returns `nil` otherwise.
  mutating func mutateValue<Result>(
    ifKeyExists k: Key, body: (inout Value)->Result
  ) -> Result? {
    guard let i = index(forKey: k) else { return nil }
    return body(&values[i])
1 Like

Here's another nice, related utility that maybe should go into the standard library:

extension Optional {
  /// If `self` is non-`nil`, returns `mutate(&w)` where `w` is the wrapped
  /// value of `self`; returns `nil` otherwise.
  mutating func mapInout<R>(_ mutate: (inout Wrapped)->R) -> R? {
    guard var m = self else { return nil }
    self = nil // Maintain uniqueness to avoid CoWs
    defer { self = m }
    return mutate(&m)

This allows you to use any API that vends an inout T? to operate on an inout T (if one is available).


It would be great if we could allow mutating associated values directly one day.

switch self {
case .some(inout wrapped):

That should also allow avoiding the potential issue with CoW.

Related talk:


I have a use case that would benefit from set-only subscripts. I've created a JSONObject type, which uses string-based dynamic member lookup to store values of a JSONValue enum type.

enum JSONValue: Codable {
   case string(String)
   case number(Double)
   // etc...

struct JSONObject {
   private var dict: [String: JSONValue]

   public subscript(dynamicMember key: String) -> JSONValue? {
      get { return dict[key] }
      set { dict[key] = newValue }

   // ...

I've implemented the various ExpressibleBy*Literal protocols for JSONValue, which means that you can construct JSON object like this:

var obj = JSONObject() = "Fred Weasley"
obj.age = 17

However, I cannot use variables in the same way, because they're not literals.

let food = "Pizza"

// Error: Cannot assign value of type 'String' to type 'JSONValue'
obj.favoriteFood = food

This disparity between literals and variables is an ergonomic burden on the type. I would love to be able to implement set-only implementations of subscript(dynamicMember:) for the various supported JSONValue types, but the language does not support that. I could implement a separate setValue(_:forKey:) method, but again, the ergonomic distinction between literals and variables makes this non-ideal.

Anyway, that's my use case.

Terms of Service

Privacy Policy

Cookie Policy