问题
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