Accessing a member of a tuple
The problem with your code is that you are defining, (Int, String?, String?)
to (statusCode: Int, message: String, errorMessage: String)
which are interchangeable.
If you leave the type definition empty for the variable, the type inference should provide you with the correct type.
let response = (statusCode: 255, message: "Welcome", errorMessage: "Error")
print(response.message)
Or, if you want to define the type make sure that the type is actually (statusCode: Int, message: String, errorMessage: String)
.
let response: (statusCode: Int, message: String, errorMessage: String) = (statusCode: 255, message: "Welcome", errorMessage: "Error")
print(response.message)
You could also use tuple de-structuring syntax,
let (_, message, _) = (statusCode: 255, message: "Welcome", errorMessage: "Error")
print(message)
Swift tuple to Optional assignment
Another answer suggested (before it was changed) to just do:
(self.status!, self.message!) = self.parseResponse(resultBody)
I have found that is unsafe. It will crash if either self.status
or self.message
is nil
at the time of the assignment. Try this test in a Playground:
class Test {
var status: Int?
var error: String?
func parseResponse() -> (Status:Int, Error:String)
{
return (200, "Success")
}
func testIt() {
(self.status!, self.error!) = parseResponse()
print(self.status)
print(self.error)
}
}
let mytest = Test()
mytest.testIt()
Here is another way it could be done:
let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (Optional(stat), Optional(err))
or, as @AndriyGordiychuk discovered, it works without Optional
:
let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (stat, err)
It's curious that that works, but assigning the result of the function does not.
Note the following experiment:
var i: Int?
var s: String?
// This works
(i, s) = (3, "hello")
// This fails with error: cannot express tuple conversion '(Int, String)' to '(Int?, String?)
let t = (3, "hello")
(i, s) = t
It seems that when the assignment is a tuple literal assigned to a tuple, Swift takes a shortcut and doesn't first construct the tuple. Instead, is just assigns the individual elements.
So this:
(i, s) = (3, "hello")
is equivalent to:
i = 3
s = "hello"
which works because you can assign an Int
to an Int?
variable and a String
to a String?
variable. The tuple assignment fails because the types need to match.
Why is it valid to assign to an empty list but not to an empty tuple?
The comment by @user2357112 that this seems to be coincidence appears to be correct. The relevant part of the Python source code is in Python/ast.c
:
switch (e->kind) {
# several cases snipped
case List_kind:
e->v.List.ctx = ctx;
s = e->v.List.elts;
break;
case Tuple_kind:
if (asdl_seq_LEN(e->v.Tuple.elts)) {
e->v.Tuple.ctx = ctx;
s = e->v.Tuple.elts;
}
else {
expr_name = "()";
}
break;
# several more cases snipped
}
/* Check for error string set by switch */
if (expr_name) {
char buf[300];
PyOS_snprintf(buf, sizeof(buf),
"can't %s %s",
ctx == Store ? "assign to" : "delete",
expr_name);
return ast_error(c, n, buf);
}
tuple
s have an explicit check that the length is not zero and raise an error when it is. list
s do not have any such check, so there's no exception raised.
I don't see any particular reason for allowing assignment to an empty list when it is an error to assign to an empty tuple, but perhaps there's some special case that I'm not considering. I'd suggest that this is probably a (trivial) bug and that the behaviors should be the same for both types.
Implicit conversion from UnsafeBufferPointer to UnsafeRawBufferPointer
You cannot cast a UnsafeBufferPointer
to a UnsafeRawBufferPointer
because that is more than reinterpreting the pointer: It requires
to calculate the raw byte count.
But you can create a UnsafeRawBufferPointer
from a UnsafeBufferPointer
:
withUnsafePointer(to: &x) { (ptr) in
let buff = UnsafeBufferPointer(start: ptr, count: 1)
let rawBuff = UnsafeRawBufferPointer.init(buff)
printBuffer(address: rawBuff, as: Int.self)
}
Here is the implementation of that initializer in UnsafeRawBufferPointer.swift.gyb:
/// Creates a raw buffer over the contiguous bytes in the given typed buffer.
///
/// - Parameter buffer: The typed buffer to convert to a raw buffer. The
/// buffer's type `T` must be a trivial type.
@_inlineable
public init<T>(_ buffer: UnsafeMutableBufferPointer<T>) {
self.init(start: buffer.baseAddress!,
count: buffer.count * MemoryLayout<T>.stride)
}
Make custom type tie-able (compatible with std::tie)
Why the current attempts fail
std::tie(a, b)
produces a std::tuple<int&, string&>
.
This type is not related to std::tuple<int, string>
etc.
std::tuple<T...>
s have several assignment-operators:
- A default assignment-operator, that takes a
std::tuple<T...>
- A tuple-converting assignment-operator template with a type parameter pack
U...
, that takes astd::tuple<U...>
- A pair-converting assignment-operator template with two type parameters
U1, U2
, that takes astd::pair<U1, U2>
For those three versions exist copy- and move-variants; add either a const&
or a &&
to the types they take.
The assignment-operator templates have to deduce their template arguments from the function argument type (i.e. of the type of the RHS of the assignment-expression).
Without a conversion operator in Foo
, none of those assignment-operators are viable for std::tie(a,b) = foo
.
If you add a conversion operator to Foo
,
then only the default assignment-operator becomes viable:
Template type deduction does not take user-defined conversions into account.
That is, you cannot deduce template arguments for the assignment-operator templates from the type Foo
.
Since only one user-defined conversion is allowed in an implicit conversion sequence, the type the conversion operator converts to must match the type of the default assignment operator exactly. That is, it must use the exact same tuple element types as the result of std::tie
.
To support conversions of the element types (e.g. assignment of Foo::a
to a long
), the conversion operator of Foo
has to be a template:
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
However, the element types of std::tie
are references.
Since you should not return a reference to a temporary,
the options for conversions inside the operator template are quite limited
(heap, type punning, static, thread local, etc).
Why can't Java convert an ArrayListTreeSetInteger into a ListSetObject?
A List<Integer>
is not a List<Object>
. If Java
allowed that, then you could call the method with List<String>
and you will be broken. As Jeremy Heiler
pointed out you can use List<? extends Object>
and you will be fine. This means every type which extends Object
is allowed. ?
is called a wildcard
in generic jargon.
Related Topics
Preferredstatusbarupdateanimation Being Ignored
Decrypted String Always Returning Null
Apple Watch and iPhone Are Not Connected When The App in Phone Goes to Background
Realm: Predicate Returning Lazyfiltercollection - How to Convert to Results<T>
Swift Combine How Set<Anycancellable> Works
Destructuring Tuple of Tuple in Closure
How to Put View on Top of All Other Views in Swiftui
Viewwilllayoutsubviews in Swift
Scrolltoitem at Indexpath at .Top Hides Cell Under Header When Sectionheaderspintovisiblebounds
Swift Navigation Bar Item Not Calling Action
Implement Protocol with Different Associated Type
Pfobject Unable to Be Cast to Custom Subclass
"The Requested Snapshot Version Is Too Old." Error in Firestore
How to Test If an Instance Is a Specific Class or Type in Swift
Swift iOS14 Datepicker Text Alignment
Modifying Struct Instance Variables Within a Dispatch Closure in Swift