Closure tuple does not support destructuring in Xcode 9 Swift 4
Let's start with the definition of flatMap
for a dictionary which is the following:
func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
You see that the transform
closure takes only one parameter of type Element
where Element
is just a typealias
for a tuple:
public typealias Element = (key: Key, value: Value)
So the first and only argument of the closure should be a tuple of two elements (key
of type Key
and value
of type Value
).
Now, if you look at your code (which compiles in Swift 3), you will see that this is not the case, and you should be asking why does this even work in Swift 3.
try flatMap({ (key, value) in
return try transform(key, value)
})
Your closure takes 2 arguments instead of one (key
of type Key
and value
of type Value
). This works in Swift 3 thanks to a feature called destructuring where the compiler will automatically transform a tuple of 2 elements into 2 arguments.
But this feature is weird, rarely used and gives unexpected results most of the time so it has been removed in Swift 4.
Edit: As pointed out by OOPer, this feature has been temporarily removed in Swift 4 beta but should be re-added before the final version is out.
Instead you should be writing:
try flatMap({ tupleArgument in
return try transform(tupleArgument.key, tupleArgument.value)
})
And your flatMap
function becomes:
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ element in
return try transform(element.key, element.value)
}))
}
Swift 5.2 tuple destructuring in closure has inconsistent behaviors
It is related to this proposal, which was once accepted in Swift 4.0.
SE-0110 Distinguish between single-tuple and multiple-argument function types
So, two of your examples (not last two though) caused error in a certain version of Swift, but Swift team found some unacceptable regression in this implementation.
Additional Commentary
And since a certain version of Swift (I do not remember the exact version, it might have been 4.0.x or 4.1), you can pass two argument closure to the parameter of function type taking a single tuple.
And recently, this exception is confirmed to be a part of SE-0110 and if some more modification is needed, a new proposal will be made and go through the usual Swift Evolution process.
Update on SE-0110 and SE-0155
the original proposal as reviewed did not include the special-case
conversion from(T, U, ...) -> V
to((T, U, ...)) -> V
for function
arguments. In response to community feedback, this conversion was
added as part of the Core Team's acceptance of the proposal.
The Swift book may not have clear explanation of this behavior, but it surely is documented.
Destructuring tuples in JavaScript
Use a nested map()
, get the index from the first, en use that to create each 'column':
const [ ] = tuple[0].map((_, i) => tuple.map((_, j) => tuple[j][i]))
const tuple = [[1,"word",3,4,5],[1,"hello",3,4,5],[1,"word",3,4,5]];
const [ item1, item2, item3, item4, item5 ] = tuple[0].map((_, i) => tuple.map((_, j) => tuple[j][i]));
console.log(item2[0], item2[1]); // word hello
console.log(item4); // [ 4, 4, 4 ]
Is it possible to unpack a tuple into function arguments?
On a nightly compiler:
#![feature(fn_traits)]
fn main() {
let tuple = (10, Vec::new());
std::ops::Fn::call(&foo, tuple);
}
fn foo(a: i32, b: Vec<i32>) {
}
There is AFAIK no stable way to do that.
Can I destructure a tuple without binding the result to a new variable in a let/match/for statement?
No.
Destructuring is something you can only do with patterns; the left-hand side of an assignment is not a pattern, hence you can't destructure-and-assign.
See proto-RFC 372 (Destructuring assignment) which discusses the possibility of adding this feature.
How do I destructure a tuple so that the bindings are mutable?
You've got a few problems:
You've put the
&mut
in the wrong place;&mut
is part of the type, not the argument (unless you're destructuring the argument, which you aren't).You can't call the argument
struct
, because that's a keyword.You can't assign to a mutable reference with straight assignment.
So, with those in mind, here's a working solution:
#[derive(Debug)]
struct MyStruct {
tuple: (i32, i32),
}
fn function(s: &mut MyStruct) {
let (ref mut val1, ref mut val2) = s.tuple;
*val1 = 1;
*val2 = 2;
}
fn main() {
let mut s = MyStruct { tuple: (0, 0) };
function(&mut s);
println!("{:?}", s);
}
The key here is that ref
in a pattern binds by-reference; combining that with mut
gives you a mutable reference. Specifically, it gives you a pair of &mut i32
s. Since these are references, you have to de-reference them in order to assign through them (otherwise, you'd be trying to re-assign the reference itself).
Related Topics
Why Does Cabasicanimation Try to Initialize Another Instance of My Custom Calayer
Swift Protocol Extension Implementing Another Protocol with Shared Associated Type
Does Swift Allow Code Blocks Without Conditions/Loops to Reduce Local Variable Scope
Apple Turicreate Always Return The Same Label
Sbdata Is Wrong When Sbvalue Comes from a Swift Dictionary
Uitextview Contentoffset Is Set
How to Override Setter in Swift
How to Invoke Method with Cvalistpointer Parameters in Swift
Difference Between Firinstanceid.Instanceid().Token() and Messaging.Messaging().Fcmtoken
Pfobject Unable to Be Cast to Custom Subclass
Bridging Header for Flurry.H Not Working with Pod