Home > Net >  AttributedString/Substring extension: Cannot assign through dynamic lookup property: subscript is ge
AttributedString/Substring extension: Cannot assign through dynamic lookup property: subscript is ge

Time:06-23

If I have the following:

let nsRange = NSRange(location: 5, length: 10)
var attributedString = AttributedString("This is some string that I want to attribute.")
// To attribute a substring I must first convert the range
let start = attributedString.index(attributedString.startIndex, offsetByUnicodeScalars: nsRange.location)
let end = attributedString.index(start, offsetByUnicodeScalars: nsRange.length)
// Now we have a Range<AttributedString.Index>
let range = start..<end
attributedString[range].foregroundColor = .red

^ This code works ^

But if I try to remove some boilerplate by creating a subscript as follows:

extension AttributedString {
  subscript(_ range: NSRange) -> AttributedSubstring {
    get {
      let start = index(startIndex, offsetByUnicodeScalars: range.location)
      let end = index(start, offsetByUnicodeScalars: range.length)
      return self[start..<end]
    }
  }
}
attributedString[nsRange].foregroundColor = .red

^ this doesn't work ^

I get a Cannot assign through subscript: subscript is get-only error. But I'm confused why. attributedString[nsRange] return an AttributedSubstring, and AttributedSubstring can have properties set with the .foregroundColor = .red syntax. It works with a Range<AttributedString.Index> like str[start..<end].foregroundColor = .red so why not with my subscript?

CodePudding user response:

You only wrote a getter for the subscript. You also need a setter if you want to write through it (as the error notes):

    set {
        let start = index(startIndex, offsetByUnicodeScalars: range.location)
        let end = index(start, offsetByUnicodeScalars: range.length)
        self[start..<end] = newValue
    }

The point that Asperi is making is that the returned struct is a copy (this is true in the example you give from the docs as well). These examples are very different:

// This modifies a *copy* of `value`. It does *not* modify `xs`.
// It calls the subscript getter of `xs`.
var value = xs[i]
value.prop = "newThing"

// This modifies `xs`:
// It calls the subscript setter of `xs`.
xs[i].prop = "newThing"

CodePudding user response:

AttributedString and AttributedSubstring are structs - ie. value types

let range = start..<end
attributedString[range].foregroundColor = .red

Does not work. this works only if above declaration is var attributedString - variable, not let attributedString - constant.

let substring = attributedString[nsRange]
substring.foregroundColor = .red

Does not work. this works only if var substring - variable

and now

attributedString[nsRange].foregroundColor = .red

this does not work because in-line return of subscript is constant, same as let

  • Related