Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift generic constructor

Tags:

generics

swift

The questions are embedded in comments as well as here:

  1. How can I define a default value for a generic T in parameter list?
  2. When copying a parameter in Swift, is it pointers or objects?
    • XCode Beta 7 tells me I can't change "head", as it is a "let", see code
  3. Any Swift-like way to while-loop checking for nil to avoid unwrapping?

Linked List implementation:

public class Node<T>
{
    public var data: T
    public var next: Node<T>?

    //1.
    //How can I define a default datatype for T?
    //In C++ I would let T ctor do it: "data: T = T()" gives 
    //error: 'T' cannot be constructed because it has no accessible initializers
    public init(data: T, next: Node<T>?)
    {
        self.data = data
        self.next = next
    }
}

func print<T>(head: Node<T>)
{
    var tmp = head  //2. Is this a copy of a pointer or an object?
    print(tmp.data)
    while(tmp.next != nil)  //3. Any Swiftier way to do it?
    {
        tmp = tmp.next!
        print(tmp.data)
    }
}

func insert<T>(head: Node<T>, _ value: T)
{
    var tmp = head
    while tmp.next != nil
    {
        tmp = tmp.next!
    }
    tmp.next = Node<T>(data: value, next: nil)
}

var head = Node<Int>(data: 1, next: nil)
insert(head, 2)
insert(head, 4)
insert(head, 8)
insert(head, 16)
print(head)

Also, any other comments? I'm very new to Swift

like image 748
Peheje Avatar asked Jun 15 '15 23:06

Peheje


2 Answers

1.

Generics is all about code safety without knowing the type on compile time. That means it would make no sense to initialise a generic type to something. However by declaring a protocol like

protocol Initializable {
    init()
}

, extending the types you want to use by doing

extension Int : Initializable {}

(maybe implement the init() method yourself) and declaring your node like

public class Node<T: Initializable>

you can use T() in your initialiser to create the initial value. Because now the compiler knows that this type does indeed have such an initialiser because it conforms to that protocol.

2.

Classes are always passed as a reference whereas structs are always copied

To be able to change an input value of a function it has to be declared as inout like

func insert<T>(inout head: Node<T>, _ value: T)

Then you also need to call the function like insert(&head, 2)

3.

You could write the loop like

while let next = tmp.next {
    tmp = next
    print(tmp.data)
}

4.

Swift is a beautiful language, and I strongly suggest you to use more Swiftier code in general.

The way you wrote those 2 methods is as if in C. You can put functions in classes and this is the optimal way to do it and it's also much simpler.

Not very important I admit, but this style of curly brackets is outdated

You don't need to declare a function as public if you don't really need it. And you only need it when writing a framework or something along those lines. The default behaviour (no access control modifier) is to have access to all your Swift files in the whole project.

This Initializable protocol I made here isn't really Swifty either

Brackets around while (boolean) are not needed and aren't Swifty

Named parameters are there for a reason: To make code more readable. Only remove them when you're absolutely sure that it's annoying and it's clear what the parameter represents.

I didn't want to do this originally but I redid your code in a much more Swifty way and I hope I can help you with that:

protocol DefaultValuable {
    static func defaultValue() -> Self
}

extension Int : DefaultValuable {
    static func defaultValue() -> Int {
        return 1
    }
}

class Node<T: DefaultValuable> {
    var data: T
    var next: Node<T>?

    init(data: T = T.defaultValue(), next: Node<T>? = nil) {
        self.data = data
        self.next = next
    }

    func printNode() {
        var tmp = self
        print(tmp.data)

        while let next = tmp.next {
            tmp = next
            print(tmp.data)
        }
    }

    func insert(value: T) {
        var tmp = self

        while let next = tmp.next {
            tmp = next
        }

        tmp.next = Node<T>(data: value)
    }
}

let head = Node<Int>()
head.insert(2)
head.insert(4)
head.insert(8)
head.insert(16)
head.printNode()

(Sorry for the kinda rant)

like image 162
Kametrixom Avatar answered Oct 05 '22 11:10

Kametrixom


  1. You can't do this, in Swift. You could make data an optional, or overload init for specific types, but self.data = T() or similar isn't possible.

  2. Variables for objects made from classes are always reference type in Swift, so basically pointers under the hood.

  3. Your print function could be better written:

    func print<T>(head: Node<T>)
    {
        var tmp: Node<T>? = head
        while let currentNode = tmp
        {
            print(currentNode.data)
            tmp = tmp?.next
        }
    }
    
like image 38
Crowman Avatar answered Oct 05 '22 11:10

Crowman