I have a protocol named Foo and a struct named Bar. Bar conforms to Foo.
protocol Foo {}
struct Bar: Foo {}
Appending a Bar instance to an array of Bar works as expected.
var array = [Bar]()
array.append(Bar())
Now, I have a generic struct called Baz that's initialized with a type that conforms to Foo (e.g. Bar).
struct Baz<T: Foo> {
private(set) var array = [T]()
init() {
if T.self is Bar.Type {
// Error: Cannot invoke 'append' with an argument list of type (Bar)
array.append(Bar())
}
}
}
Attempting to append to the array results in the following error:
Cannot invoke 'append' with an argument list of type (Bar)
Why doesn't this work as expected?
As an aside, the use case is something like this:
let bazBarStack = Baz<Bar>().array
let bazQuxStack = Baz<Qux>().array
You need to store objects in array, that are of type T. Therefore you should cast using as! T:
struct Baz<T: Foo> {
private(set) var array = [T]()
init() {
if T.self is Bar.Type {
array.append(Bar() as! T)
}
}
}
Because array holds elements of a single type T, which must conform to Foo but is not necessarily compatible with Bar. It looks like you wish to have an array that can hold anything conforming to Foo; in this case, get rid of the generics altogether and simply write
private(set) var array = [Foo]()
and then your init will be
init() {
array.append(Bar())
}
Related
I'm lost at how to initialize the struct below? Basically I want to create an empty variable that represents the struct below, but the values should be optional/nil, but i have no idea how to go about it.
// MARK: - Main
struct Main: Codable {
let meta: Meta
let objects: [Object]
}
// MARK: - Meta
struct Meta: Codable {
let status: String
let count, offset, totalcount: Int
}
// MARK: - Object
struct Object: Codable {
let id:Int
let url: String
let displayname: String
let inventory: [Inventory]
let media: [Media]
}
// MARK: - Inventory
struct Inventory: Codable {
let id, warehouseid, instock, threshold: Int
let reserved: Int
let coordinates, note: String
let prodno: Int
let lastinventory: String
let soldout, onpurchaseorders, generation: Int
let created, changed: String
}
// MARK: - Media
struct Media: Codable {
let id, prodno: Int
let webpath: String
let slot: Int
let type, extdata: String
let ctime, xsize, ysize, mediasetid: Int
let alt, title: String
let generation: Int
let created, changed: String
}
With the below code I get the error message "no exact matches in call to initializer, and a bunch of other errors if i change the code.
var sökresultat3 = Main3()
If you want the properties to be Optionals, then you have to declare them that way. Int means you want an Int. If you want an Optional Int, then declare it Int?.
That said, I would be very thoughtful about creating so many Optionals, and particularly optional arrays and strings. Is there a difference in your system between "no inventory" (nil) and "an empty list of inventory" ([])? Or "no name" (nil) and "empty name" ("")? If these are the same, then you often will prefer to just have default values, not optional values. Optionals add a lot of complexity that you may not need.
As an example of default values, using var will make this much simpler and give you the initializers you're hoping for:
struct Main: Codable {
var meta = Meta()
var objects: [Object] = []
}
// MARK: - Meta
struct Meta: Codable {
var status = ""
var count = 0
var offset = 0
var totalcount = 0
}
If you do make these optional, then var again will create the initializers you're looking for:
struct Main: Codable {
var meta: Meta?
var objects: [Object]?
}
As a rule, simple data structs that have no logic in them should declare their properties var. There are some exceptions, such as Identifiable structs, which should make their id be let because their id has some special meaning. But as a rule, making structs excessively let just makes them hard to use without gaining any benefits. (Values types are never shared, so mutation is not a problem.)
If you still want to use let, you'll need to define your own init by hand.
If all your struct's properties are optionals, and you make them all vars, Swift will create an intializer that defaults all your properties to nil. It is as if you defined your struct as follows:
struct AStruct {
var a: Int?
var string: String?
//You don't have to define this initializer if all your properties are var Optionals
init(a: Int? = nil, string: String? = nil) {
self.a = a
self.string = string
}
}
You can invoke that initializer like this:
var aStruct = AStruct()
As Alexander pointed out, just because you can do this does not mean that you should do it. It does not seem like good practice.
Change main to this
struct Main: Codable {
var meta: Meta?
var objects: [Object]?
}
I almost asked the same question the other day but in context of c++.
I try to replicate destructors and constructors in my c programming. That means for every object or struct there is an initialization function and a destruct function which frees all of the objects resources like so:
struct MyObject {
struct string a;
struct string b;
struct string c;
};
void ConstructMyObject(struct MyObject *obj) {
ConstructString(&obj->a);
ConstructString(&obj->b);
ConstructString(&obj->c);
}
void DestructMyObject(struct MyObject *obj) {
DestructString(&obj->a);
DestructString(&obj->b);
DestructString(&obj->c);
}
The destruct function is called at the end of every function scope just like in Rust only that i put it manually there instead of the compiler doing the job for me. So now in DestructMyObject function I call the destructors of every struct string type because for the struct string object i would also have a destruct function written just like for the struct MyObject Object. So everything that struct MyObject has allocated will be freed.
Example with my problem:
int main {
struct MyObject Object1;
ConstructMyObject(&Object1);
...
...
...
TransferOwnershipFunction(Object1.b); /*takes a struct string object as argument*/
...
...
...
DestructMyObject(&Object1);
return 0;
}
I transfered ownersnip of a member (struct string b) of Object1 to another function. But struct string b will be freed by the main function because i have the rule that when an object goes out of scope i call its destruct function. But I don't want the main function to free this resource. TransferOwnershipFunction(...) is now responsible to free this member of object1. How does the Rust compiler deal with such situations? In Rust would i have to make a clone of string b?
The Rust compiler is smart enough to see when only a single field of a struct is consumed. Only that specific field has its ownership transferred and the remaining fields are dropped at the end of the scope (or otherwise consumed). This can be seen in the following example.
struct MyObject {
a: String,
b: String,
c: String,
}
fn consume_string(_string: String) {}
fn main() {
let object = MyObject {
a: "".to_string(),
b: "".to_string(),
c: "".to_string(),
};
consume_string(object.b);
// We can still access object.a and object.c
println!("{}", object.a);
println!("{}", object.c);
// but not object.b
// println!("{}", object.b);
}
(playground)
However, if the struct has a non-trivial destructor, i.e., implements the Drop trait, then this can't happen. Trying to move a single field of the struct will result in a compiler error, as seen below.
struct MyObject {
a: String,
b: String,
c: String,
}
// This is new
impl Drop for MyObject {
fn drop(&mut self) {
println!("dropping MyObject");
}
}
fn consume_string(_string: String) {}
fn main() {
let object = MyObject {
a: "".to_string(),
b: "".to_string(),
c: "".to_string(),
};
consume_string(object.b);
}
Attempting to compile this gives the error
error[E0509]: cannot move out of type `MyObject`, which implements the `Drop` trait
--> src/main.rs:22:20
|
22 | consume_string(object.b);
| ^^^^^^^^
| |
| cannot move out of here
| move occurs because `object.b` has type `std::string::String`, which does not implement the `Copy` trait
error: aborting due to previous error
For more information about this error, try `rustc --explain E0509`.
error: Could not compile `playground`.
(playground) ([E0509])
I think you already understand the reasoning for this, but it's worth repeating. If Drop is implemented for a struct, the destructor might have non-trivial interactions between the fields; they might not just be dropped independently. So that means that the struct has to stay as one coherent piece until it's dropped.
As an example, the Drop implementation for Rc<T> checks if there are any (strong) references to the data left and if there aren't, drops the underlying data. If the fields of Rc<T> (a pointer, a strong reference count and a weak reference count) were dropped separately, there would be no way to check how many strong references were left when dropping the pointer. There'd be no way to keep the underlying data around if there are still strong references.
As you guessed, in the case where Drop is implemented, you'd have to clone the field if you still wanted to consume it.
I have a project that currently has a C struct which has been defined as:
typedef struct IDList {
uint32_t listID;
uint32_t count;
uint32_t idArray[];
} __attribute__((packed, aligned(4))) IDList, *IDListPtr;
There is a method in an Objective-C class that returns an IDListPtr to me.
I know I can:
let idListPtr = theIDManager.getIDList() // ObjC class that returns the struct
let idList = idListPtr.pointee // Get the IDList struct from the pointer
And I know there are idList.count items in the struct's array, but how do I access that array in Swift?
Zero-length arrays in C are not visible in Swift. A possible workaround is to add a helper function in the bridging header file, which returns the address of the first array item:
static uint32_t * _Nonnull idArrayPtr(const IDListPtr _Nonnull ptr) { return &ptr->idArray[0]; }
Now you can create a “buffer pointer” in Swift which references the variable length array:
let idListPtr = getIDList()
let idArray = UnsafeBufferPointer(start: idArrayPtr(idListPtr), count: Int(idListPtr.pointee.count))
for item in idArray {
print(item)
}
Context - I'm currently learning Swift Struct. So I decided to create my own Phone structure in Playground. (see below)
Problem -
The downloadApp method on the phone struct is throwing the following error.. Cannot use mutating member on immutable value: 'self' is immutable.
Expect outcome - To peacefully append new strings to my apps property, which is an array of strings.
Swift code
struct Phone {
let capacity : Int
let Brand : Sting
var name: String
let model: String
var apps: [String]
var contacts: [String]
var formatCapacity: String {
return "\(capacity)GB"
}
func downloadApp(name: String){
apps.append(name) // ERROR
}
}
You simply need to mark downloadApp as mutating. The issue is caused by the fact that downloadApp is declared in a struct type, which is a value type and hence if you mutate any properties (namely, the apps array here) of the struct, you actually mutate the struct itself. By marking the function as mutating, the compiler allows such modification of the struct through a member function of the type.
mutating func downloadApp(name: String){
apps.append(name)
}
the problem may sound weird but is exactly my situation.
I have a class containing a struct with only an array inside: an array of another struct. I would like to store an object of the class into user default. Let see some code
struct Inside {
var something: String
var somethingElse: Int
}
struct Outside {
var array = [Inside]()
}
class TheClass: {
var array = [Outside]()
}
var a = TheClass()
//now I would like to put 'a' into UserDefaults
I tried to use NSCoding, like you can see in this code (the code compiles)
class TheClass: NSObject, NSCoding {
var array: NSMutableArray = NSMutableArray()
override init(){
self.elementi = []
}
required init(coder aDecoder: NSCoder) {
elementi = aDecoder.decodeObjectForKey("elementi") as! NSMutableArray
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(elementi, forKey: "elementi")
}
}
but when I try to add an object of struct Outside into the NSMutableArray, I get an error. I found out that one solution is to convert into an NSValue. Here is the code in objective C, I am not able to translate it in swift
//here 'a' denotes an object of Outside struct that we want to insert into the NSMutableArray stored inside an object of TheClass (we cannot add it directly to the NSMutableArray)
NSValue* value = [NSValue value:&a withObjCType:#encode(Outside)];
//To extract it later:
[value getValue:&value];
I don't know how to solve this problem. Maybe I can change the struct to classes, in this way NSObject is no more need and I can use NSCoding directly (can be used with class object but not with struct object). But if I do this way I will get exactly the error that this guy have: SWIFT: Copying objects from arrays then changing properties and adding them back to original array
What do you suggest?