Why shouldn't I make my custom struct with sequence object? - swift

I want my struct to be created with sequence object using type inference. like:
var words: Buffer = ["generics", "ftw"] // type should be Buffer<String>
so I created a struct like this
struct Buffer<Element> {
init<S>(_ s: S) where Element == S.Element, S : Sequence {
}}
I just wrote code like above for testing generic init.
the code works when I create a struct like this:
var words = Buffer(["generics", "ftw"])
but I can't create a struct like this:
var words: Buffer = ["generics", "ftw"]
or
var words: Buffer<String> = ["generics", "ftw"]
the compiler complains me:
contextual type 'Buffer' cannot be used with array literal
I think I gave enough information to compiler as:
hey compiler, you'll gonna receive a Sequence. and the Element I mentioned is Sequence's Element.
what am I missing?

To use the syntax sugar, you have to adopt the ExpressibleByArrayLiteral protocol, which will make your struct able to be initialized using an array literal.
extension Buffer: ExpressibleByArrayLiteral {
typealias ArrayLiteralElement = Element
init(arrayLiteral: Element...) {
self.init(arrayLiteral)
}
}
After adding this extension, code like
var words: Buffer = ["generics", "ftw"]
or
var words: Buffer<String> = ["generics", "ftw"]
will compile.
Note that this does not mean that words is an array, although it is initialized with an array literal. If you assign the words variable to a variable that is of the Array type, your code will not compile, and vice versa. Also, note that if you don't infer the type Buffer in the variable declaration, it will be inferred as Array, so you must include Buffer in the variable decleration.

Related

How to assign a value to [any]

When I set c to a
var a: [Any]
var c: Array<PostCategory>
error shown:
cannot convert value of type 'Array' to expected argument type
[Any]
how to solve the problem?
The error message is a bit misleading but try initializing the array before assigning it:
var c: Array<PostCategory> = []
...or...
var c = Array<PostCategory>()
I bet your PostCategory is a struct. Apparently struct arrays aren't convertible to an Any array. This is weird because all types conforms to the Any protocol.
If you change the PostCategory to a class instead, it should work fine. You might need to create a new initializer for the class though, since classes doesn't give you the same default initializer as a struct does.

Assigning an array of structs to an array of protocols

Let's say I have the following:
protocol MyProtocol {
}
struct MyStruct: MyProtocol {
}
var s1 = MyStruct()
var s2 = MyStruct()
var s3 = MyStruct()
var structArray = [s1, s2, s3]
When I try to assign this array of structs to an array of protocols (that each struct in structArray conforms to):
var protocolArray:[MyProtocol] = structArray
I get this error: Cannot convert array of type '[MyStruct]' to specified type '[MyProtocol]'
I would expect that since each object in the array conforms to the protocol it would be ok to say that "an array of structs that conform to some protocol" is assignable to something that expects "an array of anything that conforms to that protocol". But maybe this doesn't apply when the type is "an array of " vs just "thing", if that makes any sense.
For example, this is valid:
var p1:MyProtocol = s1
Because s1 conforms to MyProtocol. But if you use arrays then it doesn't seem to hold anymore.
Incidentally, this seems to work too:
var p1Array:[MyProtocol] = [s1, s2, s3]
Presumably because the type of the array is determined to be [MyProtocol] and isn't predetermined by some previous variable (like in my example above).
So anyways, all this to ask: What's the best way around this? How can I assign an array of structs (that conform to some protocol) to another array whose type is just "an array of things that conform that protocol".
I'm fairly new to Swift so I may be missing something trivial.
I usually just map the array to the type I need:
var protocolArray: [MyProtocol] = structArray.map { $0 as MyProtocol }
When you do that, you can actually get rid of the type annotation, so that the expression as a whole isn't actually that much longer:
var protocolArray = structArray.map { $0 as MyProtocol }
Swift won't automatically convert between array types, even if they are compatible. You have to be explicit about it one way or another.

Swift implicit type conversion between C typedefs

I need to use a third party C library whose source I cannot modify and which makes heavy use of implicit type casting and typedefs to set values for its structs. They are all ints underneath and this is the preferred way of interacting with this library. I've previously used it in Objective C code without issue so now I am mostly porting some of my old code, but it feels like I am constantly hitting a brick wall with Swift.
tl;dr: how can I assign a different typedef value to a C struct member in Swift while automatically handling the type conversions (all typedefs are ints underneath)?
For example, consider the following definitions:
struct library_struct {
int member;
};
typedef enum library_consts {
LIBRARY_DEFINED_VALUE = 0
} library_consts;
In C or Objective C it would be absolutely acceptable to perform the following:
library_struct a;
a.member = LIBRARY_DEFINED_VALUE
However, attempting to do the same thing in Swift
var a: library_struct = library_struct()
a.member = LIBRARY_DEFINED_VALUE
results in an error:
Cannot assign a value of type 'library_consts' to a value of type 'Int32'
I tried several approaches:
Cast using Int32(). This leads to a Cannot find an initializer for type 'Int32' that accepts and argument list of type (library_consts) error.
Use LIBRARY_DEFINED_VALUE.rawValue. This won't work, because rawValue will return an UInt32, so I'm going to get the following error: Cannot assign a value of type 'UInt32' to a value of type 'Int32'
The only alternative is to cast again the value returned by rawValue to an Int32 like this: Int32(LIBRARY_DEFINED_VALUE.rawValue)
This works, but it feels wrong to make a double cast and it doesn't solve more complicated situations such as assigning a value of a different type (but still an int underneath) to a struct member such as the following:
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
typedef struct another_struct {
lib_bool_t aFlag;
}
var b: another_struct = another_struct()
a.aFlag = LIB_FALSE
This will error out with "Cannot assign a value of type 'library_consts' to a value of type 'lib_bool_t'"
I am afraid that there is no easier solution if you cannot change the
C interface. Using the "Generated Interface" view in Xcode 7 you can
see that
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
are mapped to Swift as
struct library_consts : RawRepresentable {
init(_ rawValue: UInt32)
init(rawValue: UInt32)
var rawValue: UInt32
}
typealias lib_bool_t = Int32
(the C int type is Int32 in Swift).
Swift does no implicit type conversions, which means that you have
to convert the types explicitly. In the second case it would be
var b: another_struct = another_struct()
b.aFlag = lib_bool_t(LIB_FALSE.rawValue)

Difference between [] and []() in Swift

I tried searching around for what this is
[]()
But I'm not really sure. In a playground, I did this:
var test = [Int]();
test.append(1);
test.append(2);
If I leave off the () and do
var test = [Int];
test.append(1);
test.append(2);
It still looks like an array of Ints to me. What is the difference?
Type() means "call init()" - i.e., make a new instance of Type.
[Int] is a type, like String. [Int] means "array-of-Int".
String() makes a new empty String instance. [Int]() makes a new empty array-of-Int instance.
You can declare a variable as being of type array-of-Int. Or you can make and assign a new array-of-Int instance. Which is what you are doing here:
var test = [Int]()
But you cannot assign a type to a variable without further syntax. Thus, this is illegal:
var test = [Int]
EXTRA for experts
You can say:
var test = [Int].self
That does assign the type to the variable! That is unlikely to be what you want here, because now you have no array; you have a type, itself, as object!!
[Type] is syntactic sugar for Array<Type>, so it represents the array type, which is a generic struct, and for which you specify the type of elements it holds (Int in this example)
So the difference between:
[Int]
and
[Int]()
is the same as for
Array<Int>
and
Array<Int>()
The first is just a type, which you can use when declaring a variable or a function parameter.
The second invokes the parameterless constructor on that type, i.e. creates an instance of that type.

Defining explicit conversion for custom types in Swift

What is currently the best/preferred way to define explicit conversions in Swift? Of the top of my head I can think of two:
Creating custom initializers for the destination type via an extension, like this:
extension String {
init(_ myType: MyType) {
self = "Some Value"
}
}
This way, you could just use String(m) where m is of type MyType to convert m to a string.
Defining toType-Methods in the source type, like this:
class MyType {
func toString() -> String {
return "Some Value"
}
}
This is comparable to Swift's String.toInt(), which returns an Int?. But if this was the definitive way to go I would expect there to be protocols for the basic types for this, like an inversion of the already existing *LiteralConvertible protocols.
Sub-question: None of the two methods allow something like this to compile: let s: MyString = myTypeInstance (as String) (part in parentheses optional), but if I understand right, the as operator is only for downcasting within type hierarchies, is that correct?
The pattern used in swift is the initializer. So for instance when converting an Int to UInt, we have to write:
var i: Int = 10
var u: UInt = UInt(i)
I would stick with that pattern.
As for the subquestion, the documentation states that:
Type casting is a way to check the type of an instance, and/or to treat that instance as if it is a different superclass or subclass from somewhere else in its own class hierarchy.
and
You can also use type casting to check whether a type conforms to a protocol
so no, the as keyword can`t be used to transform a value of a certain type to another type.
That can be tested in a simple way:
var i: Int = 10
var u: UInt = i as UInt
That generates an error:
'Int' is not convertible to 'UInt'
More about Type Casting