相同的下标代码,当用两行分开构建时它工作正常,当用一行代码构建它时,我得到一个错误,为什么?

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?

我添加了两张我的代码图片,解释了这两种情况 在第一种情况下,下标的工作方式与预期的完全一样,有两行代码

在第二种情况下,代码的计算结果应该与这两行相同,但由于某种原因我遇到了错误

你能帮我弄清楚为什么吗?

工作图像:working image

错误图片:error image

完整代码:

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?

您需要在 createClass("someStr")["0"] 周围加上括号,因为它试图在 ["0"]{"One"} 之前计算 它使用下标计算 createClass("someStr") 。即使您进行了此更改,也会出现编译错误,因为您声明了变量 str 两次。

有时候,有两个单独的语句会更容易(更不用说更清晰),而不是试图变得聪明。

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

你的例子:

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

正在使用 尾随闭包语法

尾随闭包语法 的工作原理是将尾随闭包作为附加参数包含在函数调用中。下标数组实际上是一个函数调用(对一个名为 subscript 的函数),并且 Swift 试图将该闭包作为第二个参数传递给下标调用,这就是错误所在解释:

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

换句话说,你不能同时将"0"和闭包{"One"}传递给下标函数。


至少有 3 种方法可以解决这个问题并且仍然把它放在一条线上:

选项 1:使用显式调用传递闭包而不是使用 尾随闭包语法

() 中包装闭包以使调用显式:

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

选项 2:将 createClass("someStr")["0"] 括在括号中

这让 Swift 知道下标只获取 "0" 作为参数,并允许尾随闭包语法按预期工作:

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

选项 3:在尾随闭包语法前的结果中添加 .self

这再次完成了订阅调用并避免了混淆。

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

就个人而言,我会选择 选项 1,因为 尾随闭包语法 不必要的 语法显然在这里不起作用的糖。


解决挑战

在我问的评论中:

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?

您回复了:

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")
 }

我们已经确定 尾随闭包语法 将最终闭包与索引操作配对,所以诀窍是设计一个 class 接受闭包及其索引操作:

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")
}