same subscript code, when building it with two Separate lines it's working fine, when building it with one line of code, im getting an error , why?

馋奶兔 提交于 2020-01-25 06:39:25

问题


I've add two pictures of my code , that explain both Scenarios at the first scenario the subscript work exactly like its supposed to work, with Two lines of code

at the secound scenario , the code should evaluate same as the two line, but im getting an error for some reason

can you help me figure out WHY?

working image :working image

error image : error image

full code:

class SomeClass {

    var dic = Dictionary<String,(()->String) -> String>()

    subscript(_ s:String) -> (()->String) -> String {

        get{
            return dic[s]!
        }set{
            dic[s] = newValue
        }


    }


}


func createClass(_ s:String) -> SomeClass {

    func foo(_ str:()->String) ->String {
        return str() + " AND " + "Two"
    }
    let sc = SomeClass()
    sc["0"] = foo

    return sc

}


// WORKING TWO LINES

let someClass  = createClass("someStr")["0"]
let str = someClass{"One"} // everything work fine, no errors // prints 'One AND Two'



// ERROR ONE LINE

let str  = createClass("someStr")["0"]{"One"}



// WHY?

回答1:


Your example:

let str = createClass("someStr")["0"]{"One"}

is using trailing closure syntax.

Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:

Cannot subscript a value of type 'SomeClass' with an argument of type '(String, () -> String)'.

In other words, you can't pass both "0" and the closure {"One"} to the subscripting function.


There are at least 3 ways to fix this and still put it on one line:

Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax

Wrap the closure in () to make the call explicit:

let str1 = createClass("someStr")["0"]({"One"})
print(str1)

Option 2: Wrap the createClass("someStr")["0"] in parentheses

That lets Swift know the subscripting only gets "0" as a parameter and allows trailing closure syntax to work as expected:

let str2 = (createClass("someStr")["0"]){"One"}
print(str2)

Option 3: Add .self to the result before the trailing closure syntax:

That again finishes the subscripting call and avoids the confusion.

let str3 = createClass("someStr")["0"].self {"One"}
print(str3)

Personally, I would choose Option 1, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.


Solving the Challenge

In the comments I asked:

I agree that the trailing closure syntax is most likely a bug that they could fix, but what I don't understand is why you insist on using trailing closure syntax here. What is so objectionable about wrapping the closure in () to make the call explicit even if it is just to work around a bug in Swift?

You replied:

the reason for the insisting is that I'm trying to solve a challenge. actually , this function that return a closure is only one side of it it goes like this

func Challenge() {
    // Do not edit below this line
     XCTAssertEqual(foo("str1")["str2"]{ "654321" }, "123456")
 }

We've already established that trailing closure syntax is pairing the final closure with the indexing operation, so the trick is to design a class that takes a closure with its indexing operation:

class SomeClass {
    subscript(_ s: String, closure: () -> String) -> String {
        return String(closure().reversed())
    }
}

func foo(_ str: String) -> SomeClass {
    return SomeClass()
}

func Challenge() {
    // Do not edit below this line
    XCTAssertEqual(foo("str1")["str2"]{ "654321" }, "123456")
}



回答2:


You need to put parentheses around createClass("someStr")["0"], because it's trying to evaluate ["0"]{"One"} before it evaluates createClass("someStr") with a subscript. And even when you make this change, you'll get a compilation error because you declared the variable str twice.

Sometimes it's just easier (not to mention clearer) to have two separate statements, instead of trying to be clever.

var str = "Hello, playground"

class SomeClass {
    var dic = Dictionary<String,(()->String) -> String>()
    subscript(_ s:String) -> (()->String) -> String {
        get{
            return dic[s]!
        }set{
            dic[s] = newValue
        }
    }
}
func createClass(_ s:String) -> SomeClass {
    func foo(_ str:()->String) ->String {
        return str() + " AND " + "Two"
    }
    let sc = SomeClass()
    sc["0"] = foo
    return sc
}

str = (createClass("someStr")["0"]){"One"}
print(str) // One AND Two


来源:https://stackoverflow.com/questions/59792312/same-subscript-code-when-building-it-with-two-separate-lines-its-working-fine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!